/*** 调用顺序 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()); }}
我们只关心这个标签,那我们就进入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方法的地方,
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的初始化方法调用顺序的解析就已经完了。
借用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 条评论) |