深入了解Spring之Bean(声明周期等)
GitHub:https://github.com/JDawnF/learning_note
目录
1、Spring 的配置方式
- 
	
Bean 由 Spring IoC 容器实例化,配置,装配和管理。
 - 
	
Bean 是基于用户提供给 IoC 容器的配置元数据 Bean Definition 创建。
 
单纯从 Spring Framework 提供的方式,一共有三种:
1、XML 配置文件。
Bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头。例如:
<bean id="studentBean" class="org.edureka.firstSpring.StudentBean">
     <property name="name" value="Edureka"></property>
 </bean>
2、注解配置。
- 
	
@Bean注解扮演与<bean />元素相同的角色。 - 
	
@Configuration类允许通过简单地调用同一个类中的其他@Bean方法来定义 Bean 间依赖关系。 - 
	
例如:
@Configuration public class StudentConfig { @Bean public StudentBean myStudent() { return new StudentBean(); } }可以通过在相关的类,方法或字段声明上使用注解,将 Bean 配置为组件类本身,而不是使用 XML 来描述 Bean 装配。默认情况下,Spring 容器中未打开注解装配。因此,需要在使用它之前在 Spring 配置文件中启用它。例如:
 
<beans>
 <context:annotation-config/>
 <!-- bean definitions go here
     或者通过下面这种方式,指定一个包:
     <context:component-scan base-package="cn.gacl.java"/>    -->    
 </beans>
3、Java Config 配置。
- 
	
Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。
 
目前主要使用 Java Config 配置为主。当然,三种配置方式是可以混合使用的。例如说:
- 
	
Dubbo 服务的配置,一般使用 XML 。
<dubbo:protocol name="dubbo" port="20884"></dubbo:protocol> <dubbo:application name="lehuan-search-service"/> <dubbo:registry address="zookeeper://192.168.98.135:2181"/> <!--相当于包扫描--> <dubbo:annotation package="com.lehuan.search.service.impl"/> <dubbo:provider delay="-1" timeout="10000"/> - 
	
Spring MVC 请求的配置,一般使用
@RequestMapping注解。 - 
	
Spring MVC 拦截器的配置,一般 Java Config 配置。
 
另外,现在已经是 Spring Boot 的天下,所以更加是 Java Config 配置为主。
2、Bean Scope作用域
Spring Bean 支持 5 种 Scope ,分别如下:
- 
	
Singleton - 每个 Spring IoC 容器仅有一个单 Bean 实例。默认
Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是 Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:
<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>
 - 
	
Prototype - 每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例。
每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的 bean 使用 prototype 作用域,而对无状态的 bean 使用 singleton作用域。
 - 
	
Request - 每一次 HTTP 请求都会产生一个新的 Bean 实例,并且该 Bean 仅在当前 HTTP 请求内有效。
当前 Http 请求结束,该 bean实例也将会被销毁。
<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
 - 
	
Session - 每一个的 Session 都会产生一个新的 Bean 实例,同时该 Bean 仅在当前 HTTP Session 内有效。
 - 
	
Global session - 在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效。(在Spring5中)
 
开发者是可以自定义 Bean Scope ,具体可参见 《Spring(10)—— Bean 作用范围(二)—— 自定义 Scope》
3、Bean的生命周期
- 
	
通过构造器或工厂方法创建Bean实例
 - 
	
为Bean的属性设置值和对其它Bean的引用
 - 
	
调用Bean的初始化方法
 - 
	
Bean可以使用了
 - 
	
当容器关闭时,调用Bean的销毁方法
 
Spring Bean 的初始化流程如下:
实例化 Bean 对象
- 
	
实例化一个 Bean,也就是我们常说的 new。Spring 容器根据配置中的 Bean Definition(定义)中实例化 Bean 对象。
Bean Definition 可以通过 XML,Java 注解或 Java Config 代码提供。
 
IOC 依赖注入:按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。
setBeanName 实现:如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的 setBeanName(String name) 方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值
BeanFactoryAware 实现:如果这个 Bean 已经实现了 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory(BeanFactory beanFactory) 方法。setBeanFactory(BeanFactory)传递的是 Spring 工厂自身(可以用这个方式来获取其它 Bean, 只需在 Spring 配置文件中配置一个普通的 Bean 就可以)。
ApplicationContextAware 实现:如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用setApplicationContext(ApplicationContext)方法,传入 Spring 上下文(同样这个方式也 可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接 口,有更多的实现方法)
postProcessBeforeInitialization 接口实现-初始化预处理:如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用 作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应 用于内存或缓存技术。
init-method:如果 Bean 在 Spring 配置文件中配置了<bean />的 init-method 属性,会自动调用其配置的初始化方法。
postProcessAfterInitialization:如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessAfterInitialization(Object obj, String s)方法。 注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一 般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中 也可以配置非 Singleton。
Destroy 过期自动清理阶段:当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的 destroy()方法;
destroy-method 自配置清理:
- 
	
最后,如果这个 Bean 的 Spring 配置中配置了 <bean />的destroy-method 属性,会自动调用其配置的 销毁方法。
 - 
	
bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们你可以自己定制 初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。
 
 <bean id="" class="" init-method="初始化方法" destroy-method="销毁方法"> 


4、内部Bean
只有将 Bean 仅用作另一个 Bean 的属性时,才能将 Bean 声明为内部 Bean。
- 
	
为了定义 Bean,Spring 提供基于 XML 的配置元数据在
<property>或<constructor-arg>中提供了<bean>元素的使用。 - 
	
内部 Bean 总是匿名的,并且它们总是作为原型 Prototype 。
 
例如,假设我们有一个 Student 类,其中引用了 Person 类。这里我们将只创建一个 Person 类实例并在 Student 中使用它。示例代码如下:
// Student.java
 public class Student {
     private Person person;
     // ... Setters and Getters
 }
 // Person.java
 
 public class Person {
     private String name;
     private String address;    
     // ... Setters and Getters
 }
 <!-- bean.xml -->
 
 <bean id=“StudentBean" class="com.edureka.Student">
     <property name="person">
         <!--This is inner bean -->
         <bean class="com.edureka.Person">
             <property name="name" value=“Scott"></property>
             <property name="address" value=“Bangalore"></property>
         </bean>
     </property>
 </bean>
可以理解为在一个bean标签中引用了另一个bean,并且这个内部bean是作为外部bean的属性存在的。
5、Spring 装配
当 Bean 在 Spring 容器中组合在一起时,它被称为装配或 Bean 装配。Spring 容器需要知道需要什么 Bean 以及容器应该如何使用依赖注入来将 Bean 绑定在一起,同时装配 Bean 。
装配,和上文提到的 DI 依赖注入,实际是一个东西。
自动装配
Spring 容器能够自动装配 Bean 。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 Bean 的协作者。
自动装配的不同模式:
- 
	
no - 这是默认设置,表示没有自动装配。应使用显式 Bean 引用进行装配。
 - 
	
byName - 它根据 Bean 的名称注入对象依赖项。它匹配并装配其属性与 XML 文件中由相同名称定义的 Bean 。
 - 
	
【最常用】byType - 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 Bean 类型匹配,则匹配并装配属性。是@Autowired这个Spring注解默认的注入方式。
 - 
	
构造函数(constructor) - 它通过调用类的构造函数来注入依赖项。它有大量的参数。
 - 
	
autodetect - 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。
 
6、延迟加载
默认情况下,容器启动之后会将所有作用域为单例的 Bean 都创建好,但是有的业务场景我们并不需要它提前都创建好。此时,我们可以在Bean 中设置 lzay-init = "true" 。
- 
	
这样,当容器启动之后,作用域为单例的 Bean ,就不在创建。
 - 
	
而是在获得该 Bean 时,才真正在创建加载。
 
7、单例 Bean 是否线程安全
Spring 框架并没有对单例 Bean 进行任何多线程的封装处理。
- 
	
关于单例 Bean 的线程安全和并发问题,需要开发者自行去搞定。
 - 
	
并且,单例的线程安全问题,也不是 Spring 应该去关心的。Spring 应该做的是,提供根据配置,创建单例 Bean 或多例 Bean 的功能。
 
当然,但实际上,大部分的 Spring Bean 并没有可变的状态(比如Serview 类和 DAO 类),所以在某种程度上说 Spring 的单例 Bean 是线程安全的。
如果你的 Bean 有多种状态的话,就需要自行保证线程安全。最浅显的解决办法,就是将多态 Bean 的作用域( Scope )由 Singleton 变更为 Prototype 。
8、Spring解决循环依赖
参照:芋道源码
                    
                
                
            
        
浙公网安备 33010602011771号