赞
踩
/**
* 定义方法级别的注解
*/
@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,我被调用了...
这里需要注意两个点:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。