一、spring类初始化@PostConstruct > InitializingBean > init-method

  InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的子类,在初始化bean的时候会执行该方法。

示例

<bean id="myInitializingBean" class="com.paic.phssp.springtest.init.MyInitializingBean" init-method="testInit"></bean>

bean

/**
* 继承InitializingBean接口的类,在初始化bean的时候会执行该方法
*/
//@Component
public class MyInitializingBean implements InitializingBean { public MyInitializingBean() {
System.out.println("1MyInitializingBean....");
} @Override
public void afterPropertiesSet() throws Exception {
System.out.println("3ceshi MyInitializingBean>>>>>>>>>>>>>>>>>>>");
} @PostConstruct //功能上近似init-method,但加载时机不同
public void test(){
System.out.println("2PostConstruct >>>>>>>>>>>>");
} public void testInit(){
System.out.println("4ceshi init-method");
}
}

结果:

1MyInitializingBean....
2PostConstruct >>>>>>>>>>>>
3ceshi MyInitializingBean>>>>>>>>>>>>>>>>>>>
4ceshi init-method

说明:

  通过上述输出结果,三者的先后顺序:Constructor > @PostConstruct > InitializingBean > init-method

1.1、 InitializingBean > init-method 执行时机

  spring初始化bean过程

  002-创建型-03-单例模式(Singleton)【7种】、spring单例及原理  spring 单例

  通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可看出

  如使用getBean方式创建:getBean→doGetBean→createBean→doCreateBean→initializeBean→invokeInitMethods

  查看AbstractAutowireCapableBeanFactory.invokeInitMethods

    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable { 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((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
} if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

  说明:

  a、spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用
  b、实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
  c、如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。
  d、@PostConstruct注解后的方法在BeanPostProcessor前置处理器中就被执行了,所以当然要先于InitializingBean和init-method执行了。

1.2、@PostConstruct加载过程

  

源码:InitDestroyAnnotationBeanPostProcessor.class

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

findLifecycleMetadata

    private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(clazz);
}
// Quick check on the concurrent map first, with minimal locking.
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
metadata = buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
}
return metadata;
}

buildLifecycleMetadata

    private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
final boolean debug = logger.isDebugEnabled();
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz; do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>(); ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (debug) {
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (debug) {
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
}); initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class); return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}

判断指定类型注解

if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {

  方法:buildLifecycleMetadata(),判断是否是指定的注解类型,而这个属性,在CommonAnnotationBeanPostProcessor.class构造方法中被初始化为PostConstruct。

    public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}

1.3、 @PostConstruct > InitializingBean > init-method 执行时机

查看AbstractAutowireCapableBeanFactory的InitializingBean 方法

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
} Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
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,第二部是 invokeInitMethods 【先执行了 ((InitializingBean) bean).afterPropertiesSet(),进而执行了init-method自定义invokeCustomInitMethod(beanName, bean, mbd);

小结:BeanPostProcessor的实现类注册到Spring IOC容器后,对于该Spring IOC容器所创建的每个bean实例在初始化方法(如afterPropertiesSet和任意已声明的init方法)调用前,将会调用BeanPostProcessor中的postProcessBeforeInitialization方法,而在bean实例初始化方法调用完成后,则会调用BeanPostProcessor中的postProcessAfterInitialization方法,整个调用顺序可以简单示意如下:
  --> Spring IOC容器实例化Bean
  --> 调用BeanPostProcessor的postProcessBeforeInitialization方法 (@PostConstruct在此)
  --> 调用bean实例的初始化方法(invokeInitMethods-> InitializingBean->init-method)
  --> 调用BeanPostProcessor的postProcessAfterInitialization方法

DisposableBean结束:https://www.cnblogs.com/xiaozhuanfeng/p/10415794.html

二、依赖注入ApplicationContext

方法一、@Autowired

创建一个User,内部使用ApplicationContext

@Component
public class User {
@Autowired
private ApplicationContext applicationContext;
public void show() {
System.out.println("User:"+applicationContext.getClass());
}
}

使用

    public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.lhx.spring.kuozhan");
System.out.println(context.getBean("user"));
// System.out.println(context.getBean("createUser"));
User bean = (User) context.getBean("user");
bean.show();
context.close();
}

方法二、实现ApplicationContextAware接口

@Component
public class Book implements ApplicationContextAware{
private ApplicationContext ApplicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ApplicationContext=applicationContext;
} public void show() {
System.out.println("book:"+ApplicationContext.getClass());
}
}

1)内部原理

接口BeanPostProcessor,内部方法,每一个bean初始化都会被执行

  bean初始化属性完毕后,即依赖装配完成之后,postProcessBeforeInitialization

  bean初始化在属性设置之后,Bean init之后触发的,postProcessAfterInitialization

  作用:回调,返回代理对象等

  如在postProcessBeforeInitialization中返回其他代理对象。

构建一个默认实现【两个方法不能返回null】

package com.lhx.spring.kuozhan;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component; @Component
public class EchoBeanPostProcessor implements BeanPostProcessor { @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("--------------postProcessBeforeInitialization-------------" + "bean" + bean.getClass());
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("--------------postProcessBeforeInitialization-------------" + "bean" + bean.getClass());
return bean;
} }

2)示例

User类

@Component
public class User {
private ApplicationContext applicationContext; @Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
} public void init() {
System.out.println("User init"); } public void show() {
System.out.println("User:" + applicationContext.getClass());
}
}

UserConfig类

@Configuration
public class MyConfig {
@Bean(initMethod = "init")
public User createUser() {
return new User();
}
}

调用

System.out.println(context.getBean("user"));

查看日志

User Set属性
--------------postProcessBeforeInitialization-------------beanclass com.lhx.spring.kuozhan.User
User init
--------------postProcessAfterInitialization-------------beanclass com.lhx.spring.kuozhan.User

3)自行实现ApplicationContextAware,这里起名为SpringContextAware,逻辑参考ApplicationContextAwareProcessor

public interface SpringContextAware {
public void setApplicationContext(ApplicationContext applicationContext);
}

编写ContextBeanPostProcessor

package com.lhx.spring.kuozhan;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; @Component
public class ContextBeanPostProcessor implements BeanPostProcessor {
@Autowired
private ApplicationContext applicationContext; @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SpringContextAware) {
SpringContextAware sca = (SpringContextAware) bean;
sca.setApplicationContext(applicationContext);
}
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
return bean;
} }

实际类使用

package com.lhx.spring.kuozhan;

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; @Component
public class Dog implements SpringContextAware { private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
} public void show() {
System.out.println("Dog:" + applicationContext.getClass());
}
}

使用:

context.getBean(Dog.class).show();

4)查看ApplicationContextAware内部实现:

AnnotationConfigApplicationContext→GenericApplicationContext→AbstractApplicationContext

找打refresh方法,prepareBeanFactory,

// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

ApplicationContextAwareProcessor内部

class ApplicationContextAwareProcessor implements BeanPostProcessor

方法三、Spring 4.3 新特性,构造方法直接添加,有局限性,与构造方法直接相关

@Component
public class Bank {
private ApplicationContext applicationContext;
//spring 4.3 提供,与构造方法调用有关
public Bank(ApplicationContext applicationContext) {
this.applicationContext=applicationContext;
}
public void show() {
System.out.println("book:"+applicationContext.getClass());
}
}

二、容器扩展

2.1、BeanFactoryPostProcessor

在beanFactory之后,BeanFactoryPostProcessor容器初始化之后,只初始化一次,先于所有容器以及BeanPostProcessor

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

示例代码扩展2

源码:

AnnotationConfigApplicationContext→GenericApplicationContext→AbstractApplicationContext

找打refresh方法

    public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

2.2、BeanDefinitionRegistryPostProcessor

BeanFactoryPostProcessor的子类BeanDefinitionRegistryPostProcessor

void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

注册一个Bean到Spring容器中,类似标注了@Componment

示例

新建一个Person类

public class Person {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Person [name=" + name + "]";
} }

MyBeanDefinitionRegistryPostProcessor实现

package com.lhx.spring.kuozhan2;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component; @Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub } @Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
for (int i = 0; i < 10; i++) {
BeanDefinitionBuilder rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class);
rootBeanDefinition.addPropertyValue("name", "admin" + i);
registry.registerBeanDefinition("person" + i, rootBeanDefinition.getBeanDefinition());
} } }

使用

    public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.lhx.spring.kuozhan2");
System.out.println(context.getBean("person1"));
context.getBeansOfType(Person.class).values().forEach(System.out::println);
context.close();
}

当然在使用

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.lhx.spring.kuozhan2");

使用context也可以注入

context.registerBeanDefinition(beanName, beanDefinition);
beanDefinition定义可以使用:BeanDefinitionBuilder rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class);

代码地址:https://github.com/bjlhx15/spring-boot.git

003-Spring4 扩展分析-spring类初始化@PostConstruct > InitializingBean > init-method、ApplicationContext、BeanPostProcessor、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor的更多相关文章

  1. spring bean初始化及销毁你必须要掌握的回调方法

    spring bean在初始化和销毁的时候我们可以触发一些自定义的回调操作. 初始化的时候实现的方法 1.通过java提供的@PostConstruct注解: 2.通过实现spring提供的Initi ...

  2. Spring Boot(七)扩展分析

    前面的章节在分析SpringBoot启动过程中,我们发现SpringBoot使用Spring框架提供的SpringFactoriesLoader这个类,实现检索META-INF/spring.fact ...

  3. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  5. 分析java类的初始化契机

    分析java类的静态成员变量初始化先于非静态成员变量   依上图中当class字节码文件被jvm虚拟机加载到内存中依次经过 连接 验证:对字节码进行验证 准备:给静态变量分配内存并赋予变量类型各自的默 ...

  6. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

  7. spring框架中@PostConstruct的实现原理

    在spring项目经常遇到@PostConstruct注解,首先介绍一下它的用途: 被注解的方法,在对象加载完依赖注入后执行. 此注解是在Java EE5规范中加入的,在Servlet生命周期中有一定 ...

  8. 🙈羞,Spring Bean 初始化/销毁竟然有这么多姿势

    文章来源:http://1t.click/bfHN 一.前言 日常开发过程有时需要在应用启动之后加载某些资源,或者在应用关闭之前释放资源.Spring 框架提供相关功能,围绕 Spring Bean ...

  9. Spring IOC初始化深度解析

    1.前言 本文是基于JAVA配置方法对Spring IOC进行分析,掌握Spring IOC初始化流程对于我们更好的使用Spring.学习Spring还是很有帮助的,本文所使用的的Spring版本为5 ...

随机推荐

  1. 网络初级篇之VLAN间路由(原理与配置)

    一.VLAN间的路由 由于VLAN隔离了二层广播域,也间接的隔离了各个VLAN之间的其他二层流量交换,这样导致属于不同VLAN之间的用户不能进行二层的通信.只能经过三层的路由转发才能将报文从一个VLA ...

  2. 生产者消费者问题--lock

    # 代码: public class App { public static void main(String[] args) { Depot depot = new Depot(100); Prod ...

  3. k8sStatefulSet控制器

    一.StatefulSet概述 应用程序存在有状态和无状态两种类别,因为无状态类应用的pod资源可按需增加.减少或重构,而不会对由其提供的服务产生除了并发相应能力之外的其他严重影响.pod资源的常用控 ...

  4. 根据传入url请求,返回json字符串

    /** * 根据传入url请求,返回json字符串 * @param url * @return * @throws UnsupportedEncodingException */ public st ...

  5. IPC 进程间通信方式——信号量

    信号量 本质上是共享资源的数目,用来控制对共享资源的访问. 用于进程间的互斥和同步 每种共享资源对应一个信号量,为了便于大量共享资源的操作引入了信号量集,可对多对信号量一次性操作.对信号量集中所有的操 ...

  6. springboot整合jetty(转)

    jdk1.8 springboot替换容器在网上搜索只需要两步如果不是可能就会报错Unable to start EmbeddedWebApplicationContext due to missin ...

  7. [Flask]sqlalchemy使用count()函数遇到的问题

    sqlalchemy使用count()函数遇到的问题 在使用flask-sqlalchemy对一个千万级别表进行count操作时,出现了耗时严重.内存飙升的问题. 原代码: # 统计当日登陆次数 co ...

  8. js--BOM对象(2)

    一.window对象是整个bom的核心 二.window对象的属性: history:(有关客户访问过的url信息) 方法: back() 加载 history 对象列表中的前一个URL forwar ...

  9. JavaWeb-SpringSecurity在数据库中查询登陆用户

    系列博文 项目已上传至guthub 传送门 JavaWeb-SpringSecurity初认识 传送门 JavaWeb-SpringSecurity在数据库中查询登陆用户 传送门 JavaWeb-Sp ...

  10. Android_(控件)使用自定义控件在屏幕中绘制一条虚线

    在Android屏幕中绘制虚线,最通用的是自定义控件DashedLine,再将自定义控件放入xml布局中 运行截图: 程序结构 package com.example.asus.gary_042; i ...