AOP解决的问题:是面向切面编程,将核心业务代码与外围业务(日志记录、权限校验、异常处理、事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一。
AOP的实现有Aspectj(静态代理)、JDK动态代理、CGLIB动态代理,SpringAOP不是一种新的AOP实现,其底层采用的是JDK/CGLIB动态代理。
AspectJ是一个面向切面的框架,是最首创的AOP技术,用来提供全面的AOP方案,Aspectj是一种静态代理,而SpringAOP是动态代理。但Aspectj的一套定义AOP的API非常好,直观易用。所以Spring引入了Aspectj.
SpringAOP 不是一个完备的AOP 方案,它是Spring提供的一个标准易用的aop框架,依托Spring的IOC容器,提供了极强的AOP扩展增强能力,对项目开发提供了极大地便利。
- Spring中,有两种方式可以实现动态代理--JDK动态代理和CGLIB动态代理。
- SpringAOP不是一种新的AOP实现,使用了 AspectJ库的注解并且使用 AspectJ库对切点表达式进行解析和匹配,但AOP运行时并不使用 AspectJ的编译器和织入,仍然是使用纯粹的SpringAOP实现,代理部分仍然是使用的JDK动态代理和Cglib代理。
- SpringAOP配置方式核心是Advisor,可以自定义Advisor,也可以通过AspectJ间接定义Advisor
- SpringAOP的实现遵循了AOP联盟规范,AOP联盟顶级API接口贯穿了整个AOP过程
1.静态代理
静态代理就是将一个实现类,使用委托代理的方式让其去执行。比如我们需要买东西,可让跑腿代理我们去购买,至于跑腿怎么买,在哪买我们不用关心,我们只需要接收购买的东西就行。
/**
* 定义抽象的类,代表抽象的角色
*/
public interface Customer {
void shop(String shopName);
}
/**
* 实现抽象的业务,小明需要买东西
*/
public class XiaoMingCustomer implements Customer {
@Override
public void shop(String shopName) {
System.out.println("小明需要买" + shopName);
}
}
/**
* 代理的角色,跑腿
*/
public class Proxy implements Customer {
private Customer customer;
public Proxy(Customer customer) {
this.customer = customer;
}
@Override
public void shop(String shopName) {
System.out.println("===调用前====");
this.customer.shop(shopName);
System.out.println("===调用后====购买的东西" + shopName);
}
}
public static void main(String[] args) {
Customer customer = new XiaoMingCustomer();
Proxy proxy = new Proxy(customer);
proxy.shop("盐");
}
2.JDK动态代理
jdk动态代理通过实现InvocationHandler接口和实现类进行绑定,通过 Proxy.newProxyInstance得到代理类的实例。
Proxy.newProxyInstance(iCustomerInstance.getClass().getClassLoader(), iCustomerInstance.getClass().getInterfaces(), this);
新增一个抽象类
/**
* 定义抽象的类,代表抽象的角色
*/
public interface ICustomer {
void shop(String shopName);
}
创建一个实现类,实现业务
/**
* 实现抽象的业务,小明需要买东西
*/
public class CustomerImpl implements ICustomer {
@Override
public void shop(String shopName) {
System.out.println("小明需要买" + shopName);
}
}
将代理类和实现类进行绑定
/**
* Customer的代理类
*/
public class CustomerInvocationHandler implements InvocationHandler {
ICustomer iCustomerInstance;
public Object bind(ICustomer iCustomerInstance) {
this.iCustomerInstance = iCustomerInstance;
return Proxy.newProxyInstance(iCustomerInstance.getClass().getClassLoader(), iCustomerInstance.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----调用前处理");
method.invoke(this.iCustomerInstance, args);
System.out.println("----调用后处理");
return null;
}
public static void main(String[] args) {
ICustomer iCustomer = new CustomerImpl();
CustomerInvocationHandler customInvocationHandler = new CustomerInvocationHandler();
ICustomer bindProxy = (ICustomer) customInvocationHandler.bind(iCustomer);
bindProxy.shop("盐");
}
}
代理类增强,使用反射机制
public class CustomerEnhanceInvocationHandler implements InvocationHandler {
private Object implClassInstance;
public Object bind(Class<?> implClass) {
try {
implClassInstance = implClass.newInstance();
} catch (Exception e) {
}
return Proxy.newProxyInstance(implClassInstance.getClass().getClassLoader(), implClassInstance.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----代理增强调用前处理");
method.invoke(this.implClassInstance, args);
System.out.println("----代理增强调用后处理");
return null;
}
public static void main(String[] args) {
CustomerEnhanceInvocationHandler customInvocationHandler = new CustomerEnhanceInvocationHandler();
ICustomer bindProxy = (ICustomer) customInvocationHandler.bind(CustomerImpl.class);
bindProxy.shop("盐");
}
}
3.CGLIB动态代理
通过Enhancer.create得到代理类的实例,通过实现MethodInterceptor接口和业务类进行绑定。
新建一个业务类
public class Customer {
public void shop(String shopName) {
System.out.println("小明需要买" + shopName);
}
}
新建一个实现MethodInterceptor接口类,对代理和业务类进行绑定。
public class CustomerMethodIntercepto implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----调用前处理");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("----调用后处理");
return obj;
}
public static void main(String[] args) {
Enhancer en = new Enhancer();
en.setSuperclass(Customer.class);
en.setCallback(new CustomerMethodIntercepto());
Customer customer = (Customer) en.create();
customer.shop("盐");
}
}
4.Spring AOP实现方式:
1.直接配置Advisor(实现接口)
实现Advisor接口,定义拦截器和拦截规则(切点)。需要定义三个类,一个Advisor的实现类,一个Advice实现类(拦截器),一个aop适配过滤器(这里使用的Advisor为派生的PointcutAdvisor ,需要定义PointCut切点)
2.间接配置Advisor(常用的注解方式)
使用Aspectj jar包提供的注解定义AOP配置,由Spring解析配置生成Advisor。
- 前置通知:在我们执行目标方法之前运行(@Before,参数)
- 后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
- 返回通知:在我们的目标方法正常返回值后运行(@AfterReturning
- 异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
-
环绕通知:动态代理, 需要手动执行procced()方法(其实就是执行我们的目标方法执行之前相当于前置通知,执行之后就相当于我们后置通知(@Around
4.1.Spring AOP支持的AspectJ切入点指示符
在spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常。这些指示符Spring AOP可能会在以后进行扩展。
4.2. 通知参数
1) JoinPoint:提供访问当前被通知方法的目标对象、代理对象、方法参数等数据:
getSignature() //获取修饰符+ 包名+组件名(类名) +方法名
getSignature().getName() //获取方法名
getSignature().getDeclaringTypeName()//获取包名+组件名(类名)
2)ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法:
3) JoinPoint.StaticPart:提供访问连接点的静态部分,如被通知方法签名、连接点类型等:
4.3 实现方式
新建一个注解,切入点使用注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLogAnnotation {
String name();
}
新建一个测试业务类
@Service
public class CustomService {
@Autowired
ApplicationContext applicationContext;
public void passByIoc() {
applicationContext.getBean(CustomService.class).printMessage("passByIoc调用");
}
/**
* 使用this方法调用printMessage不会被AOP拦截
*
* @param args
*/
public void passByThis(Integer args) {
this.printMessage("passByThis调用");
}
@MyLogAnnotation(name = "this is MyLogAnnotation printMessage")
public String printMessage(String message) {
// System.out.println("printMessage执行了;message=" + message);
return "printMessage return!";
}
}
新建一个切面类
@Aspect
@Component
public class AopAspect {
// @Before(value = "execution(* com.lx.lxbootaop.aopimpl.components.ExpressionComponent.passByIoc())")
public void doBefore(JoinPoint joinPoint) {
System.out.println("AOP拦截到==passByIoc:" + joinPoint.getSignature());
}
// @Before(value = "execution(* com.lx.lxbootaop.aopimpl.components.ExpressionComponent..*(java.lang.Integer))")
public void doBeforeArgs(JoinPoint joinPoint) {
System.out.println("aop==passByThis:" + joinPoint.getSignature());
}
@Pointcut("execution(* com.lx.lxbootaop.aopimpl.service.CustomService.printMessage(*))")
public void pointPrintMessage() {
}
/**
* 执行之前
*
* @param joinPoint
*/
// @Before(value = "execution(* com.lx.lxbootaop.dynamicproxy.components.ExpressionComponent.printMessage(*))")
@Before("pointPrintMessage()")
public void before(JoinPoint joinPoint) {
String name = joinPoint.getSignature().toString();
String args = Arrays.stream(joinPoint.getArgs()).map(row -> row.toString()).collect(Collectors.joining(","));
System.out.println("+++AOP 之前:" + name + "++++args:" + args);
}
/**
* 执行返回之前
*
* @param joinPoint
* @param pringMessageResult
*/
@AfterReturning(value = "pointPrintMessage()", returning = "pringMessageResult")
public void afterReturning(JoinPoint joinPoint, Object pringMessageResult) {
String name = joinPoint.getSignature().toString();
String args = Arrays.stream(joinPoint.getArgs()).map(row -> row.toString()).collect(Collectors.joining(","));
System.out.println("---AOP返回之前:" + name + "---args=" + args + "result=" + pringMessageResult);
}
/**
* 执行返回之后
*/
@After(value = "pointPrintMessage()")
public void aferReturning(JoinPoint joinPoint) {
String name = joinPoint.getSignature().toString();
System.out.println("---AOP返回之后:" + name);
}
/**
* 执行异常返回之前
*
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = "pointPrintMessage()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
String name = joinPoint.getSignature().toString();
System.out.println("---AOP返回异常:" + name + "----Exception=" + exception.getMessage());
}
@Around(value = "pointPrintMessage()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---AOP环绕之前:" + joinPoint.getSignature());
Object result = joinPoint.proceed(joinPoint.getArgs());
System.out.println("---AOP环绕之后:" + joinPoint.getSignature() + "+++result=" + result);
return result;
}
@Pointcut("@annotation(com.lx.lxbootaop.aopimpl.MyLogAnnotation)")
public void pointAnnotation() {
}
/**
* 注解形式
*/
@Before("pointAnnotation() && @annotation(myLogAnnotation)")
public void annotationBefore(JoinPoint joinPoint, MyLogAnnotation myLogAnnotation) {
String args = Arrays.stream(joinPoint.getArgs()).map(row -> row.toString()).collect(Collectors.joining(","));
System.out.println("&&&AOP拦截到注解 Before&&& args=" + args + ";name=" + myLogAnnotation.name());
}
}
参考:
SpringAOP学习--SpringAOP简介及原理_程序源程序的博客-CSDN博客_springaop
Spring AOP详细介绍_Trycol的博客-CSDN博客