从动态代理到AOP
0x00前言
上文简单了解了下两种动态代理,JDK CGLib 咱就是说啥呢,确实CGLib比JDK
动态代理有点方便。。。
JDK动态代理的缺点就是被代理对象必须要实现一个接口,这种严苛的条件并不能满足日常开发的全部需求,并不是所有的类都必须实现接口
0x02进入正题AOP
AOP:面向切面编程(Aspect Oriented Programming)是面向对象编程的一种补充和完善
AOP提供"横向"的切面逻辑,将与多个对象有关的公共模块封装成一个可重用模块,并将其整合成Aspect(切面),减少了代码的重复降低了模块的耦合度更加有利于扩展。
功能:再不改变原有业务逻辑的前提下对业务进行增强
0x03相关概念与实现流程
- 连接点(JoinPoint):程序执行过程中某个特定的点,由于Sping只支持方法类型的连接点,所以总是表示一个方法的执行
- 横切关注点:下文统称切点单不是PointCut, 对原方法进行增强的方法
- 切面:包含切点的类叫切面类
- 切入点(PointCut):被代理类中的方法 被横切的方法
- 申明切入点(使用SpEL表达式匹配一个或多个)pointcut
- 申明切面类(在此之前将切面类交给spring管理)
- Aspect在切面类中进行通知(切点放在切入点前还是后)
0x04注解形式实现
<!--包扫描注解 自动装配-->
<context:component-scan base-package="com"></context:component-scan>
<!--开启aop注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 添加context aspects 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>
这里展示aop利用两种动态代理的实现
Apple 使用CGLib动态代理
Banana 使用JDK动态代理
//水果接口
public interface Fruit {
void sayWhoami();
}
//Banana 使用JDK动态代理 实现水果接口 并交给Spring管理
@Component
public class Banana implements Fruit {
@Override
public void sayWhoami() {
System.out.println("I am Banana!");
}
}
@Component
public class Apple {
//苹果使用CGLib动态代理
public void sayWhoami() {
System.out.println("I am Apple!");
}
}
//切面类
@Aspect
@Component
public class FruitHandler {
//SpEL表达是匹配切入点
@Pointcut("execution(* com.*.sayWhoami())")
public void eat(){}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss");
//前置通知
@Before("eat()")
public void startTime(){
Date date = new Date();
String format = simpleDateFormat.format(date);
System.out.println("开启事务"+format);
}
//后置通知
@After("eat()")
public void endTime(){
Date date = new Date();
String endformat = simpleDateFormat.format(date);
System.out.println("提交事务"+endformat);
}
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Apple apple = (Apple) applicationContext.getBean("apple");
apple.sayWhoami();
Fruit banana = (Fruit) applicationContext.getBean("banana");
banana.sayWhoami();
}
在两个对象调用sayWhoami处打断点 验证目的
0x05xml配置类实现
注解实现改写配置类:删除注解修改FruitHandler(删除声明切入点) 与 配置文件
public class FruitHandler {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss");
public void startTime(){
Date date = new Date();
String format = simpleDateFormat.format(date);
System.out.println("开启事务"+format);
}
public void endTime(){
Date date = new Date();
String endformat = simpleDateFormat.format(date);
System.out.println("提交事务"+endformat);
}
}
<bean id="apple" class="com.Apple"></bean>
<bean id="banana" class="com.Banana"></bean>
<bean id="eatHandler" class="com.FruitHandler"></bean>
<aop:config>
<!--切入点 -->
<aop:pointcut id="eat" expression="execution(* com.*.sayWhoami())"/>
<aop:aspect ref="eatHandler">
<aop:before method="startTime" pointcut-ref="eat" />
<aop:after method="endTime" pointcut-ref="eat" />
</aop:aspect>
</aop:config>
运行结果