首页 > 作文

spring初始化方法的执行顺序及其原理分析

更新时间:2023-04-05 00:26:18 阅读: 评论:0

目录
spring中初始化方法的执行顺序首先通过一个例子来看其顺序配置我们进入这个类看我们看到了annotation-config了我们重点看下这行代码我们直接看initializebean这个方法spring加载顺序典例解决方案

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

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

/*** 调用顺序 init2(postconstruct注解) --> afterpropertiest(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 afterpropertiest() throws exception {    system.out.println("afterpropertiest");  }}

配置

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

通过运行,我们得出其执行顺序为init2(postconstruct注解) –> afterpropertiest(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() {  registerbeandefinitionparr("property-placeholder", new propertyplaceholderbea墨西哥狼ndefinitionparr());  registerbeandefinitionparr("property-override", new propertyoverridebeandefinitionparr());  registerbeandefinitionparr("annotation-config", new annotationconfigbeandefinitionparr());  registerbeandefinitionparr("component-scan", new componentscanbeandefinitionparr());  registerbeandefinitionparr("load-time-weaver", new loadtimeweaverbeandefinitionparr());  registerbeandefinitionparr("spring-configured", new springconfiguredbeandefinitionparr());  registerbeandefinitionparr("mbean-export", new mbeanexportbeandefinitionparr());  registerbeandefinitionparr("mbean-rver", new mbeanrverbeandefinitionparr()); }}

我们看到了annotation-config了

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

public beandefinition par(element element, parrcontext parrcontext) {  object source = parrcontext.extractsource(element);  // obtain bean definitions for all relevant beanpostprocessors.  t<beandefinitionholder> processordefinitions =      annotationconfigutils.registerannotationconfigprocessors(parrcontext.getregistry(), source);  // register component for the surrounding <context:annotation-config> element.  compositecomponentdefinition compdefinition = new compositecomponentdefinition(element.gettagname(), source);  parrcontext.pushcontainingcomponent(compdefinition);  // nest the concrete beans in the surrounding component.  for (beandefinitionholder processordefinition : processordefinitions) {    parrcontext.registercomponent(new beancomponentdefinition(processordefinition));  }  // finally register the composite component.  parrcontext.popandregistercontainingcomponent();  return null;}

我们重点看下这行代码

t<beandefinitionholder> processordefinitions = annotationconfigutils.registerannotationco计算机三级证书含金量nfigprocessors(parrcontext.getregistry(), source);

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

public static t<beandefinitionholder> registerannotationconfigprocessors(    beandefinitionregistry registry, object source) {  ...  // check for jsr-250 support, and if prent add the commonannotationbeanpostprocessor.  if (jsr250prent && !registry.containsbeandefinition(common_annotation_processor_bean_name)) {    rootbeandefinition def = new rootbeandefinition(commonannotationbeanpostprocessor.class);    def.tsource(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.getcuritymanager() != null) {    accesscontroller.doprivileged(new privilegedaction<object>() {      @override      public object run() {        invokeawaremethods(beanname, bean);        return null;      }    }, getaccesscontrolcontext());  }  el {    invokeawaremethods(beanname, bean);  }  object wrappedbean = bean;  if (mbd == null || !mbd.issynthetic()) {    // 调用@postconstruct方法注解的地方    wrappedbean = applybeanpostprocessorsbeforeinitialization(wrappedbean, beanname);//①  }  try {    // 调用afterpropertiest和init-method地方    invokeinitmethods(beanname, wrappedbean, mbd);// ②  }  catch (throwable ex) {    throw new beancreationexc怎样制作闪图eption(        (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 beanxception {  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 beanxception {  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,如果实现了就调用目标类的afterpropertiest方法,然后如果有配置init-method就调用其方法

protected void invokeinitmethods(string beanname, final object bean, rootbeandefinition mbd)    throws throwable {  // 1、调用afterpropertiest方法  boolean isinitializingbean = (bean instanceof initializingbean);  if (isinitializingbean && (mbd == null || !mbd.ixternallymanagedinitmethod("afterpropertiest"))) {    if (logger.isdebugenabled()) {      logger.debug("invoking afterpropertiest() on bean with name '" + beanname + "'");    }    if (system.getcuritymanager() != null) {      try {        accesscontroller.doprivileged(new privilegedexceptionaction<object>() {          @override          public object run() throws exception {            ((initializingbean) bean).afterpropertiest();            return null;          }        }, getaccesscontrolcontext());      }      catch (privilegedactionexception pae) {        throw pae.getexception();      }    }    el {      ((initializingbean) bean).afterpropertiest();    }  }  // 2、调用init-method方法  if (mbd != null) {    string initmethodname = mbd.getinitmethodname();    if (initmethodname != null && !(isinitializingbean && "afterpropertiest".equals(initmethodname)) &&        !mbd.ixternallymanagedinitmethod(initmethodname)) {      invokecustominitmethod(beanname, bean, mbd);    }  }}

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

spring加载顺序典例

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

解决方案

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

<column name="ur_info" pattern="%x{ur_info}" isunicode="fal" />

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

解决环境变量的方法:

拦截器:

@componentpublic class loginterceptor implements handlerinterceptor {    /**     * 需要记录在log中的参数     */    public static final string ur_info= "ur_info";    @override    public boolean prehandle(httprvletrequest httprvletrequest, httprvletrespon httprvletrespon, object arg)        throws exception {        string urname = logincontext.getcurrenturname();        threadcontext.put(ur_info, geturinfo());    }        @override    public void aftercompletion(httprvletrequest httprvletrequest, httprvletrespon httprvletrespon,        object arg, exception exception) throws exception {        threadcontext.remove(ur_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", "/licen/lackofresource", "/error"    };    /**     * 注册一个拦截器     *     * @return hpcloginterceptor     */    @bean    public loginterceptor tlogbean() {        return new loginterceptor();    }    @override    public void addinterceptors(interceptorregistry reg) {        // 拦截的对象会进入这个类中进行判断        interceptorregistration registration = reg.addinterceptor(tlogbean());        // 添加要拦截的路径与不用拦截的路径        registration.addpathpatterns(logurl).excludepathpatterns(excludeurl);    }}

如下待优化:

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

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

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

原因:

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

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

@componentpublic class loginterceptor implements handlerinterceptor {    /**     * 需要记录在log中的参数     */    public static final string ur_info= "ur_info";@resource(name = "jdbctemplate")    private jdbctemplate jdbctemplate;    @override    public boolean prehandle(httprvletrequest httprvletrequest, httprvletrespon httprvletrespon, object arg)        throws exception {        string urname = logincontext.getcurrenturname();        threadcontext.put(ur_info, geturinfo());    }        @override    public void aftercompletion(httprvletrequest httprvletrequest, httprvletrespon httprvletrespon,        object arg, exception exception) throws exception {        threadcontext.remove(ur_info);    }public string geturinfo(string urname) {        string sqltemplate = "lect ur_info from test.test_ur where ur_name = ?";        list<string> urinfo= new arraylist<>();        urinfo= jdbctemplate.query(sqltemplate, preparedstatement -> {            preparedstatement.tstring(1, urname);        }, new curityroledtomapper());        if (urinfo.size() == 0) {            return constants.hpc_normal_ur;        }        return urinfo.get(0);    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持www.887551.com。

本文发布于:2023-04-05 00:26:17,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/d1d36632dc9555e705dcbd82be8add6d.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:spring初始化方法的执行顺序及其原理分析.doc

本文 PDF 下载地址:spring初始化方法的执行顺序及其原理分析.pdf

下一篇:返回列表
标签:方法   顺序   注解   初始化
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图