前言
  接触过Spring的都知道,aop是其中重要的特性之一。笔者在开发做项目中,aop更多地是要和注解搭配:在某些方法上加上自定义注解,然后要对这些方法进行增强(很少用execution指定,哪些包下的哪些方法要增强)。那这时就要引出@annotation、@target、@within了。我们一一讲解。
 
@annotation
方法上是否有指定注解;子类调用不重写的方法会被aop拦截,调用重写的方法看是否加了指定注解。
首先引入依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.7.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.7.4</version>
    <scope>test</scope>
</dependency>
自定义一个注解:
import java.lang.annotation.Target;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Outer {
    int limit() default 0;
}
目标类:
import org.springframework.stereotype.Component;
@Component
public class Target {
    @Outer(limit = 8)
    public void invoke() {
        System.out.println("执行Target的方法");
    }
    
}
@Component
public class SonTarget extends Target {
		
}
切面类:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class MyAspect {
    @Around("@annotation(com.gs.spring_boot_demo.aop.Outer)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Method method = ((MethodSignature)point.getSignature())
                        .getMethod();
        Outer outer = method.getAnnotation(Outer.class);
        System.out.println("aop前置:" + outer.limit());
        return point.proceed();
    }
}
编写测试类:
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.jupiter.api.Test;
@SpringBootTest
public class AopTest {
    @Resource
    private Target target;
    @Autowired
    private SonTarget sonTarget;
    @Test
    public void aop() {
        target.invoke();
        System.out.println("---");
        sonTarget.invoke();
    }
    
}
运行aop方法,打印结果:

 
   把子类SonTarget修改一下,
import org.springframework.stereotype.Component;
@Component
public class SonTarget extends Target {
	
    public void invoke() {
        System.out.println("子类执行Target的方法");
    }
}
再次运行测试类,这时子类的invoke()方法不会被拦截了:

当然,如果SonTarget的invoke()方法上加上@Outer,那就能被aop拦截了。
@target
调用方法的对象,所属的类上是否有指定注解;注解被@Inherited修饰,子类调用会生效;无@Inherited,看子类上有无该注解。
自定义注解不动,目标类修改为:
import org.springframework.stereotype.Component;
@Component
@Outer(limit = 8)
public class Target {
	
    public void invoke() {
        System.out.println("执行Target的方法");
    }
}
import org.springframework.stereotype.Component;
@Component
public class SonTarget extends Target {
	
    public void invoke() {
        System.out.println("子类执行Target的方法");
    }
}
切面类修改为:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class MyAspect {
    /**
     * 要注意一下,@target很硬霸:所有的bean都会被动态代理(不管类上有没有加自定
     * 义注解),所以要约束为:本项目下的包下
     * 不然测试用例运行时会报错:依赖中有些类是final的,被动态代理会报错
     */
	@Around("@target(com.gs.spring_boot_demo.aop.Outer) && 
	        within(com.gs.spring_boot_demo..*)")
	public void around(ProceedingJoinPoint point) throws Throwable {
	    Method method = ((MethodSignature)point.getSignature())
	                    .getMethod();
	    Outer outer = method.getDeclaringClass().getAnnotation(
	                  Outer.class);
	
	    System.out.println("aop前置:" + outer.limit());
	    point.proceed();
	}
}
测试类不动,运行:

SonTarget的invoke()没有被拦截,想要被拦截,就在SonTarget类上添加@Outer;或者自定义注解上增加@Inherited(表明父类加上该注解后,子类能够继承):
import java.lang.annotation.Target;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Outer {
    int limit() default 0;
}
@within
方法所属的类上,是否有指定注解;注解没有被@Inherited修饰,子类调用不重写的方法会被拦截,调用重写的方法看子类上是否有注解;注解被@Inherited修饰,子类调用方法都会被拦截,不管是否重写
  自定义注解改一下,就把修饰它的@Inherited去掉;
   目标类:
import org.springframework.stereotype.Component;
@Component
@Outer(limit = 8)
public class Target {
	
    public void invoke() {
        System.out.println("执行Target的方法");
    }
}
import org.springframework.stereotype.Component;
@Component
public class SonTarget extends Target {
	
}
切面类修改为:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class MyAspect {
	
    @Around("@within(com.gs.spring_boot_demo.aop.Outer)")
    public void around(ProceedingJoinPoint point) throws Throwable {
        Method method = ((MethodSignature)point.getSignature())
                        .getMethod();
        Outer outer = method.getDeclaringClass().getAnnotation(
                      Outer.class);
        System.out.println("aop前置:" + outer.limit());
        point.proceed();
    }
}
测试类不动,运行:

子类的方法能被拦截;我们把子类的方法重写一下:
import org.springframework.stereotype.Component;
@Component
public class SonTarget extends Target {
	
    public void invoke() {
        System.out.println("子类执行Target的方法");
    }
}
再次运行测试类,打印出结果:

子类的方法没有被拦截,想要被拦截,SonTarget类上加上@Outer。
我们再试一下自定义注解被@Inherited修饰的情况。@Outer注解加上@Inherited,然后Target不动,SonTarget也不动(重写了invoke()方法,类上也没有@Outer),运行测试类:

SonTaget改一下,不重写invoke()方法,运行测试类:




















