AOP 的基本概念
- 切面(Aspect):
一个关注点的模块化,这个关注点可能会横切多个对象。
事务管理是 J2EE 应用中一个关于横切关注点的很好列子。
在 Spring AOP 中,切面可以使用基于模式或者基于 @Aspect 注解的方式来实现。 - 连接点(Joinpoint):
在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。
在 Spring AOP 中,一个连接点总是表示一个方法的执行。 - 切入点(Pointcut):
匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如:当执行某个特定名称的方法时)。
切入点表达式如何和连接点匹配是 AOP 的核心:Spring 缺省使用 Aspect 切入点语法。 - 引入(Introduction):
用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration)) Spring 允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,可以使用引入来使一个 bean 实现 isModified 接口,以便简化缓存机制。 - 目标对象(Target Object):
被一个或者多个切面所通知的对象,也被称为通知(advised)对象。
既然 Spring AOP 是通过运行时代理实现的。这个对象永远是一个被代理(proxied)对象。 - AOP 代理(AOP Proxy):
AOP 框架创建的对象,用来实现切面契约(例如,通知方法执行等等)。
在 Spring 中,AOP 代理可以是 JDK 动态代理或者 CGLIB 代理。 - 织入(Weaving):
把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用 AspectJ 编译器),类加载时和运行时完成。Spring 和其他纯 Java AOP 框架一样,在运行时完成织入。 - 通知(Advice):
在切面的某个特定的连接点上执行的动作。其中包括“around”、“before” 和 “after” 等不同类型的通知。
许多 AOP 框架(包括 Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
自定义Advisor
首先必须知道几个对象:Advice,PointCut,MethodMatcher,Advisor的关系
Advisor(顾问)由Advice(通知)与PointCut(切点)组成,Advice(通知)用于执行具体需要增强的逻辑,切点用于设置那些方法需要增强,而PointCut(切点)是通过MethodMatcher对象筛选出那些类与方法需要增强。
- 定义注解
package com.iot.cloud.springaopdemo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @version: 1.00.00
* @description:
* @copyright: Copyright (c) 2018 立林科技 All Rights Reserved
* @company: 厦门立林科技有限公司
* @author: hj
* @date: 2018-05-03 17:04
* @history:
*/
@Target(ElementType.METHOD)//这个注解是应用在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
/**
* 锁名称,支持{}格式
* @return
*/
String lockName();
}
- 定义advisor
package com.iot.cloud.springaopdemo.aspect;
import com.iot.cloud.springaopdemo.annotation.DistributeLock;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.stereotype.Component;
/**
* @version: 1.00.00
* @description: 自定义切面
* @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
* @company: 厦门立林科技有限公司
* @author: hj
* @date: 2022-07-27 13:31
*/
@Component
public class MyAdvisor implements PointcutAdvisor {
/**
* 定义切点
* @return
*/
@Override
public Pointcut getPointcut() {
// //自定义切入点表达式
// AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// pointcut.setExpression("execution(* com.iot.cloud.springaopdemo.service.AopService.*(..))");
// //基于method级别的注解
// AnnotationMatchingPointcut annotationMatchingPointcut = AnnotationMatchingPointcut.forMethodAnnotation(DistributeLock.class);
// //基于class级别的注解
// AnnotationMatchingPointcut pointcut1 = AnnotationMatchingPointcut.forClassAnnotation(DistributeLock.class);
return new MyPointCut();
}
/**
* 定义通知
* @return
*/
@Override
public Advice getAdvice() {
return new MyBeforeAdvise();
}
@Override
public boolean isPerInstance() {
return true;
}
}
- 定义切点
- 通过spring自带的AnnotationMatchingPointcut或AspectJExpressionPointcut定义
- 通过实现Pointcut进行个性化定义
package com.iot.cloud.springaopdemo.aspect;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
/**
* @version: 1.00.00
* @description: 自定义pointCut
* @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
* @company: 厦门立林科技有限公司
* @author: hj
* @date: 2022-07-27 13:45
*/
public class MyPointCut implements Pointcut {
/**
* 用于过滤类,先过滤类,如果类不满足,
* 那么就不会判断方法是否匹配
* @return
*/
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
/**
* 用于过滤方法,需要一个MyMethodMatcher用于方法逻辑判断
* @return
*/
@Override
public MethodMatcher getMethodMatcher() {
return new MyMethodMatcher();
}
}
定义切点方法过滤规则
package com.iot.cloud.springaopdemo.aspect;
import com.iot.cloud.springaopdemo.annotation.DistributeLock;
import org.springframework.aop.MethodMatcher;
import java.lang.reflect.Method;
/**
* @version: 1.00.00
* @description: 方法匹配
* @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
* @company: 厦门立林科技有限公司
* @author: hj
* @date: 2022-07-27 13:48
*/
public class MyMethodMatcher implements MethodMatcher {
@Override
public boolean matches(Method method, Class<?> targetClass) {
DistributeLock annotation = method.getAnnotation(DistributeLock.class);
//判断方法是否使用了MyAnnotation注解。
if (annotation != null) {
return true;
}
return false;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
}
- 定义通知
package com.iot.cloud.springaopdemo.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
/**
* @version: 1.00.00
* @description: 前置通知
* @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
* @company: 厦门立林科技有限公司
* @author: hj
* @date: 2022-07-27 13:53
*/
@Slf4j
public class MyAdvise implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
log.info("前置通知");
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("环绕前");
Object proceed = invocation.proceed();
log.info("环绕后");
return proceed;
}
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
log.info("后置通知");
}
public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
log.info("异常通知:" + e.getMessage());
}
}
注 :
MethodBeforeAdvice: 前置通知
AfterReturningAdvice: 后置返回通知
MethodInterceptor:环绕通知
ThrowsAdvice:异常通知
- 测试
package com.iot.cloud.springaopdemo.handle;
import com.iot.cloud.springaopdemo.annotation.DistributeLock;
import org.springframework.stereotype.Component;
/**
* @version: 1.00.00
* @description: aop处理
* @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
* @company: 厦门立林科技有限公司
* @author: hj
* @date: 2022-07-27 14:40
*/
@Component
public class AopHandle {
@DistributeLock(lockName = "123")
public int handle(String name) {
return 15;
}
}
package com.iot.cloud.springaopdemo.service.impl;
import com.iot.cloud.springaopdemo.handle.AopHandle;
import com.iot.cloud.springaopdemo.service.AopService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.*;
/**
* @Version: 1.0
* @Description:
* @copyright: Copyright (c) 2019 立林科技 All Rights Reserved
* @company 厦门立林科技有限公司
* @Author: hj
* @date: 2022-07-27 14:14
* @history:
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
class AopServiceImplTest {
@Autowired
private AopHandle aopHandle;
@Test
void test1() {
int test = aopHandle.handle("123");
log.info("test={}", test);
}
}
测试结果 :
异常通知: