【每天学点Spring】Spring @Aspect学习

2022-09-18  本文已影响0人  伊丽莎白2015

【官方文档】

1. 依赖

我使用的是Spring Boot 2.7.0,引入了:

2. 简单介绍Aspect类

理论上来说,一个@Around注解,可以做完切面所有的定义。

@Around方法内:

具体如下: image.png

比如单独用@Pointcut + @Before来定义:

@Pointcut(value = "execution(* com.aspect.sample.cup.service.*.*(..))")
public void pointCut() {
}

@Before(value = ("pointCut()"))
public void before() {
    log.info("@Before");
}

既然已经有了@Around,再设计@Before@AfterReturning,... 这样的设计细粒度小,更方便。

执行的顺序:
正常执行:【Before】-->【目标方法】-->【AfterReturning】-->【After】
目标方法执行报错:【Before】-->【目标方法】-->【AfterThrowing】-->【After】

3. 自定义一个annotation类以及Service

本章节自定义annotation类,然后在切点上定义,目标是所有被该annotation声明的方法都被include进来。

3.1 自定义annotation类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserUpdate {
    FlushMode updateType() default FlushMode.ON_SAVE;
}

enum FlushMode类:

public enum FlushMode {
    ON_SAVE, IMMEDIATELY
}
3.2 Service类
@Service
public class UserServiceImpl implements UserService {
    @UserUpdate(updateType = FlushMode.IMMEDIATELY)
    public UserDomain update(UserRes userRes, boolean batch) {
        log.info("enter user service...");
        return new UserDomain(1, "test");
    }
}

4. Aspect类

4.1 声明Aspect类

@Aspect注解来定义切面类,@Component用来声明需要被Spring scan到。

@Aspect
@Component
public class UserUpdateAspect {
    //
}
4.2 定义一个切点方法
@Around(value = "@annotation(UserUpdate)")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
    log.info("enter around method...");
    return joinPoint.proceed();
}

通过Controller --> 调用上述的userService.save(userRes, batch),日志:

enter around method...
enter user service...

5. 在切点方法中拿到目标方法的信息

5.1 joinPoint.getSignature()

拿到目标方法userSave.save(userRes, batch)上的注解@UserUpdate中的值:

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
UserUpdate userUpdate = method.getAnnotation(UserUpdate.class);
log.info("anotation: {}", userUpdate.updateType());

打印:

anotation: IMMEDIATELY

5.2 joinPoint.getArgs()

拿到目标方法userSave.save(userRes, batch)参数的值:

for (Object obj: joinPoint.getArgs()) {
    log.info("joinPoint args: {}", obj);
}

打印:

joinPoint args: UserRes(id=1, name=test)
joinPoint args: false

5.3 joinPoint.getTarget().getClass()

取得目标class的类名:

String className = joinPoint.getTarget().getClass().getName();
log.info("class name: {}", className);
String classSimpleName = joinPoint.getTarget().getClass().getSimpleName();
log.info("class simple name: {}", classSimpleName);

class name: com.aspect.sample.service.impl.UserServiceImpl
class simple name: UserServiceImpl

5.4 取得执行的结果
Object returnObj = joinPoint.proceed();
log.info("return object: {}", returnObj);

也可以在@AfterReturnning中取得:

@AfterReturning(value="@annotation(UserUpdate)", returning="retVal")
public void afterReturning(Object retVal) {
    log.info("return object: {}", retVal);
}

打印:

return object: UserDomain(id=1, name=test)

6. 切点的定义

上述两种方式都可以定义:

对于比较复杂的切点,可以使用组合的方式:
如:@Around(value="表达式 || pointCut方法")的形式。

@Pointcut(value = "execution(* com.faj.aspect.sample.cup.service.*.*(..))")
public void pointCut() {
}

@Around(value = "@annotation(UserUpdate) || pointCut()")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
}

组合的条件可以有:

关于切点的表达式,更多的可以参考官网:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-pointcuts-examples

6.1 execution示例

参考:https://howtodoinjava.com/spring-aop/aspectj-pointcut-expressions/

No 表达式 描述
1 匹配com.abc.EmployeeManager类下所有的方法 execution(* com.abc.EmployeeManager.*(..))
2 匹配和这个aspect同一个包下的EmployeeManager类下所有的方法 execution(* EmployeeManager.*(..))
3 匹配同一个包下的EmployeeManager类的public方法 execution(public * EmployeeManager.*(..))
4 匹配同一个包下的EmployeeManager类的public方法并且返回类型为EmployeeDTO execution(public EmployeeDTO EmployeeManager.*(..))
5 匹配同一个包下的EmployeeManager类的所有public方法,返回类型为EmployeeDTO,并且第1个方法参数为EmployeeDTO execution(public EmployeeDTO EmployeeManager.*(EmployeeDTO, ..))
6 在5的基础上,需要加上条件:方法参数为EmployeeDTO,Integer execution(public EmployeeDTO EmployeeManager.*(EmployeeDTO, Integer))
6.2 match示例
No 表达式 描述
1 匹配在com.abc 包中的所有类的所有方法 within(com.howtodoinjava.*)
2 匹配在com.abc 包以及它的子包(sub-packages)下的所有类的所有方法 within(com.howtodoinjava..*)
3 匹配com.bbb.EmployeeManagerImpl这个类的所有方法 within(com.howtodoinjava.EmployeeManagerImpl)
4 匹配任何包下的EmployeeManagerImpl类的所有方法 within(EmployeeManagerImpl)
5 匹配任何包下的EmployeeManager接口的实现类EmployeeManagerImpl的所有方法(用+号表示实现类) within(EmployeeManagerImpl+)
6.3 bean示例
No 表达式 描述
1 匹配bean名字,以Manager结尾 bean(*Manager)

【参考】

上一篇下一篇

猜你喜欢

热点阅读