2023-1-11【spring IOC + Spring AOP】

1、Spring  IOC理解及循环依赖【先有鸡还是先有蛋】?

IOC 全称是 Inversion of Control,意思是控制反转,它的设计思想是,将设计好的对象交给有一个专门的容器来管理,管理这些对象的生命周期,控制对象的创建,并且由这个IOC容器控制外部资源的获取,与传统的直接由对象内部来new创建对象是不同的。

        好处是:使用了Ioc的设计思想,将对象的创建,查找依赖,以及生命周期的控制权交给了Ioc 容器。对象之间耦合较松,更加灵活。

        什么是循环依赖:【例子】

public class A {
    private B b;
    // 省略set/get方法
}
 
public class B {
    private A a;
    // 省略set/get方法
}

可以看到A类里有一个属性是B类对象,而B类里也有一个属性是A类对象,则我们可以称A类对象与B类对象之间互相循环依赖。然后我们对把这俩个类纳入到IOC容器中进行管理,现在进行xml配置:

<bean id="a" class="com.A">
    <property name="b" ref="b"/>
</bean>
 
<bean id="b" class="com.B">
    <property name="a" ref="a"/>
</bean>
@Test
public void test() throws Exception {
 // 创建IoC容器,并进行初始化
 String resource = "spring/spring-ioc-circular-dependency.xml";
 ApplicationContext context = new ClassPathXmlApplicationContext(resource);
 // 获取ClassA的实例(此时会发生循环依赖)
 ClassA classA = (ClassA) context.getBean(ClassA.class);
}

调用getBean方法来获取某个对象,那么会发生什么事情呢?正常逻辑应该是发生了死循环a对象的创建需要依赖b对象,而b对象的创建同时也需要a对象

但是SpringIOC却解决了这个问题,并且你可以正常的获取到相应的对象而不会发生错误。

2、Spring  IOC是如何解决循环依赖的

        Spring  IOC 解决循环依赖的思路是依靠三级缓存

        Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性

三级缓存的划分及作用:

一级缓存: singletonObjects存储的是所有创建好了的单例Bean
二级缓存:earlySingletonObjects完成实例化,但是还未进行属性注入及初始化的对象
三级缓存:singletonFactories提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象。
 

  1. 获取A时首先会尝试从一级缓存 中获取;
  2. 获取不到就再从二级缓存中获取;
  3. 若是还没有则尝试从三级缓存获取;
  4. 还是没有获取到则再尝试创建A对象
  5. 会执行doGetBean->createBean->createBeanInstance并使用构造器实例化
  6. 在尝试给A进行初始化时,由于B不存在无法完成初始化,则将半成品A放入第二级缓存中,进入B的创建流程。
  7. 与先前过程相似,在第三级缓存中放入beanName和表达式sharedInstance,进入B的初始化过程
  8. 由于在第二级缓存中可以找到A,则B可以完成初始化,将成品Bean放入一级缓存中备用,删除三级缓存中的B
  9. 同时完成A的初始化,并删除二级缓存中的半成品A
     

总结:

1、要获取一个bean,先从一级缓存一直查找到三级缓存,缓存bean的时候是从三级到一级的顺序保存,并且缓存bean的过程中,三个缓存都是互斥的,只会保持bean在一个缓存中,而且,最终都会在一级缓存中。
2、Bean在实例化后(createBeanInstance)、属性注入前(populateBean),会先将属性为null的Bean包装成对象工厂(ObjectFactory)放入三级缓存中,在属性注入过程中会依次从一级到三级查询缓存查找依赖的Bean,不存在则先实例化依赖的Bean,完成属性注入。Bean初始化完成后,会被放入一级缓存。
3、三级缓存其实是为了解决代理对象之间(AOP)的循环依赖,通过第三级缓存我们可以拿到可能经过包装的对象,解决对象代理封装的问题。

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取。

  • 先获取到三级缓存中的工厂;
  • 调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
  • 当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

3、Spring  AOP是什么及其实现?

       面向切面 的编程用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性

Spring通知有哪些类型:

  1. 前置通知(Before):在目标方法被调用之前调用通知功能;
  2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

Spring AOP通过以下两种方式来使用。但是最广泛使用的方式是Spring AspectJ 注解风格(Spring AspectJ Annotation Style)

  • 使用AspectJ 注解风格
  • 使用Spring XML 配置风格

Spring AOP 代理是什么?

实现 AOP 的技术,主要分为两大类:

  • 一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
  • 二是采用静态织入的方式,引入特定的语法创建 “ 方面 ” ,从而使得编译器可以在编译期间织入有关 “ 方面 ” 的代码。

SpringAOP是动态代理来实现的。有两种代理方式:JDK动态代理与CGLIB动态代理
(1) JDK动态代理:是通过反射来接收被代理类,要求必须实现一个接口
  (2) CGLIB动态代理:当被代理类没有实现一个接口的时候,就会使用CGLIB进行动态代理。CGLIB动态代理通过运行时动态生成被代理类的子类,运用继承的方式来实现动态代理。如果被代理类被final修饰了,那么就不能使用CGLIB进行动态代理了
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值