回炉Spring--容器及Bean生命周期
本文内容纲要:回炉Spring--容器及Bean生命周期
一、Spring容器:
** **在基于Spring的应用中,你的应用对象生存于Spring容器(container)中,Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期,从生存到死亡。(在这里,可能就是从new()到finalize())。
容器是Spring框架的核心。Spring容器使用DI(依赖注入)管理构成应用的组件,它会创建相互协作的组件之间的关联。毫无疑问,这些对象更简单干净,更易于理解,更易于重用并且更易于进行单元测试。
Spring自带了多种类型的应用上下文:
AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。
AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。
ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义, 把应用上下文的定义文件作为类资源。
FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
* *XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。
无论是从哪里装载应用上下文,将bean加载到bean工厂的过程都是类似的。
如果你想从Java配置类中加载应用上下文, 那么可以使用AnnotationConfigApplicationContext,来启动Spring容器:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(xxxConfig.class); // 多个Java配置类逗号隔开
在这里没有指定加载Spring应用上下文所需的XML文件, AnnotationConfigApplicationContext通过一个配置类加载bean。
应用上下文准备就绪之后, 我们就可以调用上下文的getBean()方法从Spring容器中获取bean。
** Spring容器的一些疑问?**
** 1、**BeanFactory和FactoryBean的区别
BeanFactory是IOC容器的核心接口,ApplicationContext的父类。Spring使用BeanFactory来实例化、配置和管理bean,采用延迟加载的方式来注入bean,只有在getBean的时候才去加载创建bean
FactoryBean是创建bean的工厂,通过自定义类实现FactoryBean
**2、**BeanFactory和ApplicationContext的区别
ApplicationContext是完整的IOC容器,其由BeanFactory派生而来,具有BeanFactory的所有功能,可以通过配置实现,且额外提供了messageSource,资源访问,aop和web应用,加载多种应用上下文。
并可在启动的时候一次性创建所有需要的单实例bean
二、Spring注解驱动开发
使用Java配置类代替XML配置文件来装配bean:
import org.springframework.beans.factory.FactoryBean;import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
/**
* Spring 基于Java类注解形式的配置
*
* @Configuration :使Spring容器知道这是一个配置类
* @ComponentScan :启用组件扫描,默认扫描本文件所在的包及其子包下声明了@Component注解的类,并在Spring容器中为其创建一个bean,value属性指定要扫描的包
* includeFilters 指定扫描的时候只需要包含哪些组件
* excludeFilters 指定扫描的时候按照什么规则排除那些组件,如:
* excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class):排除Controller注解的类
* @PropertySource :使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量Environment中;加载完外部的配置文件以后使用@Value("${key}" 取出配置的值。
* 也可以用Spring容器获取Environment变量,然后getProperty获取到配置的value。如:
* ConfigurableEnvironment environment = context.getEnvironment();
* String name = environment.getProperty("name");
*
* @Import(xxx.class) :快速给容器中导入一个组件,即将组件注册到Spring容器中; 注册到容器的bean的id为导入类的全限定名
*
*/
@Import(Teacher.class)
@Configuration
@ComponentScan(value = {"com.yang.config", "com.yang.service"})
@PropertySource(value = {"classpath:/config.properties"})
public class ApplicationContextConfig {
/**
* 手动向Spring容器中注册一个bean
* 默认作用域是单例的,bean的类型为方法的返回值,bean的id为方法名,也可以使用@Bean的name属性指定bean的名称。
*
* @Scope :bean的作用域,取值范围:
* ConfigurableBeanFactory#SCOPE_PROTOTYPE singleton 单例,默认,Spring容器启动就会创建对象并放在容器中,以后每次获取都去map缓存中取
* ConfigurableBeanFactory#SCOPE_SINGLETON prototype 原型/多例,Spring容器启动时并不会去创建对象,每次获取的时候都回去创建一个新的对象
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request 同一个请求创建一个对象
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session 同一个session创建一个对象
*
*
* @Lazy :即懒加载,针对单实例作用域的bean,默认值为true,即Spring容器启动的时候不创建,第一次调用的时候才去创建
* 不使用@Lazy注解的话则不是懒加载,Spring容器启动的时候就创建bean
*
* @Conditional :按照一定的条件进行判断,满足条件给容器中注册bean;若用在配置类上,则只有满足条件,此配置类上的所有的bean才会去Spring容器中去注册
* 使用场景如只在linux系统中才创建bean
* Class<? extends Condition>[] value(); 接收一个实现Condition接口的类的class对象,需要自定义类实现Condition接口在matches方法中实
* 现具体判断逻辑,返回true代表符合条件
*
*/
@Lazy(value = false)
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Bean
public Person person() {
return new Person("yangyongjie");
}
/**
* 工厂bean方式注册bean到Spring容器中
* bean的id同样为 方法名,注册到Spring容器中的bean为getObject方法返回的对象
*/
@Bean
public StudentFactoryBean studentFactoryBean(){
return new StudentFactoryBean();
}
/**
* Student类的工厂bean
*/
static class StudentFactoryBean implements FactoryBean<Student> {
/**
* 返回Student对象
*/
@Override
public Student getObject() throws Exception {
return new Student();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
//false 多例 true 单例
return false;
}
}
}
** Java类注解形式配置的一些疑问?**
** 1、**JavaConfig方式创建bean的时候如何实现注入依赖?
1)直接引用创建依赖bean的方法,使用的是带参构造器。但是这种引用并不会创建多个依赖bean的对象,因为Spring将会拦截对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
2)将依赖的bean类型当成@Bean所在方法的一个参数。当Spring调用@Bean注解所在方法创建bean的时候,会自动装配一个参数类型bean到方法之中,然后方法体就可以按照合适的方式来使用它。
这种方式不会像方式1那样要求将依赖bean的声明和当前bean在同一个配置类之中,甚至不要求依赖bean必须在JavaConfig类中声明。
疑问,若有多个同一类型的依赖bean,Spring怎么选择装配?
答案:先参数类型,后参数名称。源码在DefaultListableBeanFactory#doResolveDependency:1213
**2、**Spring为啥把bean默认设计成单例?
优势:为了提高性能
1、减少创建新实例的消耗
2、减少JVM垃圾回收
3、可以快速获取到bean
劣势:不能做到线程安全,bean如果是有状态的话在并发环境下线程不安全
3、@PropertySource("classpath:/config.properties") 是在什么时机解析配置文件的?
步骤如下:
addPropertySource(ConfigurationClassParser.java:512) // 将resource对象解析成PropertySource对象并添加到Spring容器中的environment对象中。并将配置文件名添加到propertySourceNames集合中processPropertySource(ConfigurationClassParser.java:463) // 处理属性配置文件,将自定义的属性配置文件解析成Resource对象
doProcessConfigurationClass(ConfigurationClassParser.java:280) // 如果配置类上有@PropertySource,那么处理属性文件,后面还会处理其他注解,如@ComponentScan等
processConfigurationClass(ConfigurationClassParser.java:250) // 处理Java配置类
processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:323) // 处理配置类中bean的定义信息
postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) // 调用postProcessBeanDefinitionRegistry方法
invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) // 调用BeanDefinitionRegistryPostProcessor(BeanFactroyPostProcessor的子接口)类型的后置处理器
invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) // 调用BeanFactory的后置处理器
** 总结:**
向Spring容器中注册组件(bean)的四种方式:
1、包扫描+组件标注注解(@Component、@Controller、@Service、@Repository)
2、@Bean:适合导入第三方包里面的组件
3、@Import:快速给容器中导入一个组件
①:@Import(要导入的组件的class对象),容器中就会自动注册这个bean,名称是全类名(com.yang.service.TestService)
②:@Import(ImportSelector.class),一个ImportSelector接口的实现类,在重写的selectImports方法中返回需要导入的组件的全类名数组
③:@Import(ImportBeanDefinitionRegistrar.class)手动注册bean到容器中(在ImportBeanDefinitionRegistrar中使用BeanDefinitionRegistry的registerBeanDefinition方法手动注册)
BeanDefinitionRegistry:接口,提供往Spring容器中注册bean,移除bean,获取容器中所有注册了的bean和判断Spring容器中是否注册了某个bean
4、使用Spring提供的 FactoryBean(工厂Bean),与普通bean不同的是,普通bean是调用其无参构造器去创建对象然后注册到容器中,FactoryBean是调用其 T getObject()方法,将返回的对象注册到Spring容器中
①:Spring容器中注册的是FactoryBean getObject返回的对象,bean的id同样是方法名,如:personFactoryBean
②:要获取FactoryBean本身,FactoryBean本身注册到Spring容器中的id为 “&+bean名称”,如&personFactoryBean
三、Spring容器中Bean的生命周期
** **bean创建——属性赋值——初始化——销毁
Spring容器管理bean的生命周期
①:若是单例的bean,在Spring容器的启动后先创建对象,然后初始化,容器关闭的时候销毁bean
②:若是多实例的bean,Spring容器在每次获取bean的时候才去创建对象,然后初始化,但是Spring容器关闭时并不会去销毁bean,需要手动去销毁
bean装配到Spring容器中的一个典型的生命周期过程:
①:Spring容器对bean进行实例化
②:Spring将值和bean的引用注入到bean对应的属性中
③:如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法
④:如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
⑤:如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传进来
⑥:遍历容器中BeanPostProcessor接口的实现bean,调用它们的postProcessBeforeInitialization()方法
⑦:如果bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
⑧:遍历容器中BeanPostProcessor接口的实现bean,调用它们的postProcessAfterInitialization()方法
⑨:此时bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁
⑩:如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法。同样,如果bean使用destory-method声明了销毁方法,该方法也会被调用。
bean初始化和销毁的四种方式:
** **1、指定在bean中自定义初始化和销毁方法,通过@Bean注解指定initMethod和destroyMethod
如:@Bean(initMethod = "customInit",destroyMethod = "customDestory")
2、指定在bean中自定义初始化和销毁方法,然后使用javax提供的注解
@PostConstruct标注在bean的初始化方法上,将在bean创建并属性赋值完成,调用此方法
@PreDestroy标注在bean的销毁方法上,将在容器销毁对象之前调用
3、通过使Bean实现InitializingBean和DisposableBean接口,在重写的afterPropertiesSet和destroy方法实现初始化和销毁逻辑
4、bean的后置处理器,实现BeanPostProcessor接口,在bean的初始化前后做一些工作
针对全局的bean,如果一个bean实现了BeanPostProcessor接口,那么容器中其他所有bean(只针对单例的bean)在初始化方法执行之前都会调用其postProcessBeforeInitialization方法,在初始化方法执行之后都会调用其postProcessAfterInitialization方法
postProcessBeforeInitialization:在任何初始化方式之前调用(在PostConstruct、afterPropertiesSet、initMethod 这些初始化之前调用)
postProcessAfterInitialization:在任何初始化方式之后调用(在PostConstruct、afterPropertiesSet、initMethod 这些初始化之后调用)
因此bean在初始化前后会调用所有实现了BeanPostProcessor接口的bean的postProcessBeforeInitialization和postProcessAfterInitialization方法。
如果四种方式都写了,那么执行顺序为:
①:constructor 创建bean实例
②:postProcessBeforeInitialization BeanPostProcessor的初始化前置方法
③:PostConstruct javax注解定义的初始化方法
④:afterPropertiesSet 实现InitializingBean接口的初始化方法
⑤:initMethod @Bean注解上自定义的初始化方法
⑥:postProcessAfterInitialization BeanPostProcessor的初始化后方法
⑦:PreDestroy javax注解定义的销毁方法
⑧:destroy 实现DisposableBean接口的销毁方法
⑨:destroyMethod @Bean注解上自定义的销毁方法
即初始化和销毁的执行顺序都是:BeanPostProcessor——> PostConstruct/PreDestroy——>InitializingBean/DisposableBean——>@Bean(initMethod /destroyMethod )
四、从源码看bean创建初始化生命周期
MyBeanPostProcessor.postProcessAfterInitialization(MyBeanPostProcessor.java:20) // 执行某个bean后置处理器的postProcessAfterInitialization方法 AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:437) // 应用并执行所有的bean后置处理器的postProcessAfterInitialization方法
at com.yang.domain.Teacher.afterPropertiesSet(Teacher.java:26) // bean初始化
at com.yang.domain.Teacher.postConstruct(Teacher.java:40) // bean自定义初始化方法
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333) //在这个方法中执行自定义的bean的初始化方法,注意:这也是通过后置处理器完成的
InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157) // 执行具体某个bean后置处理器的postProcessBeforeInitialization方法
AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422) // 应用并执行所有的bean后置处理器的postProcessBeforeInitialization方法
AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1808) // 若bean实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口则调用其回调方法
AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778) // 在这个方法中初始化bean
AbstractAutowireCapableBeanFactory.populateBean // 为bean的属性赋值,如注入依赖
AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) // 在这个方法中执行创建bean的逻辑
AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) // 创建bean
AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
AbstractBeanFactory$$Lambda$301.692743054.getObject(Unknown Source:-1)
DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) // 从 Map<String, Object> singletonObjects = new ConcurrentHashMap(256);缓存中获取单实例bean
AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) // 在这个方法中执行获取bean的逻辑
AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) // 先尝试获取bean
DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) // 实例化所有剩余的(非延迟初始化)单例bean
AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) // 初始化剩下的所有单实例对象
AbstractApplicationContext.refresh(AbstractApplicationContext.java:567) // 向BeanFactory中注册所有的bean的后置处理器,用来后续拦截bean的创建过程
AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) // refresh()方法,刷新容器
AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93) // AnnotationConfigApplicationContext构造器中调用 refresh()方法
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContextConfig.class); //创建IOC容器
五、BeanPostProcessor工作原理
** BeanPostProcessor原理**
populateBean(beanName, mbd, instanceWrapper); 给bean进行属性赋值
initializeBean
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd); 执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
遍历得到容器中所有的BeanPostProcessor;挨个执行postProcessBeforeInitialization(),一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
}
** Spring底层对BeanPostProcessor的使用:**
1、其实现类ApplicationContextAwareProcessor可对实现了ApplicationContextAware接口及一些其他xxxAware接口的bean 注入容器ApplicationContext依赖对象及其他 xxx 依赖对象
postProcessBeforeInitialization方法中执行依赖的注入,代码如下:
private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationStartupAware) {
((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
2、其实现类InitDestroyAnnotationBeanPostProcessor,对bean中标注了@PostContruct和@PreDestory注解的方法进行调用执行
3、其实现类AutowiredAnnotationBeanPostProcessor,在对象创建完成以后,对所有标注了@Autowired注解的属性进行注入值
六:属性赋值
** ** 1、使用@Value赋值
此注解值的注入发生在AutowiredAnnotationBeanPostProcessor类中。
使用方式:
①:可以直接写基本数值,如@Value("123")
②:可以写SPEL表达式#{}
③:可以写${},取出在配置文件中配置的(@PropertySource指定外部配置文件),容器启动加载到 环境变量Environment中的值
2、自动装配
创建容器中对象协作关系的行为叫做装配,是DI的核心。
自动装配:即Spring利用依赖注入DI,完成对IOC容器中各个组件的依赖关系赋值
1、@AutoWired,自动注入(可标注在setter方法、参数、构造器上)
1)默认按照类型byType(applicationContext.getBean(xxx.class))去容器中找对应的组件,然后注入;
2)如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
3)@Qualifier("xxx"):指定需要装配的组件的名字
4)自动装配默认一定要将属性赋值好,没有就会报错,可以使用@AutoWired(required=false),允许不注入
5)@Primary:用在依赖的类上,让Spring进行自动装配的时候,默认首选装配它
2、@Resource
和@AutoWired一样实现自动装配功能,默认是按照属性名称ByName进行装配的,按名称没有找到则再按类型进行装配,如果指定了name或者type则按照名称和类型进行装配,没有找到则报错
3、Inject
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能
@Autowired:Spring定义的; @Resource、@Inject都是java规范
以上注解的自动装配是由 AutowiredAnnotationBeanPostProcessor 解析完成的。
4、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
把Spring底层一些组件注入到自定义的Bean中;
xxxAware:功能使用xxxProcessor,是通过后置处理器来实现的。如:ApplicationContextAware==》ApplicationContextAwareProcessor;
3、高级装配
@Profile(value = "env"):指定组件在哪个环境的情况下才能被注册到容器中。
1、加了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中,默认激活环境是default
2、若标注在配置类上,则只有在执行的环境激活的时候,整个配置类里面的所有配置才生效
3、没有标注环境标识@Profile注解的bean在任何环境下都会注册的
如何激活Profile环境:
1、使用命令行动态参数:在虚拟机参数位置加载 -Dspring.profiles.active=test
2、代码的方式激活某个环境变量,需要在启动Spring容器的时候使用无参构造不传配置文件的参数
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 设置需要激活的环境
context.getEnvironment().setActiveProfiles("pre");
// 注册主配置类,多个配置类逗号隔开
context.register(ApplicationContextConfig.class);
// 启动刷新容器
context.refresh();
疑问?
AutowiredAnnotationBeanPostProcessor 注入依赖的时机?
populateBean 给属性赋值,也能注入依赖,那么和AutowiredAnnotationBeanPostProcessor 注入依赖什么关系,先后顺序?
END.
本文内容总结:回炉Spring--容器及Bean生命周期
原文链接:https://www.cnblogs.com/yangyongjie/p/10958143.html
以上是 回炉Spring--容器及Bean生命周期 的全部内容, 来源链接: utcz.com/z/362465.html