千家信息网

spring初始化方法的执行顺序及其原理是什么

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章主要讲解了"spring初始化方法的执行顺序及其原理是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"spring初始化方法的执行顺序及其原
千家信息网最后更新 2026年11月02日spring初始化方法的执行顺序及其原理是什么

这篇文章主要讲解了"spring初始化方法的执行顺序及其原理是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"spring初始化方法的执行顺序及其原理是什么"吧!

Spring中初始化方法的执行顺序

首先通过一个例子来看其顺序

/** * 调用顺序 init2(PostConstruct注解) --> afterPropertiesSet(InitializingBean接口) --> init3(init-method配置) */public class Test implements InitializingBean {    public void init3(){        System.out.println("init3");    }    @PostConstruct    public void init2(){        System.out.println("init2");    }    @Override    public void afterPropertiesSet() throws Exception {        System.out.println("afterPropertiesSet");    }}

配置

通过运行,我们得出其执行顺序为init2(PostConstruct注解) --> afterPropertiesSet(InitializingBean接口) --> init3(init-method配置)。但是为什么是这个顺序呢?我们可以通过分析其源码得出结论。

首先在解析配置文件的时候,碰到context:annotation-config/自定义标签会调用其自定义解析器,这个自定义解析器在哪儿呢?在spring-context的spring.handlers中有配置

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler

我们进入这个类看

public class ContextNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());    }}

我们看到了annotation-config了

我们只关心这个标签,那我们就进入AnnotationConfigBeanDefinitionParser类中,看它的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {    Object source = parserContext.extractSource(element);    // Obtain bean definitions for all relevant BeanPostProcessors.    Set processorDefinitions =            AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);    // Register component for the surrounding  element.    CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);    parserContext.pushContainingComponent(compDefinition);    // Nest the concrete beans in the surrounding component.    for (BeanDefinitionHolder processorDefinition : processorDefinitions) {        parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));    }    // Finally register the composite component.    parserContext.popAndRegisterContainingComponent();    return null;}

我们重点看下这行代码

Set processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);

我们追踪进去(其中省略了一些我们不关心的代码)

public static Set registerAnnotationConfigProcessors(        BeanDefinitionRegistry registry, Object source) {    ...    // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);        def.setSource(source);        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));    }    ...}

在这个方法其中注册了一个CommonAnnotationBeanPostProcessor类,这个类是我们@PostConstruct这个注解发挥作用的基础。

在bean实例化的过程中,会调用AbstractAutowireCapableBeanFactory类的doCreateBean方法,在这个方法中会有一个调用initializeBean方法的地方,

我们直接看initializeBean这个方法

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {    if (System.getSecurityManager() != null) {        AccessController.doPrivileged(new PrivilegedAction() {            @Override            public Object run() {                invokeAwareMethods(beanName, bean);                return null;            }        }, getAccessControlContext());    }    else {        invokeAwareMethods(beanName, bean);    }    Object wrappedBean = bean;    if (mbd == null || !mbd.isSynthetic()) {        // 调用@PostConstruct方法注解的地方        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);//①    }    try {        // 调用afterPropertiesSet和init-method地方        invokeInitMethods(beanName, wrappedBean, mbd);// ②    }    catch (Throwable ex) {        throw new BeanCreationException(                (mbd != null ? mbd.getResourceDescription() : null),                beanName, "Invocation of init method failed", ex);    }    if (mbd == null || !mbd.isSynthetic()) {        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);    }    return wrappedBean;}

先看①这行,进入applyBeanPostProcessorsBeforeInitialization方法

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)        throws BeansException {    Object result = existingBean;    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {        result = beanProcessor.postProcessBeforeInitialization(result, beanName);        if (result == null) {            return result;        }    }    return result;}

我们还记得前面注册的一个类CommonAnnotationBeanPostProcessor,其中这个类间接的实现了BeanPostProcessor接口,所以此处会调用CommonAnnotationBeanPostProcessor类的postProcessBeforeInitialization方法,它本身并没有实现这个方法,但他的父类InitDestroyAnnotationBeanPostProcessor实现了postProcessBeforeInitialization的方法,其中这个方法就实现调用目标类上有@PostConstruct注解的方法

// 获取目标类上有@PostConstruct注解的方法并调用public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());    try {        metadata.invokeInitMethods(bean, beanName);    }    catch (InvocationTargetException ex) {        throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());    }    catch (Throwable ex) {        throw new BeanCreationException(beanName, "Failed to invoke init method", ex);    }    return bean;}

然后接着看initializeBean方法中②这一行代码,首先判断目标类有没有实现InitializingBean,如果实现了就调用目标类的afterPropertiesSet方法,然后如果有配置init-method就调用其方法

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)        throws Throwable {    // 1、调用afterPropertiesSet方法    boolean isInitializingBean = (bean instanceof InitializingBean);    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {        if (logger.isDebugEnabled()) {            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");        }        if (System.getSecurityManager() != null) {            try {                AccessController.doPrivileged(new PrivilegedExceptionAction() {                    @Override                    public Object run() throws Exception {                        ((InitializingBean) bean).afterPropertiesSet();                        return null;                    }                }, getAccessControlContext());            }            catch (PrivilegedActionException pae) {                throw pae.getException();            }        }        else {            ((InitializingBean) bean).afterPropertiesSet();        }    }    // 2、调用init-method方法    if (mbd != null) {        String initMethodName = mbd.getInitMethodName();        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&                !mbd.isExternallyManagedInitMethod(initMethodName)) {            invokeCustomInitMethod(beanName, bean, mbd);        }    }}

至此Spring的初始化方法调用顺序的解析就已经完了。

spring加载顺序典例

借用log4j2,向数据库中新增一条记录,对于特殊的字段需要借助线程的环境变量。其中某个字段需要在数据库中查询到具体信息后插入,在借助Spring MVC的Dao层时遇到了加载顺序问题。

解决方案

log4j2插入数据库的方案参考文章:

需要执行日志插入操作(比如绑定到一个级别为insert、logger.insert())的线程中有环境变量user_info。

解决环境变量的方法:

拦截器:

@Componentpublic class LogInterceptor implements HandlerInterceptor {    /**     * 需要记录在log中的参数     */    public static final String USER_INFO= "user_info";    @Override    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg)        throws Exception {        String userName = LoginContext.getCurrentUsername();        ThreadContext.put(USER_INFO, getUserInfo());    }        @Override    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,        Object arg, Exception exception) throws Exception {        ThreadContext.remove(USER_INFO);    }

需要拦截的URL配置:

@Configurationpublic class LogConfigurer implements WebMvcConfigurer {    String[] logUrl = new String[] {        "/**",    };    String[] excludeUrl = new String[] {        "/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.png", "/**/*.svg", "/**/*.woff", "/**/*.eot", "/**/*.ttf",        "/**/*.less", "/favicon.ico", "/license/lackofresource", "/error"    };    /**     * 注册一个拦截器     *     * @return HpcLogInterceptor     */    @Bean    public LogInterceptor setLogBean() {        return new LogInterceptor();    }    @Override    public void addInterceptors(InterceptorRegistry reg) {        // 拦截的对象会进入这个类中进行判断        InterceptorRegistration registration = reg.addInterceptor(setLogBean());        // 添加要拦截的路径与不用拦截的路径        registration.addPathPatterns(logUrl).excludePathPatterns(excludeUrl);    }}

如下待优化:

问题就出在如何获取信息这个步骤,原本的方案是:

通过Dao userDao从数据库查询信息,然后填充进去。

出现的问题是:userDao无法通过@Autowired方式注入。

原因:

调用处SpringBoot未完成初始化,导致dao层在调用时每次都是null。

因此最后采用的方式如下:

@Componentpublic class LogInterceptor implements HandlerInterceptor {    /**     * 需要记录在log中的参数     */    public static final String USER_INFO= "user_info";                @Resource(name = "jdbcTemplate")    private JdbcTemplate jdbcTemplate;    @Override    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg)        throws Exception {        String userName = LoginContext.getCurrentUsername();        ThreadContext.put(USER_INFO, getUserInfo());    }        @Override    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,        Object arg, Exception exception) throws Exception {        ThreadContext.remove(USER_INFO);    }        public String getUserInfo(String userName) {        String sqlTemplate = "select user_info from Test.test_user where user_name = ?";        List userInfo= new ArrayList<>();        userInfo= jdbcTemplate.query(sqlTemplate, preparedStatement -> {            preparedStatement.setString(1, userName);        }, new SecurityRoleDtoMapper());        if (userInfo.size() == 0) {            return Constants.HPC_NORMAL_USER;        }        return userInfo.get(0);    }

感谢各位的阅读,以上就是"spring初始化方法的执行顺序及其原理是什么"的内容了,经过本文的学习后,相信大家对spring初始化方法的执行顺序及其原理是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

方法 顺序 配置 注解 原理 数据 数据库 目标 问题 代码 信息 变量 地方 接口 方案 环境 学习 内容 参数 字段 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 天津市摩尔互联网科技 上海卓越科技互联网 广州紫鲸互联网科技有限公司 互联网巨头反垄断金融科技 礼当家互联网科技 互联网科技作文 朗涯互联网科技 上海亿帐通互联网科技 汉立互联网科技有限公司联系电话 成都玖崟互联网科技有限公司乐商圈怎么样 须眉科技是互联网公司吗 银川新华互联网科技职业技术培训学校图片 互联网企业和科技型企业 互联网金融中的现代科技 电子科技大学互联网创业大赛 明优互联网科技有限公司 黑云互联网科技有限公司 扫呗互联网科技有限公司 利用互联网等科技手段 互联网科技快速发展优美句子 互联网科技海报 易通互联网络科技有限公司 苏州大垚互联网科技 互联网养老 桔豆科技 央行互联网金融科技公司 互联网科技公司实习生 电子商务与互联网科技的关系 济南小医互联网科技有限公司招聘 普陀区互联网教育科技管理办法 恒生互联网科技趋势
0