回炉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<T>接口,实现其getObject()方法创建bean对象,在配置文件中使用FactoryBean创建bean对象
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
扩展:
手动干预一个bean比另一个bean先加载,可使用@DependsOn(value = {"beanName1","beanName2"})
作用:@DependOn注解指定当前bean所依赖的bean。任何指定的bean都保证由容器在此bean之前创建。
使用场景:在一个bean没有通过属性或构造函数参数显式地依赖另一个bean,而是依赖于另一个bean初始化的副作用的情况下,不经常使用
用法:@DependOn注解可用于@Component注解的类上,或@Bean注解的方法上
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DependsOn { String[] value() default {}; }
三、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.