Java中的面向切面编程(AOP)

一、什么是AOP?

Aspect Oriented Programming ,即面向切面编程。

AOP是对面向对象编程的一个补充。它的目的是将复杂的需求分解为不同的切面,将散布在系统中的公共功能集中解决。它的实际含义是在运行时将代码切入到类的指定方法、指定位置上,将不同方法的同一个位置抽象为一个切面对象,并对该对象进行编程。

下面是AOP的一个示意图

二、AOP的优点和一些概念

1、AOP的优点

降低模块之间的耦合度使系统更容易扩展更好的代码复用非业务代码更加集中,不分散,便于统一管理业务代码更加简洁纯粹,不掺杂其他的代码的影响

2、AOP中出现的一些概念

切面:横切关注点,被模块化的抽象对象通知:切面对象完成的工作(非业务代码)目标:被通知的对象(即被横切的对象)代理:切面、通知、目标混合之后的对象连接点:通知要插入业务代码的具体位置(如Spring实现中的JoinPoint)切点:AOP通过切点定位到连接点

三、使用动态代理实现AOP

1、添加依赖

org.springframework

spring-aop

5.0.11.RELEASE

org.springframework

spring-aspects

5.0.11.RELEASE

2、创建一个计算器接口Cal,定义四个方法

动态代理的类的方法应当都由接口来实现,这样才方便使用动态代理对象执行方法

3、创建接口的实现类CalImpl

高耦合的写法,每次打印日志都要手动完成:

4、使用动态代理进行优化

上方代码中,日志信息和业务逻辑的耦合性很高,不利于代码的维护。使用AOP可以进行优化,我们可以使用动态代理实现AOP: 给业务代码找一个代理,打印日志信息的工作交给代理来做。这样的话业务代码就只需要关注自身业务即可。 (1)去掉手动输出的日志信息 (2).代理辅助类的编写和使用(动态代理的核心)

我们创建的并不是所谓的代理类,而是一个可以帮助我们返回代理对象的辅助类,这个辅助类有两个功能

接收委托对象并依次返回代理对象处理代理对象调用方法的过程 值得注意的是:

注意别忘了给委托对象赋值的那一步创建代理动态代理对象时传入的方法保证了代理类拥有原类的全部功能调用代理对象的方法时会自动调用invoke方法 (3)测试

四、使用Spring实现AOP

动态代理实现AOP比较复杂,不易理解。Spring框架对AOP进行了封装,使用Spring框架可以用面向对象的思想实现AOP。Spring框架中不需要创建辅助类,只需要创建一个切面对象,将所有的非业务代码在切面对象中完成即可(但实际上Spring框架底层依然会根据切面类和代理类来生成代理对象。)

1、添加依赖

org.springframework

spring-aop

5.0.11.RELEASE

org.springframework

spring-aspects

5.0.11.RELEASE

org.springframework

spring-context

5.0.11.RELEASE

2、创建一个计算器接口Cal,定义四个方法

当使用Spring实现时,这一步非必须!直接在实体类里面定义方法也可

3、定义实体类,实现方法

注意需要加上Component注解把他交给IoC

4、创建切面类

类定义处的两个注解

@Aspect表示该类是一个切面类@Component将该类的对象注入到IoC容器(切面类和实体类都需要加上这个注解)

方法处的注解

@Before表示方法执行的具体位置和时机是方法开始时

@After类似Before,不过位置是方法的最后

@AfterReturning在下文有作解释

@AfterThrowing在下文有作解释

5、在spring.xml中进行配置

context:component-scan指扫描com.pedro包中的所有类,如果该类同时添加了component注解,则将该类扫描IoC容器中。即IoC管理它的对象aop:aspectj-autoproxy让Spring容器结合切面类和目标类自动生成代理对象

6、使用

用代理对象调用方法就会自动执行它本身的方法和切面类中的非业务代码

为什么类名首字母要小写? 当使用注解配置bean时,默认id(别名)就是首字母改为小写的类名。若想修改,就在实体类的注解处加上自定义的名字即可。如@Component("test"),这样的话在getBean的时候就可以使用自定义的别名了,即xx.getBean("test")

7、两个特殊的After注解

(1)AfterReturning

用于在获取返回值后执行一段非业务代码

注:因为有两个参数,这里的value标签名被标出,而上面的before、after等注解只有一个参数,所以省略了value

结合上面的其他注解,会输出:

div方法的参数是[6,2]

div方法执行完毕

div方法的结果是3

(2)AfterThrowing

切面类的AfterThrowing注解,用于在抛出异常后执行一段非业务代码 结合上面的其他注解,会输出:

div方法的参数是[6,0]

div方法执行完毕

div方法抛出的异常:java.lang.ArithmeticException: / by zero

2021.4.3