AOP实现
- AOP(Aspect Oriented Programming 面向切面编程),是基于动态代理的实现代码复用的机制,是对传统OOP(Object Oriented Programming 面向对象编程)的补充。
新建一个计算器ICalculatorService接口
public interface ICalculatorService {
int mul(int a, int b);
int div(int a, int b);
}
实现这个接口
@Service
public class CalculatorService implements ICalculatorService {
@Override
public int mul(int a, int b) {
int result = a*b;
return result;
}
@Override
public int div(int a, int b) {
int result = a/b;
return result;
}
}
- 如果想要在接口实现类中的方法前后增加代码,
- 直接增加(过于繁琐,如果有很多方法和类工作量巨大)
- 动态代理工厂(JDK动态代理,cglib动态代理等),为每个需要增强的类编写一个工厂(如果多个类仍然需要写大量的代理工厂类)
- 借助Spring框架和注解和切入点表达式自动的为目标对象生成动态代理对象(这种方法就是对动态代理的封装)
Spring框架中提供了XML文件配置,和注解两种方式实现AOP
这里主要介绍一下Aspectj注解实现AOP
- 准备导入Spring框架的核心jar包(五个),然后两个支持AOP的jar包(两个)。一共需要导入7个jar包其他的jar包暂时可以不导入。
- 配置XML文件(application.xml文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描包中的注解把有@Conponment @Service @Repository 注解的类
为其创建对象并放入Spring容器中-->
<context:component-scan base-package="com.jd.calculater"></context:component-scan>
<!--开启自动代理,扫描响应的注解,并产生代理对象。在执行过程中详细讲解
proxy-target-class属性的值false为默认值使用JDK动态代理
true则设置为cglib代理
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
</beans>
注意: 即使proxy-target-class设置为false,如果目标类没有声明接口,则Spring将自动使用CGLib生成代理对象
- 编写CalculatorAspect类
@Aspect
@Component
public class CalculatorAspect {
//前置增强,括号内为切入点表达式:用于指定执行哪些类中的哪些方法时触发增强方法
@Before("execution(public int mul(int,int))")
public void before(JoinPoint joinPoint) {
System.out.println("前置增强");
//获取目标类对象
Object target = joinPoint.getTarget();
//获取目标类需要被增强的方法
Object[] args = joinPoint.getArgs();
//获取被增强的方法名
String name = joinPoint.getSignature().getName();
System.out.println(target.getClass().getName() + ":The " + name + " method begins.");
System.out.println(target.getClass().getName() + ":Parameters of the " + name + " method: [" + args[0] + ","
+ args[1] + "]");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
ICalculatorService calculatorService = context.getBean(ICalculatorService.class);
//把代理对象打印出来
System.out.println(calculatorService.getClass());
int result = calculatorService.mul(1, 1);
System.out.println("结果为:" + result);
}
}
结果
class com.sun.proxy.$Proxy8
前置增强
com.jd.calculater.CalculatorService:The mul method begins.
com.jd.calculater.CalculatorService:Parameters of the mul method: [1,1]
结果为:1
- 如果把XML配置文件中的proxy-target-class设置为true或者目标类没有声名接口则会使用cglib代理
如果打印出的动态代理对象也可以看出使用了cglib代理
class com.jd.calculater.CalculatorService$$EnhancerBySpringCGLIB$$e0cef19
前置增强
com.jd.calculater.CalculatorService:The mul method begins.
com.jd.calculater.CalculatorService:Parameters of the mul method: [1,1]
结果为:1
执行过程
-
XML配置文件添加autoproxy标签
-
Spring寻找带有@Aspect的类,例如上面的CalculatorAspect类
-
扫描带有@Aspect的类中的所有方法
-
寻找该类中带有 @Before , @After , @AfterReturning , @Around , @AfterThrowing 注解的方法
得到该注解
根据不同的注解判断是对应的是哪一种增强方式 -
获取表达式,例如上面的(execution(int mul(int a, int b))这个表达式就是指定需要被增强的方法
-
检查Spring能扫描到的所有类,找到表达式匹配的方法对应的类
-
为该类创建动态对象