回炉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接口,实现其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

    

三、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

回到顶部