/**
* 定义方法级别的注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBMasterAnno {
String value() default "on";
}
Advisor 必然包含两个重要元素,Pointcut 和 Advice、其中 Pointcut 使用来匹配某些类是否能够被我的这个 Advisor 增强,而实际增强逻辑是在 Advice 中实现,而最有名的 Advice 子类就是 MethodInterceptor。
实现代码如下,里面包含了这两个重要的元素 Pointcut + Advice
@Component
public class MyAdvisor implements PointcutAdvisor {
/**
* 匹配对象,专门用来匹配方法、类、参数是否需要被增强
*/
@Autowired
private MyDbMasterPointcut myDbMasterPointcut;
@Autowired
private DbMasterAdvice dbMasterAdvice;
@Override
public Pointcut getPointcut() {
return myDbMasterPointcut;
}
@Override
public Advice getAdvice() {
return dbMasterAdvice;
}
@Override
public boolean isPerInstance() {
return false;
}
}
Pointcut 接口作用是匹配和过滤作用的,那么这种匹配一般会有三种:类、方法、参数。类的匹配过程交给了 ClassFilter 类匹配器、方法和参数的交给了 MethodMatcher 方法匹配器,如下代码直接实现了这两个接口,并且精确到了参数级别的校验。
这个 MyDbMasterPointcut 主要实现了功能:拦截被 @DBMasterAnno 注解修饰的方法、并且参数还必须要等于 “小明” 该方法才可以被 Advisor 增强,否则不增强。
/**
* 这里不直接在这里实现 MethodMatcher,ClassFilter 接口也行的
*/
@Component
public class MyDbMasterPointcut implements Pointcut, MethodMatcher,ClassFilter {
/**
* 这里是核心匹配过程,可能是个非常复杂的匹配过程
* 这个只能匹配到方法级别,也就是只能判断这个方法是否被什么修饰之类的
*/
@Override
public boolean matches(Method method, Class<?> targetClass) {
/**
* 可以在这里拿到原始方法,判断这个方法上是否标注了注解才可以
* method 这个方法对象是接口上的,注意了,接口上没有任何的注解修饰,拿到死都拿不到
* 所以可以通过这个 AopUtils 工具类获取到实习类上的 method 对象
* 或者可以通过 targetClass 字节码文件获取到都可以,方法很多
*/
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
if (specificMethod.isAnnotationPresent(DBMasterAnno.class)) {
System.out.println("匹配到了有 @DBMaster 修饰的方法,可以对其进行拦截操作");
return true;
}
return false;
}
/**
* 这里是核心匹配过程,可能是个非常复杂的匹配过程
* 这个不仅可以匹配方法,还可以匹配参数级别
*/
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
System.out.println("-------> 方法匹配成功了,然后开始参数级别的 matches() ...");
// 先检验方法是否符合要求
if (method.getName().equalsIgnoreCase("crateOrder")) {
// 然后再检验参数是否符合要求
String name = (String) args[0];
if ("小明".equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
@Override
public boolean isRuntime() {
/**
* 这里返回 true 参数级别的校验方法才会执行
* 这里方法 false 下面就不会被执行,看源码就知道了
*/
return true;
}
@Override
public ClassFilter getClassFilter() {
/**
* 当前自己就是 ClassFilter 所以直接返回 this
* 但是由于我们的 @DBMaster 注解是对方法起作用的,所以这里就没必要对类进行校验操作了
* 或者没必要实现 ClassFilter 接口,直接在 getClassFilter() 方法上返回 ClassFilter.TRUE
*/
return this;
//return ClassFilter.TRUE;
}
@Override
public MethodMatcher getMethodMatcher() {
return this;
}
/**
* 重写 ClassFilter 方法 对类的匹配
* 但是由于我们的 @DBMaster 注解是对方法起作用的,所以这里就没必要对类进行校验操作了
* 或者没必要实现 ClassFilter 接口,直接在 getClassFilter() 方法上返回 ClassFilter.TRUE
*/
@Override
public boolean matches(Class<?> clazz) {
return true;
}
}
这里实现的是 Advice 子类 MethodInterceptor 接口,注意这里执行完拦截逻辑,记得往下传递,否则到这里就结束了,类似 SpringMVC 拦截器功能
@Component
public class DbMasterAdvice implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
System.out.println("DbMasterAdvice 被调用了,记得要火炬传递哦.....");
return invocation.proceed();
}
}
这里不用配置 @Aspect,因为我们不用 Spring 提供的 Advice 功能,都是用我们自定义的 Advice,可以更灵活的自己控制
@Configuration
@ComponentScan({"com.gwm.spring.pointcut"})
@EnableAspectJAutoProxy
public class AdviceConfig {
}
这里我们使用自定定义的注解 @DBMasterAnno 修饰 crateOrder() 方法,表示只有这个方法需要被 Advisor 增强。
public interface AopProxyOrderService {
void crateOrder(String name);
}
@Service
public class AopProxyOrderServiceImpl implements AopProxyOrderService {
/**
* 这里使用自己定义的注解
*/
@DBMasterAnno
@Override
public void crateOrder(String name) {
System.out.println("我是目标方法 invoke crateOrder method...");
}
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(JavAspect.class);
AopProxyOrderService bean = context.getBean(AopProxyOrderService.class);
bean.crateOrder("小明");
}
运行结果如下:
DbMasterAdvice 被调用了,记得要火炬传递哦.....
我是目标方法 crateOrder,我被调用了...
这里需要注意两个点: