Spring 总结 IOC-Bean 的生命周期-循环依赖


在这里插入图片描述
    控制反转(IOC)/依赖注入(DI) 和 AOP 面向切面编程是 Spring 的两大核心。
    控制反转:本来,类A 依赖于类B,即类B 具有控制权,(因为如果不把方法给类A,类A就调不了),而 Spring 会将控制权反转,Spring具有控制权(就相当于刚刚的接口),能管理类A与类B之间的关系。

一、 Bean 的实例化三种方式

  • 构造方法实例化
 <bean id="exampleBean" class="ExampleBean" />
 <bean name="example" class="... ...">
  • 静态工厂方法实例化
        通过一个类的静态(工厂)方法来创建 bean。
public class ClientService
  {
       private static clientService clientService=new ClientService( );
        private ClientService() {   }
          public static ClientService createInstance()
            {
                     return clientService;
                     }
  }
<bean id="clientService" class="ClientService" >
factory-method="createInstance" />
  • 实例工厂方法实例化
        实例工厂 和 静态工厂方法类似,区别在于 实例工厂方法 是 通过已经在容器中的 Bean 通过其方法实例化一个新的 Bean。
public class DefaultServiceLocator
  {
   private static clientService clientService=new ClientServiceImpl( );
     
          public  ClientService createClientServiceInstance()
            {
                 return clientService;
                     }
     }
<bean id="serviceLocator" class="DefaultServiceLocator"> </bean>
<bean id="clientService" factory-bean="serviceLocator" 
factory-method="createClientServiceInstance"/>

二、ApplicationContext 的三个常用实现类

(1)ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。否则无法加载。
(2)FileSystemApplicationContext:它可以加载磁盘任意路径下的配置文件(前提是有访问权限)。
(3)AnnotationConfigApplicationContext:单例对象适用。用于读取注解创建容器。


  • ApplicationContext 与 BeanFactory:
        ApplicationContext :原型。创建对象是立即加载,一读取完配置文件就创建对象。
        BeanFactory 采用延迟加载。

三、常见 IOC 注解分类

(1)用于创建对象的注解:
    和在xml 配置文件中写的功能是一样的。
@Componet
把当前对象存入Spring 容器中。
value:用于指定 bean 的ID,如果不写,默认值是当前类名,且首字母改为小写。
    
@Controller
表现层。该注解标注的是 Web 控制器类,和 @Componet 注解等价,只不过在 Web 层使用。经常配合 @RequestMapping 注解使用。
    
@Service
业务层
    
@Repository
持久层
    
     @Component 注解 也就是 @Controller注解、@Service 和@Repository 的通用注解,可以和它们起到相同的作用。
(在不清楚使用那个注解的时候,可以统统使用 Component,为了代码逻辑清晰,还是建议使用具体的注解)。

    这四个注解都是类级别的, 可以不带任何参数,也可以带一个参数,代表 bean 名字,在进行注入的时候就可以通过名字进行注入了。

(2)用于注入数据的注解:
@Autowired
自动按照类型注入,只要容器中有唯一的bean对象类型和要注入的变量类型匹配,就可以注入成功。如果没有任何 bean 的类型匹配,则报错。
出现位置:可以是成员变量,也可以是方法。
Spring IOC 容器是 map 结构的,直接找 value。
使用这个注解时,setter 方法就不是必须的了。
    
@Qualifier
在按照类型注入的基础上再按照名称注入,在给类成员注入时不能单独使用;在给方法参数注入时可以。
如:容器中有三个类型为 Shape 的 Bean,分别名为 :circular,rectanglr,triangle:

@Componet
public class ShapeService
{
  @Autowired
   @Qualifier(value="circular")
     private Shape shape;
  }

使用@Qualifier(value=“circular”) 就会注入cricular 的 bean。
    
@Resource
自动按照名称注入。属性 name。
以上三个注入都只能注入其他 bean 类型的数据,而基本数据类型 和 String 类型无法使用上述注解实现,只能用 @Value(可以用 ${表达式} ,属性 value)。集合类型的注入只能通过 XML 注入。
    


@Resource 和 @Autowired 的区别

     @Resource 是 JDK 的 注解,而 @Autowired 是 Spring 框架中的注解。
    @Autowired 默认情况下是按照 类型 type 装配 bean 的,而 @Resource 默认是按照 名称 name 装配,如果没有匹配的 name ,才会按照 类型 type 进行装配。
    处理这两个注解的后置处理器不同,@Resource 是 CommonAnnotationBeanPostProcessors 处理的。 @Autowired 是AutoWiredAnnotationBeanPostProcessers 处理的。


(3)用于改变作用范围的:
和在中使用scope 属性一样
@Scope
属性 value,指定范围的取值,singleton 和 prototype。
    
(4)和生命周期相关的:
和在中使用 init-method 和 destory-method 属性一样
@PostConstruct
用于指定初始化方法
    
@PreDestory
用于指定销毁方法
    
提供一个配置类 SpringConfiguration ,它的作用和 bean.xml 相同,用 @Configuration 注解。当配置类作为 ApplicationContext 对象创建的参数时,该注解可以不写。
    
@CompontScan
用于通过注解指定 Spring 在创建容器时要扫描的包。
    
@Bean
用于把当前方法的返回值作为 bean 对象,存入 Spring 的IOC 容器中。
name 用于指定 bean 的ID,不写时默认值是当前方法的名称。配置时,如果方法有参数,Spring 会去容器中查找有无可用的 bean 对象,查找的方式 和 @AutoWIred 是一样的。
    
@import
用于导入其他的配置类。属性:value,用于指定其他配置类字节码。当我们使用 import 的注解后,有 Import 注解的就是父配置类,而导入的都是子配置类。
    
@RequestMapping
指定请求和处理方法之间的映射关系。可以用在 控制器类的级别 和 方法级别上。
    


整合 JUnit:
    Junit 不会为我们读取配置文件/配置类 创建 Spring 核心容器,当测试方法执行时,没有 IOC 容器,就算写了 Autowired 注解,也无法实现注入。
     而 Spring 整合 Junit 的配置:
1.导入 Spring 整合 junit 的 jar 包
2.使用 Junit 提供的注解 @Runwith 把 原有的 main 方法替换成 Spting 提供的
3.告诉 Spring 运行器, Spring 的 IOC 创建是基于 xml 的还是 基于 注解的,并且说明位置。


四、Bean 的作用域

1. Singleton 和 Prototype
     默认是单例模式,Bean 在 IOC 容器中只存在一个实例,所有对象对其的引用都返回同一个。
     原型 Prototype 每次请求时都会创建新的实例。
    
2.request 【MVC】
描述:
     每次 HTTP 请求会创建新的 bean 实例,类似于 prototype。
场景:
     一次 HTTP 请求和响应的共享 Bean。
备注:
限定 SpringMVC 中使用。
    
3.session【MVC】
描述:
     在一个 HTTP Session 中,定义一个 Bean 实例。
场景:
     用户回话的共享 Bean,比如记录一个用户的登录信息。
备注:
限定 SpringMVC 中使用。
    
4.global session
描述:
     类似于 HTTP Session 但限于 protlet web 应用使用。
场景:
     所有构成某个 portlet web 应用的各种不同的 protlet 所共享。
备注:
     限定 protlet web 应用,在编写一个标准的基于 Servlet 的 web 应用,并且定义了一个或多个具有 global session 作用域的 bean,HTTP session 作用域,而且不会引起任何错误。
    
5.application session【MVC】
描述:
     在一个 HTTP Servlet Context 中,定义一个 Bean 实例。
场景:
     Web 应用的上下文,比如:记录一个应用的共享信息。
备注:
     限定 SpringMVC 中使用。
    

五、Bean 的生命周期

(1)实例化 ApplicationContext 对象
(2)扫描类、解析类
(3)生成 BeanDefinition 对象,并把解析到的信息:如 类名、父类、类型:原型 prototype 还是 单例 Singleton;有无延迟加载等信息作为对象的属性。
(4)把 BeanDefiniation put 到 map 中。
(5)Spring 会遍历这个 map,先验证,返回 String 类型的 BeanName
(6)推断构造方法,获取所有的 Constructor,根据合适的构造方法通过反射构建对象,并把对象封装成 BeanWrapper 包装类对象。
(7)看是否允许循环依赖,默认是单例模式,是允许循环依赖的,会把对象封装成 SingletonFactory,放置在 SingletonFactories 中。这是二级缓存,本质是个 HashMap,key 是 String 类型,value 是 ObjectFactory 类型。
(8)populateBean 进行属性注入
(9)回调 Aware 接口
(10)执行初始化生命周期方法 invokeInitMethod,顺序是 注解、接口、XML
(11)是否需要代理,需要的话会完成 AOP
(12)经过以上步骤后会把对象放置到一级缓存 SingletonObjects 中,这是个 ConcurrentHashMap,key 是 String 类型,value 是 Object ,放置的是经过完整生命周期的 Bean。
     以上是单例模式,如果是原型或者延迟加载,只有获取对象时才会创建对象,而容器关闭时单例对象会销毁,而对于原型,一个类不只一个对象,所以不会销毁。

六、Spring 解决循环依赖

    先说说什么是循环依赖:比如 a 中有属性 b,并通过 setB 方法设置;同理,b 中有属性 a,并通过 setA 设置,这样就出现了循环依赖。默认 单例模式是允许循坏依赖的,而原型模式下是会报错的。
    假设先走 A 的生命周期,是允许循环依赖的,就会在二级缓存 SingltonFactories 中放置 A 的工厂对象,而且还有一个 Set 是放置“正在创建中”的对象的 beanName 集合,A 的 beanName 会被放置到这个集合中,到了要进行属性注入的时候,会调用 getBean(B) 尝试获取 B 对象,先去一级缓存 SingletonObjects 中尝试获取,是获取不到的,接下来会判断 B 是否在创建中,到 Set 中获取,也获取不到,这时会去创建 B ,走 B 的生命周期,那相同地,B 也会在二级缓存中生成工厂对象、在 Set 中放置 B 的 beanName,到了 B 需要注入属性时,调用 getBean(A) 尝试获取对象 A,先到一级缓存中,获取不到,这时会判断 A 是否正在创建中,在 Set 中是可以获取到 A 的 beanName 的,就会先去三级缓存 EarlySingletonObjects 中,获取不到,再去二级缓存 SingletonFactories 中,可以获取到 A 的工厂,通过 getObject() 方法就可以通过工厂获取到 A 对象,而且会把工厂从二级缓存中 remove() 移除,并把对象放置到三级缓存中,那么获取到 A 对象就可以作为属性给 B 注入了,B继续向下走生命周期,直到放置到 单例池 SingltonObjects 中,就生成了完整的 bean B,然后回退,把它作为 属性注入给 A,A再继续向下走生命周期,这样 A、B 就分别走完了完整的生命周期并进行了相互的属性注入。

七、Bean 三级缓存的作用

一级:SingletonObjects 放置经过完整生命周期的对象
二级:SingletonFactories 用于解决循环依赖,提供工厂,方便代理
三级:EalrySingletonObjects 用于提前暴露对象,如复杂的循环依赖 a、b、c,如果 b 注入属性 a,会从 二级缓存获取到 a,接着移到三级缓存中,接下来 c 也需要注入属性 a 时,就会先从三级缓存中获取,避免重复生产。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值