最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring初始化方法的執(zhí)行順序及其原理分析

 更新時(shí)間:2022年02月14日 10:32:55   作者:落魄書生已存在  
這篇文章主要介紹了spring初始化方法的執(zhí)行順序及其原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Spring中初始化方法的執(zhí)行順序

首先通過一個(gè)例子來看其順序

/**
?* 調(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"/>

通過運(yùn)行,我們得出其執(zhí)行順序?yàn)閕nit2(PostConstruct注解) --> afterPropertiesSet(InitializingBean接口) --> init3(init-method配置)。但是為什么是這個(gè)順序呢?我們可以通過分析其源碼得出結(jié)論。

首先在解析配置文件的時(shí)候,碰到context:annotation-config/自定義標(biāo)簽會(huì)調(diào)用其自定義解析器,這個(gè)自定義解析器在哪兒呢?在spring-context的spring.handlers中有配置

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

我們進(jìn)入這個(gè)類看

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)心這個(gè)標(biāo)簽,那我們就進(jìn)入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;
}

我們重點(diǎn)看下這行代碼

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

我們追蹤進(jìn)去(其中省略了一些我們不關(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));
? ? }
? ? ...
}

在這個(gè)方法其中注冊(cè)了一個(gè)CommonAnnotationBeanPostProcessor類,這個(gè)類是我們@PostConstruct這個(gè)注解發(fā)揮作用的基礎(chǔ)。

在bean實(shí)例化的過程中,會(huì)調(diào)用AbstractAutowireCapableBeanFactory類的doCreateBean方法,在這個(gè)方法中會(huì)有一個(gè)調(diào)用initializeBean方法的地方,

我們直接看initializeBean這個(gè)方法

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

先看①這行,進(jìn)入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;
}

我們還記得前面注冊(cè)的一個(gè)類CommonAnnotationBeanPostProcessor,其中這個(gè)類間接的實(shí)現(xiàn)了BeanPostProcessor接口,所以此處會(huì)調(diào)用CommonAnnotationBeanPostProcessor類的postProcessBeforeInitialization方法,它本身并沒有實(shí)現(xiàn)這個(gè)方法,但他的父類InitDestroyAnnotationBeanPostProcessor實(shí)現(xiàn)了postProcessBeforeInitialization的方法,其中這個(gè)方法就實(shí)現(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)類有沒有實(shí)現(xiàn)InitializingBean,如果實(shí)現(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ù)庫(kù)中新增一條記錄,對(duì)于特殊的字段需要借助線程的環(huán)境變量。其中某個(gè)字段需要在數(shù)據(jù)庫(kù)中查詢到具體信息后插入,在借助Spring MVC的Dao層時(shí)遇到了加載順序問題。

解決方案

log4j2插入數(shù)據(jù)庫(kù)的方案參考文章:

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

需要執(zhí)行日志插入操作(比如綁定到一個(gè)級(jí)別為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"
    };
    /**
     * 注冊(cè)一個(gè)攔截器
     *
     * @return HpcLogInterceptor
     */
    @Bean
    public LogInterceptor setLogBean() {
        return new LogInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry reg) {
        // 攔截的對(duì)象會(huì)進(jìn)入這個(gè)類中進(jìn)行判斷
        InterceptorRegistration registration = reg.addInterceptor(setLogBean());
        // 添加要攔截的路徑與不用攔截的路徑
        registration.addPathPatterns(logUrl).excludePathPatterns(excludeUrl);
    }
}

如下待優(yōu)化:

問題就出在如何獲取信息這個(gè)步驟,原本的方案是:

通過Dao userDao從數(shù)據(jù)庫(kù)查詢信息,然后填充進(jìn)去。

出現(xiàn)的問題是:userDao無法通過@Autowired方式注入。

原因:

調(diào)用處SpringBoot未完成初始化,導(dǎo)致dao層在調(diào)用時(shí)每次都是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);
    }

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • Java反射(Class類,Class對(duì)象獲取)

    Java反射(Class類,Class對(duì)象獲取)

    下面是對(duì)Java反射機(jī)制是在程序的運(yùn)行過程中,Java語(yǔ)言的反射機(jī)制的超詳細(xì)解說,點(diǎn)進(jìn)來的小伙伴不要錯(cuò)過奧
    2021-08-08
  • 關(guān)于Jar包部署命令全面解析

    關(guān)于Jar包部署命令全面解析

    這篇文章主要介紹了Jar包部署命令全面解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Java中的WeakHashMap淺析

    Java中的WeakHashMap淺析

    這篇文章主要介紹了Java中的WeakHashMap淺析,weakhashmap關(guān)鍵是:當(dāng)一個(gè)對(duì)象被GC回收時(shí),響應(yīng)的值對(duì)象的引用從map中刪除,weakhashmap能節(jié)約存儲(chǔ)空間,來實(shí)現(xiàn)緩存那些非必要的數(shù)據(jù),需要的朋友可以參考下
    2023-09-09
  • Flink自定義Sink端實(shí)現(xiàn)過程講解

    Flink自定義Sink端實(shí)現(xiàn)過程講解

    這篇文章主要介紹了Flink自定義Sink端實(shí)現(xiàn)過程,在Fink官網(wǎng)中sink端只是給出了常規(guī)的write api.在我們實(shí)際開發(fā)場(chǎng)景中需要將flink處理的數(shù)據(jù)寫入kafka,hbase kudu等外部系統(tǒng)
    2023-01-01
  • jQuery 動(dòng)畫效果代碼分享

    jQuery 動(dòng)畫效果代碼分享

    本文給大家分享一段關(guān)于jquery實(shí)現(xiàn)的動(dòng)畫效果,代碼簡(jiǎn)單易懂,非常不錯(cuò),感興趣的朋友參考下
    2016-11-11
  • SpringMVC基于阻塞隊(duì)列LinkedBlockingQueue的同步長(zhǎng)輪詢功能實(shí)現(xiàn)詳解

    SpringMVC基于阻塞隊(duì)列LinkedBlockingQueue的同步長(zhǎng)輪詢功能實(shí)現(xiàn)詳解

    這篇文章主要介紹了SpringMVC基于阻塞隊(duì)列LinkedBlockingQueue的同步長(zhǎng)輪詢功能實(shí)現(xiàn)詳解,本文介紹的也是生產(chǎn)者消費(fèi)者的一種實(shí)現(xiàn),生產(chǎn)者不必是一個(gè)始終在執(zhí)行的線程,它可以是一個(gè)接口,接受客戶端的請(qǐng)求,向隊(duì)列中插入消息,需要的朋友可以參考下
    2023-07-07
  • 詳解Java Streams 中的異常處理

    詳解Java Streams 中的異常處理

    這篇文章主要介紹了Java Streams 中的異常處理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • android中GridView的用法示例

    android中GridView的用法示例

    這篇文章主要介紹了android中GridView的用法,對(duì)于Android初學(xué)者很有參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2014-08-08
  • Java獲取時(shí)間打印到控制臺(tái)代碼實(shí)例

    Java獲取時(shí)間打印到控制臺(tái)代碼實(shí)例

    這篇文章主要介紹了Java獲取時(shí)間打印到控制臺(tái)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Java上傳視頻實(shí)例代碼

    Java上傳視頻實(shí)例代碼

    本文通過實(shí)例代碼給大家講解了java上傳視頻功能,代碼分為頁(yè)面前臺(tái)和后臺(tái),工具類,具體實(shí)例代碼大家通過本文學(xué)習(xí)吧
    2018-01-01

最新評(píng)論

南雄市| 奎屯市| 根河市| 武城县| 游戏| 巫溪县| 沭阳县| 双柏县| 淳安县| 武汉市| 张家界市| 西青区| 富阳市| 奉新县| 镇安县| 湘西| 松桃| 霍林郭勒市| 启东市| 吉水县| 含山县| 利辛县| 西吉县| 潮州市| 清丰县| 巴林左旗| 徐汇区| 特克斯县| 保定市| 乌恰县| 峨眉山市| 那坡县| 海兴县| 千阳县| 建德市| 花莲县| 临澧县| 德惠市| 枝江市| 望谟县| 灵武市|