spring初始化方法的執(zhí)行順序及其原理分析
Spring中初始化方法的執(zhí)行順序
首先通過一個例子來看其順序
/** ?* 調(diào)用順序 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"); ? ? } }
配置
<context:annotation-config/> <bean class="com.cyy.spring.lifecycle.Test" id="test" init-method="init3"/>
通過運行,我們得出其執(zhí)行順序為init2(PostConstruct注解) --> afterPropertiesSet(InitializingBean接口) --> init3(init-method配置)。但是為什么是這個順序呢?我們可以通過分析其源碼得出結(jié)論。
首先在解析配置文件的時候,碰到context:annotation-config/自定義標(biāo)簽會調(diào)用其自定義解析器,這個自定義解析器在哪兒呢?在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了
我們只關(guān)心這個標(biāo)簽,那我們就進入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);
我們追蹤進去(其中省略了一些我們不關(guān)心的代碼)
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這個注解發(fā)揮作用的基礎(chǔ)。
在bean實例化的過程中,會調(diào)用AbstractAutowireCapableBeanFactory類的doCreateBean方法,在這個方法中會有一個調(diào)用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()) { ? ? ? ? // 調(diào)用@PostConstruct方法注解的地方 ? ? ? ? wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);//① ? ? } ? ? try { ? ? ? ? // 調(diào)用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,其中這個類間接的實現(xiàn)了BeanPostProcessor接口,所以此處會調(diào)用CommonAnnotationBeanPostProcessor類的postProcessBeforeInitialization方法,它本身并沒有實現(xiàn)這個方法,但他的父類InitDestroyAnnotationBeanPostProcessor實現(xiàn)了postProcessBeforeInitialization的方法,其中這個方法就實現(xiàn)調(diào)用目標(biāo)類上有@PostConstruct注解的方法
// 獲取目標(biāo)類上有@PostConstruct注解的方法并調(diào)用 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方法中②這一行代碼,首先判斷目標(biāo)類有沒有實現(xiàn)InitializingBean,如果實現(xiàn)了就調(diào)用目標(biāo)類的afterPropertiesSet方法,然后如果有配置init-method就調(diào)用其方法
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) ? ? ? ? throws Throwable { ? ? // 1、調(diào)用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、調(diào)用init-method方法 ? ? if (mbd != null) { ? ? ? ? String initMethodName = mbd.getInitMethodName(); ? ? ? ? if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && ? ? ? ? ? ? ? ? !mbd.isExternallyManagedInitMethod(initMethodName)) { ? ? ? ? ? ? invokeCustomInitMethod(beanName, bean, mbd); ? ? ? ? } ? ? } }
至此Spring的初始化方法調(diào)用順序的解析就已經(jīng)完了。
spring加載順序典例
借用log4j2,向數(shù)據(jù)庫中新增一條記錄,對于特殊的字段需要借助線程的環(huán)境變量。其中某個字段需要在數(shù)據(jù)庫中查詢到具體信息后插入,在借助Spring MVC的Dao層時遇到了加載順序問題。
解決方案
log4j2插入數(shù)據(jù)庫的方案參考文章:
<Column name="user_info" pattern="%X{user_info}" isUnicode="false" />
需要執(zhí)行日志插入操作(比如綁定到一個級別為insert、logger.insert())的線程中有環(huán)境變量user_info。
解決環(huán)境變量的方法:
攔截器:
@Component public class LogInterceptor implements HandlerInterceptor { /** * 需要記錄在log中的參數(shù) */ 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配置:
@Configuration public 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); } }
如下待優(yōu)化:
問題就出在如何獲取信息這個步驟,原本的方案是:
通過Dao userDao從數(shù)據(jù)庫查詢信息,然后填充進去。
出現(xiàn)的問題是:userDao無法通過@Autowired方式注入。
原因:
調(diào)用處SpringBoot未完成初始化,導(dǎo)致dao層在調(diào)用時每次都是null。
因此最后采用的方式如下:
@Component public class LogInterceptor implements HandlerInterceptor { /** * 需要記錄在log中的參數(shù) */ 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); }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java 如何將表格數(shù)據(jù)導(dǎo)入word文檔中
這篇文章主要介紹了Java將表格數(shù)據(jù)導(dǎo)入word文檔中的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06SpringCloud Gateway 路由配置定位原理分析
本節(jié)主要了解系統(tǒng)中的謂詞與配置的路由信息是如何進行初始化關(guān)聯(lián)生成路由對象的。每個謂詞工廠中的Config對象又是如何被解析配置的2021-07-07maven?scope?provided和runtime的例子說明
這篇文章主要介紹了maven?scope?provided和runtime的例子說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12