Spring中IOC与AOP的基本介绍

一.IOC

1.1IOC简介

IOC(Inversion of Control)是Spring中核心思想之一.IOC就是控制权的颠倒,传统的程序中,对象的创建和控制是由程序员负责,而在Spring中,IOC容器负责管理对象的创建以及它们之间的依赖关系,通过配置来控制程序的流程.这样,我们只需要关注业务逻辑的实现,而不必关心对象是如何创建、在哪里创建以及如何组合的.

1.2IOC的底层实现

通过工厂模式实现Bean的创建,使用工厂提供的getBean方法,利用反射获取到Bean.

1.3DI(Dependency Injection)依赖注入

注入方式:

  • set注入

  • 构造器注入

  • 工厂注入

    • 实例工厂

    • 静态工厂

  • 自动注入

1.4自动注入的注解

  • @Autowired:根据类型实现Bean的自动装配

  • @Qualifier:与@Autowired配合使用,实现Bean按照名称的自动装配

  • @Resource:JDK提供的根据名称自动注入Bean

  • @Component:将Bean标记为可被自动扫描的组件注入容器

  • @Configuation:将Bean标记为Spring的配置类注入容器

1.5bean的生命周期

1.容器启动,实例化Bean对象

2.为Bean的属性进行注入

3.如果Bean 实现了BeanNameAware 接口,则 Spring调用Bean的setBeanName()方法传入当前Bean的id值

4.如果Bean实现了BeanFactoryAware 接口,则 Spring 调用setBeanFactory()方法传入当前工厂实例的引用。

5.如果Bean 实现了ApplicationContextAware 接口,则 Spring调用setApplicationContext()方法传入当前ApplicationContext 实例的引用。

6.如果Bean实现了BeanPostProcessor接口 ,则 Spring将调用该接口的预初始化方法postProcessBeforelnitialzation()对 Bean进行加工操作,此处非常重要,Spring的AOP就是利用它实现的。

7.如果Bean实现了InitializingBean接口,则 Spring将调用afterPropertiesSet()方法。

8.如果在配置文件中通过 init-method属性指定了初始化方法,则调用该初始化方法。

9.如果Bean实现了BeanPostProcessor接口 ,则 Spring将调用该接口的初始化方法postProcessAfterlntialization()。此时,Bean已经可以被应用系统使用了。

10.如果在 中指定了该Bean的作用范围为scope=“singleton”,则将该Bean放入Spring IOC的缓存池中,将触发Spring对该Bean 的生命周期管理;如果在中指定了该Bean的作用范围为scope=“prototype”,则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该 Bean。

11.执行自身的业务方法【使用bean】

12.如果Bean实现了DisposableBean接口,则 Spring 会调用destory()方法将Spring 中的 Bean销毁; 如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则 Spring将调用该方法。

1.6BeanFactory 和 FactoryBean

BeanFactory: 是IOC容器的顶级父类,并且提供了 getBean 方法,可以用于获取bean实例.在程序启动时根据配置信息产生各种类型的 bean,并添加到 IOC容器中.如果使用原生的BeanFactory创建Bean,它是延迟加载的方式创建bean.一般我们都使用其子接口 ApplicationContext,该接口默认是立刻加载bean FactoryBean:它是spring中的一个接口,用来创建复杂的bean,提供了一个getObject方法,该方法用来创建bean实例,并交给spring容器来管理.

1.7循环依赖

循环依赖是指在应用程序中,两个或多个Bean之间相互依赖,形成了循环链.例如,Bean A依赖于Bean B,而Bean B又依赖于Bean A,就形成了循环依赖.

循环依赖通常发生在对象之间相互协作的场景中,例如两个服务接口互相调用,或者两个数据访问层的对象相互依赖.当循环依赖发生时,会导致Bean无法正常初始化,从而导致应用程序启动失败或者出现一些预料之外的问题.

解决方案:

在Spring的DefaultSingletonBeanRegistry类中,可以找到3个Map

/** Cache of singleton objects: bean name to bean instance.*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
​
/** Cache of early singleton objects: bean name to bean instance.*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
​
/** Cache of singleton factories: bean name to ObjectFactory.*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

singletonObjects(一级缓存)俗称“单例池容器”,用于缓存创建并属性赋值完成的单例Bean.

earlySingletonObjects(二级缓存)映射Bean的早期引用,也就是说在这个Map里的Bean是半成品,它只是创建出来了,并没有完成属性初始化.使用二级缓存可以解决非AOP的循环依赖.

singletonFactories(三级缓存)映射创建Bean的原始工厂,可以从三级缓存中拿到对应的Bean创建工厂,再通过工厂获取Bean实例.ObjectFactory 是一个函数式接口,支持Lambda表达式:() ->getEarlyBeanReference(beanName, mbd, bean),这个Lambda表达式就是一个ObjectFactory,执行该Lambda表达式就会去执行getEarlyBeanReference方法,创建一个半成品的二级缓存代理对象.因此,三级缓存主要是用于解决AOP的循环依赖问题.

后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了.简单来说就是在创建一个Bean时,我们会先从三级缓存中获取Bean工厂;然后在利用Bean工厂获取Bean的半成品实例,并放入二级缓存中,同时还要将三级缓存中的对象进行移除;然后为二级缓存中的半成品Bean对象完成属性赋值,再放入一级缓存,同时再将二级缓存中的对象移除,这样才算完成一个Bean的注入.

对于没有被AOP切面代理的循环依赖问题,使用二级缓存的具体解决方案流程是:

1.实例化 A,此时 A 还未完成属性填充和初始化方法(@PostConstruct)的执行,A 只是一个半成品.

2.发现 A 需要注入 B 对象,但是一级、二级、三级缓存均未发现对象 B.

3.实例化 B,此时 B 还未完成属性填充和初始化方法(@PostConstruct)的执行,B 只是一个半成品.

4.发现 B 需要注入 A 对象,此时在一级缓存中未发现对象A,但是在二级缓存中发现了对象 A,从二级缓存中得到对象 A的半成品实例,将该半成品对象注入到对象 B 中.

5.对象 B 完成属性填充,执行初始化方法,此时对象B已经是一个成品,放入到一级缓存中,同时删除二级缓存中的对象 B.

6.对象A现在就可以从一级缓存中得到完整的对象B,此时再将对象B注入到对象 A 中.

7.对象A完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 A.

同理,如果是AOP切面代理的Bean发生了循环依赖,只是多了一个从三级缓存中获取Bean的代理对象的过程.

具体的代码实现,我们可以在发生循环依赖的Bean上使用@Lazy实现懒加载来解决.

当然也可以优化代码设计,避免不同业务之间业务层,持久层的相互引用,使用交叉引用作为替代.

二.AOP

2.1AOP简介

AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性.可用于权限认证、日志、事务处理等

2.2AOP中的几个概念

连接点(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行.连接点是在应用执行过程中能够插入切面的一个点.这个点可以是调用方法时、抛出异常时、甚至修改一个字段时.切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为.

通知(Advice):在AOP术语中,切面的工作被称为通知.

切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点.我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点.

切面(Aspect):切面是通知和切点的结合.通知和切点共同定义了切面的全部内容.在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect注解来实现.

目标对象(Target Object):被一个或者多个切面所通知的对象.它通常是一个代理对象.也有人把它叫做被通知对象.既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理对象.

2.3通知类型

前置通知(@Before):在目标方法被调用之前调用通知功能;

后置通知(@After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

返回通知(@AfterReturning ):在目标方法成功执行之后调用通知;

异常通知(@AfterThrowing):在目标方法抛出异常后调用通知;

环绕通知(@Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为.

2.4AOP底层原理

AOP的底层原理是使用动态代理,在不修改代码的前提下,对功能做增强.

实现AOP的方式主要有两种

1.使用JDK实现动态代理

我们让目标对象实现一个接口,再通过Proxy.newProxyInstance()方法获取一个该接口的实现类,这个方法主要有三个参数,分别是类加载器,目标对象与要获取的代理对象实现的公共接口,以及这个代理对象对原方法的重写实现增强

2.使用CGLIB实现动态代理

我们的目标对象必须首先是一个没有被final修饰的类,然后我们使用Enhancer.create()方法获取其增强的子类代理对象.Enhancer.create()方法主要核心参数是目标对象的Class对象,另一个是需要增强的回调方法.

2.5AOP的应用

Spring中的应用:

过滤器、拦截器、异常处理器、声明式事务

项目中的应用:

  • 网关使用全局过滤器对用户请求进行拦截,实现登录鉴权

  • 服务中的拦截器可以在执行业务代码之前将请求头中的当前操作用户ID存入到线程域中,在业务代码执行结束之后将线程域中的用户ID移除

  • 全局异常处理器对自定义业务异常进行统一处理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值