我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

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

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

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

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");    }}

配置

<context:annotation-config/><bean class="com.cyy.spring.lifecycle.Test" id="test" init-method="init3"/>

通过运行,我们得出其执行顺序为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<BeanDefinitionHolder> processorDefinitions =            AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);    // Register component for the surrounding <context:annotation-config> 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<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);

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

public static Set<BeanDefinitionHolder> 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<Object>() {            @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<Object>() {                    @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插入数据库的方案参考文章:

<Column name="user_info" pattern="%X{user_info}" isUnicode="false" />

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

解决环境变量的方法:

拦截器: 

@Componentpublic class LogInterceptor implements HandlerInterceptor {        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[] {        "*.js", "*.css", "*.jpg", "*.png", "*.svg", "*.woff", "*.eot", "*.ttf",        "*.less", "/favicon.ico", "/license/lackofresource", "/error"    };        @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 {        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<String> 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初始化方法的执行顺序及其原理是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

Spring初始化与销毁的执行顺序是什么

这篇文章主要介绍了Spring初始化与销毁的执行顺序是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring初始化与销毁的执行顺序是什么文章都会有所收获,下面我们一起来看看吧。演示 - 初始化销毁顺序/
2023-07-05

Spring AOP与代理类的执行顺序是什么

本篇内容介绍了“Spring AOP与代理类的执行顺序是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!关于 Spring AOP和Asp
2023-07-05

spring中bean的初始化方法是什么

在Spring中,bean的初始化可以通过两种方式来完成:使用@Bean注解的initMethod属性或者实现InitializingBean接口。1. 使用@Bean注解的initMethod属性:可以在@Bean注解中通过initMet
2023-09-21

mysql初始化失败的原因及解决方法是什么

MySQL初始化失败的原因可能有很多,常见的原因包括:1. 配置文件错误:MySQL的配置文件(my.cnf)中可能存在错误配置,比如语法错误、路径错误等。解决方法:检查并修复配置文件中的错误。2. 端口被占用:MySQL默认使用3306端
2023-09-23

C#类中方法执行顺序指的是什么

小编给大家分享一下C#类中方法执行顺序指的是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!有些中级开发小伙伴还是搞不太明白在继承父类以及不同场景实例化的情况下
2023-06-15

Spring中Bean初始化和销毁的方法是什么

今天小编给大家分享一下Spring中Bean初始化和销毁的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Sprin
2023-07-06

labview停止运行并初始化的方法是什么

在LabVIEW中,停止运行并初始化的方法是使用"Stop"按钮或通过执行"Abort VI"函数。以下是使用这两种方法进行停止运行和初始化的步骤:使用"Stop"按钮停止运行并初始化:1. 在LabVIEW界面的工具栏上找到"Stop"按
2023-09-15

Spring源码解析容器初始化构造的方法是什么

这篇“Spring源码解析容器初始化构造的方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring源码解析容器初
2023-07-02

ASP.NET处理程序的执行方法是什么

这篇文章主要介绍“ASP.NET处理程序的执行方法是什么”,在日常操作中,相信很多人在ASP.NET处理程序的执行方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”ASP.NET处理程序的执行方法是什么
2023-06-17

go语言结构体初始化及赋值的方法是什么

在Go语言中,结构体的初始化及赋值可以通过以下两种方法进行:字面量初始化:直接使用结构体类型的名称,并在花括号内指定字段的初始值。例如:type Person struct {Name stringAge int}func main()
2023-10-25

MySql执行失败的原因及解决方法是什么

MySql执行失败的原因有很多种,以下是常见的一些原因及解决方法:1. 语法错误:在编写SQL语句时,可能会出现语法错误,比如拼写错误、缺少关键字等。解决方法是仔细检查SQL语句,确保语法正确。2. 数据库连接问题:可能是由于网络问题、数据
2023-09-14

mysql_query()函数执行失败的原因及解决方法是什么

mysql_query()函数执行失败的原因和解决方法可能有以下几种:1. 连接数据库失败:如果连接数据库失败,可以检查数据库服务器是否正常运行,数据库连接参数是否正确,以及防火墙是否阻止了数据库连接。2. SQL语句错误:如果SQL语句有
2023-08-08

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录