利用Cglib实现AOP

前文讲了, 可以利用Spring, Guice等框架提供的容器实现AOP, 如果想绕过容器, 直接注入Class,

可以利用Cglib为对象加上动态代理,实现代码切入, 但是每次调用比较繁琐,

因此我们还需要给他加了一层语法糖, 使之更易用.

Advice

Spring带了一堆Advice, 我们只模拟实现环绕Advice, 以及增加了一个Clear切入的注解, 下面看具体实现.

 1 /**
 2  * 环绕Advie
 3  *
 4  * 可以加在类上, 或者方法上.
 5  * 加在类上的话, 类中所有无@Clear注解的方法都会被切入
 6  *
 7  *     @Before({CustomInterceptor.class, B.class})
 8  *     @Before(CustomInterceptor.class)
 9  */
10 @Inherited
11 @Retention(RetentionPolicy.RUNTIME)
12 @Target({ElementType.TYPE, ElementType.METHOD})
13 public @interface Before {
14     Class<? extends Interceptor>[] value();
15 }
 1 /**
 2  * 清除Advice
 3  *
 4  * 可以清除方法上的指定Interceptor, 若不指定, 则清除所有切入.
 5  *
 6  *     @Clear 清除所有
 7  *     @Clear(CustomInterceptor.class) 清除CustomInterceptor
 8  */
 9 @Inherited
10 @Retention(RetentionPolicy.RUNTIME)
11 @Target({ElementType.TYPE, ElementType.METHOD})
12 public @interface Clear {
13     Class<? extends Interceptor>[] value() default {};
14 }

语法糖

直接调用Cglib做切入, 需要setSuperClass, setCallback等等.

1   Enhancer enhancer = new Enhancer();
2   enhancer.setSuperclass(AopDemo.class);
3   enhancer.setCallback(new MethodInterceptorImpl());
4  
5   AopDemo demo = (AopDemo) enhancer.create();

我们需要对Enhancer以及Callback进行封装, 减少复杂度

  1 import java.util.concurrent.ConcurrentHashMap;
  2 
  3 /**
  4  * cglib中Enhancer的语法糖, 让注入更简单点
  5  */
  6 public class Enhancer {
  7     
  8     private static final ConcurrentHashMap<String, Object> singleton = new ConcurrentHashMap<String, Object>();
  9     
 10     private Enhancer(){}
 11 
 12     public static <T> T enhance(Class<T> targetClass) {
 13         return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback());
 14     }
 15     
 16     public static <T> T enhance(Class<T> targetClass, Interceptor... injectInters) {
 17         return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback(injectInters));
 18     }
 19 
 20     public static <T> T getTarget(String singletonKey) {
 21         return (T)singleton.get(singletonKey);
 22     }
 23     
 24     public static <T> T enhance(String singletonKey, Class<T> targetClass) {
 25         Object target = singleton.get(singletonKey);
 26         if (target == null) {
 27             target = enhance(targetClass);
 28             singleton.put(singletonKey, target);
 29         }
 30         return (T)target;
 31     }
 32     
 33     public static <T> T enhance(String singletonKey, Class<T> targetClass, Interceptor... injectInters) {
 34         Object target = singleton.get(singletonKey);
 35         if (target == null) {
 36             target = enhance(targetClass, injectInters);
 37             singleton.put(singletonKey, target);
 38         }
 39         return (T)target;
 40     }
 41     public static <T> T enhance(Object target) {
 42         return (T)net.sf.cglib.proxy.Enhancer.create(target.getClass(), new Callback(target));
 43     }
 44     
 45     public static <T> T enhance(Object target, Interceptor... injectInters) {
 46         return (T)net.sf.cglib.proxy.Enhancer.create(target.getClass(), new Callback(target, injectInters));
 47     }
 48     public static <T> T enhance(String singletonKey, Object target) {
 49         Object result = singleton.get(singletonKey);
 50         if (result == null) {
 51             result = enhance(target);
 52             singleton.put(singletonKey, result);
 53         }
 54         return (T)result;
 55     }
 56     
 57     public static <T> T enhance(String singletonKey, Object target, Interceptor... injectInters) {
 58         Object result = singleton.get(singletonKey);
 59         if (result == null) {
 60             result = enhance(target, injectInters);
 61             singleton.put(singletonKey, result);
 62         }
 63         return (T)result;
 64     }
 65     
 66 }
 67 
 68 
 69 import net.sf.cglib.proxy.MethodInterceptor;
 70 import net.sf.cglib.proxy.MethodProxy;
 71 
 72 import java.lang.reflect.Method;
 73 import java.util.HashSet;
 74 import java.util.Set;
 75 
 76 /**
 77  * Callback.
 78  */
 79 class Callback implements MethodInterceptor {
 80     
 81     private Object injectTarget = null;
 82     private final Interceptor[] injectInters;
 83     
 84     private static final Set<String> excludedMethodName = buildExcludedMethodName();
 85     private static final InterceptorManager interMan = InterceptorManager.me();
 86 
 87     public Callback() {
 88         this.injectInters = InterceptorManager.NULL_INTERS;
 89     }
 90     
 91     public Callback(Interceptor... injectInters) {
 92         checkInjectInterceptors(injectInters);
 93         this.injectInters = injectInters;
 94     }
 95     
 96     public Callback(Object injectTarget, Interceptor... injectInters) {
 97         if (injectTarget == null) {
 98             throw new IllegalArgumentException("injectTarget can not be null.");
 99         }
100         checkInjectInterceptors(injectInters);
101         this.injectTarget = injectTarget;
102         this.injectInters = injectInters;
103     }
104     
105     private void checkInjectInterceptors(Interceptor... injectInters) {
106         if (injectInters == null) {
107             throw new IllegalArgumentException("injectInters can not be null.");
108         }
109         for (Interceptor inter : injectInters) {
110             if (inter == null) {
111                 throw new IllegalArgumentException("interceptor in injectInters can not be null.");
112             }
113         }
114     }
115     
116     public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
117         if (excludedMethodName.contains(method.getName())) {
118             // if (method.getName().equals("finalize"))
119             //     return methodProxy.invokeSuper(target, args);
120             // return this.injectTarget != null ? methodProxy.invoke(this.injectTarget, args) : methodProxy.invokeSuper(target, args);
121             
122             // 保留上面注释部分,此处为优化
123             if (this.injectTarget == null || method.getName().equals("finalize")) {
124                 return methodProxy.invokeSuper(target, args);
125             } else {
126                 return methodProxy.invoke(this.injectTarget, args);
127             }
128         }
129         
130         if (this.injectTarget != null) {
131             target = this.injectTarget;
132             Interceptor[] finalInters = interMan.buildServiceMethodInterceptor(injectInters, target.getClass(), method);
133             Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
134             invocation.useInjectTarget = true;
135             invocation.invoke();
136             return invocation.getReturnValue();
137         }
138         else {
139             Class<?> targetClass = target.getClass();
140             if (targetClass.getName().indexOf("$$EnhancerByCGLIB") != -1) {
141                 targetClass = targetClass.getSuperclass();
142             }
143             Interceptor[] finalInters = interMan.buildServiceMethodInterceptor(injectInters, targetClass, method);
144             Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
145             invocation.useInjectTarget = false;
146             invocation.invoke();
147             return invocation.getReturnValue();
148         }
149     }
150 
151     private static final Set<String> buildExcludedMethodName() {
152         Set<String> excludedMethodName = new HashSet<String>();
153         Method[] methods = Object.class.getDeclaredMethods();
154         for (Method m : methods) {
155             excludedMethodName.add(m.getName());
156         }
157         // getClass() registerNatives() can not be enhanced
158         // excludedMethodName.remove("getClass");    
159         // excludedMethodName.remove("registerNatives");
160         return excludedMethodName;
161     }
162 }

封装后可以直接使用一句话, 还可用来增强已有对象

1         AopDemo demo = Enhancer.enhance(AopDemo.class);

示例

 1 @Before({PrivilegeInterceptor.class, LogInterceptor.class})
 2 public class AopDemo {
 3 
 4     public static void main(String[] args){
 5         AopDemo demo = Enhancer.enhance(AopDemo.class);
 6         demo.doSomething();
 7         demo.doOtherthing();
 8 
 9     }
10 
11     public void doOtherthing() {
12         // 默认沿用Class的interceptor
13         System.out.println("do 111111111111111");
14     }
15 
16     @Clear(PrivilegeInterceptor.class)
17     public void doSomething() {
18         // 手动清除了权限Interceptor
19         System.out.println("do 222222222222222");
20     }
21 }
 1 public class LogInterceptor implements Interceptor{
 2     @Override
 3     public void intercept(Invocation inv) {
 4         inv.invoke();
 5         System.out.println("Log记录入库");
 6     }
 7 }
 8 
 9 public class PrivilegeInterceptor implements Interceptor{
10     @Override
11     public void intercept(Invocation inv) {
12         System.out.println("鉴权成功");
13         inv.invoke();
14     }
15 }

doOtherthing执行结果

鉴权成功 do 111111111111111 Log记录入库

doSomething执行结果

do 222222222222222 Log记录入库

其他使用直接用来增强对象

1         AopDemo demoSinle1 = Enhancer.enhance(AopDemo.getInstance());

在enhance里new Interceptor

1         AopDemo demo3 = Enhancer.enhance(AopDemo.class, new Interceptor() {
2             @Override
3             public void intercept(Invocation inv) {
4                 System.out.println("new before");
5                 inv.invoke();
6                 System.out.println("new after");
7             }
8         });
9         demo3.doSomething();

在需要增强的方法上写@Before

1     @Before(LogInterceptor.class)
2     public void doOtherthing() {
3     }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码匠的流水账

聊聊spring cloud gateway的GatewayFilter

本文主要研究一下spring cloud gateway的GatewayFilter

4981
来自专栏吴生的专栏

spring boot 源码解析-SpringApplication初始化

就是这么简单的代码,构成了spring boot的世界. 那么代码中只有⼀个@SpringBootApplication 注解 和 调⽤了SpringAppli...

4445
来自专栏葡萄城控件技术团队

深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)

访问修饰符(或者叫访问控制符)是面向对象语言的特性之一,用于对类、类成员函数、类成员变量进行访问控制。同时,访问控制符也是语法保留关键字,用于封装组件。 Pub...

3179
来自专栏desperate633

LeetCode Fizz Buzz题目分析代码

Write a program that outputs the string representation of numbers from 1 to n.

741
来自专栏行者悟空

基于Scala Acotor实现多线程单词统计(WordCount)

2462
来自专栏函数式编程语言及工具

SDP(9):MongoDB-Scala - data access and modeling

    MongoDB是一种文件型数据库,对数据格式没有硬性要求,所以可以实现灵活多变的数据存储和读取。MongoDB又是一种分布式数据库,与传统关系数据库不同...

3894
来自专栏码匠的流水账

springboot2自定义statsd指标前缀

springboot2引入了micrometer,1.x版本的spring.metrics.export.statsd.prefix在2版本中已经被标记为废弃,...

1332
来自专栏积累沉淀

Hadoop--HDFS API编程封装

HDFS是一个分布式文件系统,既然是文件系统,就可以对其文件进行操作,比如说新建文件、删除文件、读取文件内容等操作。下面记录一下使用JAVA API对HDFS中...

28810
来自专栏Hongten

python开发_calendar

如果你用过linux,你可能知道在linux下面的有一个强大的calendar功能,即日历

1202
来自专栏小樱的经验随笔

Code forces 719A Vitya in the Countryside

A. Vitya in the Countryside time limit per test:1 second memory limit per test:2...

3466

扫码关注云+社区

领取腾讯云代金券