浅谈对 IOC 和 AOP的认知

浅谈对 IOC 和 AOP的认知

Spring学了这么久,IOC 和 AOP你真的认识到位了吗?

IOC作为spring的核心中的核心,这东西基本是面试必问的;而AOP作为继面向对象编程又一编程思想,则是每个java程序员必须要掌握的;

IOC

IOC,全称又叫Inversion of Control即控制反转;2004年,Martin Fowler探讨了一个问题,既然IoC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理对象变为由IoC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection,DI)”;

好,以上是官方的描述和理解;之前笔者也看到一个比较有意思的解释:所谓控制反转,打个比方你要去炸敌人的碉堡,以前是你自己背着炸药包跑到敌人碉堡下面,点燃炸药;现在是你不用自己背着炸药到处跑了,你自己跑到敌人碉堡下,呼叫spring的ioc容器把炸药(对象及其依赖)扔向你;虽然这个比喻有些不恰当,但是也形象的说明了IOC容器的作用,将自己构建对象(携带炸药)的这一行为交给IOC容器(远程火力支援)来统一管理,以解决对象创建后,A类和B类间的耦合问题!

我们怎么来理解控制和反转呢?首先,我们需要知道它控制了什么?又在什么地方反转了?

  • 控制:其实IOC控制了创建对象的执行流程,Spring之前对象的创建大都是应用程序new一个出来的,Spring之后IOC控制了对象的创建流程;

  • 反转:反转的是对象依赖过程;Spring之前是自己new出来对象后添加对应的依赖,Spring之后是IOC容器查找和注入依赖,它反转了获取对象依赖的过程;即你不要来找我,我来找你;当IOC注入对象的时候,对应的依赖也一并注入了进来;

了解了这些后,你还需要了解IOC的底层实现原理:

  • 使用dom4j解析XML配置文件
  • 使用工厂模式初始化各种Bean工厂
  • 通过反射创建各种Bean

那么知道了这些后,你还有必要了解下:

  • Bean的初始化过程?

  • 以及IOC是如何解决循环依赖的?

  • 为啥要用上三级缓存?

  • IOC容器实际指的哪个集合类,为什么?(其实是CurrentHashMap,面试到IOC一般必问的!)

这里就不在深入了,推荐去大佬的博客下学习:chaser的博客,相信你能学到很多;

好,知道了这些后我们再来认识下AOP;

AOP

Aop,全称“Aspect Oriented Programming”意为面向切面编程;我们来看下官网的描述Spring Aop

“Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns (such as transaction management) that cut across multiple types and objects. (Such concerns are often termed “crosscutting” concerns in AOP literature.)

One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP (meaning you do not need to use AOP if you don’t want to), AOP complements Spring IoC to provide a very capable middleware solution.”

面向方面编程(AOP)是面向对象编程(OOP)的补充,它提供了另一种关于程序结构的思考方式。OOP中模块化的关键单元是类,而在AOP中,模块化单元是切面。切面支持跨多个类型和对象的关注点(如事务管理)的模块化。(在AOP文献中,此类关注点通常被称为“横切”关注点。)

Spring的关键组件之一是AOP框架。虽然springioc容器不依赖于AOP(这意味着如果您不想使用AOP,就不需要使用AOP),AOP补充了springioc,以提供一个功能非常强大的中间件解决方案。

是的,通过官方文档的阅读,我们知道了AOP框架是IOC的一种增强,是一个强大的中间件。(学习Spring的过程中,你将会发现各种给中间件可以和Spring结合使用;这也就印证了一句话:在spring中没有啥是加一层解决不了的,这也侧面说明了Spring这个框架的强大之处,高扩展性和低耦合);

概念

开始深入理解AOP之前,我们看下AOP中各种术语概念:

连接点(JoinPoint):增强执行的位置(增加代码的位置),Spring只支持方法作为连接点;

切点(PointCut):具体的连接点,一般可能通过一个表达式来描述;

增强(Advice):也称为消息,指的是增加的额外的代码,Spring中,增强除了包含代码外,还包含位置信息;

四种增强:MethodBeforeAdvice(前置增强)、MethodInterceptor(环绕增强)、ThrowsAdvice(异常增强,也叫消息增强)、AfterReturingAdvice(返回值增强)

引介(Introduction):特殊的增强,动态为类增加方法

织入(Weaving):将增强加入到目标类的过程

织入分为三种时期:编译期(AspectJ)、类加载期、运行期(jdk动态代理,实现InvocationHandler接口;CGLIB)

AOP实现的关键,在于AOP框架自动创建的AOP代理,AOP代理主要分为 静态代理动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表;

AspectJ静态代理和 Spring AOP动态代理

  • AspectJ:关于AspectJ的底层实现,是AspectJ内置了特殊的编译器,在代码编译期间可以在不改变代码的前提下改变写入代码(在方法执行前和方法执行后加入自己的代码);关于AspectJ的具体实现可以参考博客:AspectJ入门

  • Spring AOP:spring aop实现动态代理主要是两种方式。一种是JDK动态代理,一种是CGLIB动态代理;我们知道使用代理模式,有个很重要的类是Proxy;那么在Spring中,还有一个很重要的接口InvocationHandler,实现了InvocationHandler接口的类通过JDK反射来实现动态代理(重写invoke方法),而没有实现此接口的类,则都默认使用CGLIB来实现动态代理(这一点可以扒源码了解);

实际上动态代理的本质上是:在调用代理类的方法时,动态对代理类的方法进行拦截,以此达到对方法的改造和增强!

了解了基本概念和本质后,我们来模仿CGLIB的实现,写个小例子测试一下:

代码

新增Shopping类:

@Component("shopping")
public class Shopping {
    public void shop() {
        System.out.println("到超市挑东西");
        System.out.println("去收银台付钱");
    }
}

新增切点类CutPointer:

@Aspect
@Component
public class CutPointer {
    
    @Before("execution(* com.isky.visual.Shopping.shop())")
    public void before() {
        System.out.println("选择目的地超市");
        System.out.println("选择交通工具前往");
    }

    @After("execution(* com.isky.visual.Shopping.shop())")
    public void after() {
        System.out.println("选择交通工具回家");
    }
}

测试类中新增测试接口:

@Autowired
private Shopping shopping;

@GetMapping("/test")
public void testCglib(){
    shopping.shop();
}

然后查看运行结果如下,说明我们成功在shop方法前后,加入了我们自己的执行代码;

UoAKDP.png

最后关于AOP在Spring中典型的应用有:日志,性能监控,权限控制,缓存优化,事务管理

更多参考

好了,aop的东西真的太多了,就上面的应用场景每个都能怼到你怀疑人生,这里就不再发散了,让我们站在牛牛的肩膀上学习吧;关于Spring AOP的更多理解请参考博客:

小结:作为Spring的两个核心,东西确实有很多;这里只是梳理出了一个大概的认知流程,具体更深层次的认知还得去扒源码,去理解去看。哈哈,你的头发还好吗?