91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2022-02-14 13:41:37 來源:億速云 閱讀:306 作者:iii 欄目:開發技術

這篇文章主要講解了“spring初始化方法的執行順序及其原理是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“spring初始化方法的執行順序及其原理是什么”吧!

Spring中初始化方法的執行順序

首先通過一個例子來看其順序

/**
 * 調用順序 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"/>

通過運行,我們得出其執行順序為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。

解決環境變量的方法:

攔截器: 

@Component
public class LogInterceptor implements HandlerInterceptor {
    /**
     * 需要記錄在log中的參數
     */
    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);
    }
}

如下待優化:

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

通過Dao userDao從數據庫查詢信息,然后填充進去。

出現的問題是:userDao無法通過@Autowired方式注入。

原因:

調用處SpringBoot未完成初始化,導致dao層在調用時每次都是null。

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

@Component
public class LogInterceptor implements HandlerInterceptor {
    /**
     * 需要記錄在log中的參數
     */
    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初始化方法的執行順序及其原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

西华县| 寻甸| 分宜县| 隆德县| 达拉特旗| 汶上县| 丰宁| 浪卡子县| 瓦房店市| 清流县| 循化| 沂南县| 潢川县| 伊川县| 库尔勒市| 隆安县| 孝昌县| 云浮市| 敦化市| 德钦县| 达拉特旗| 莱州市| 江阴市| 滨海县| 东丽区| 苏尼特左旗| 开化县| 宁武县| 安义县| 泌阳县| 宜兰市| 湘潭市| 准格尔旗| 克拉玛依市| 淮滨县| 安丘市| 靖远县| 河西区| 阳东县| 东乡| 博乐市|