AOP的另一种实现----cglib

简介:

 大家都知道,动态代理能够实现AOP,但是它有一个缺点,就是所有被代理的对象必须实现一个接口,否则就会报异常。那么如果被代理对象没有实现接口那该如何实现AOP呢?当然是能的,使用CGlib就可以实现。

1、什么是CGlib

  CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。 然这些实际的功能是asm所提供的,asm又是什么?Java字节码操控框架,具体是什么大家可以上网查一查,毕竟我们这里所要讨论的是cglib, cglib就是封装了asm,简化了asm的操作,实现了在运行期动态生成新的class。 可能大家还感觉不到它的强大,现在就告诉你。 实际上CGlib为spring aop提供了底层的一种实现;为hibernate使用cglib动态生成VO/PO (接口层对象)。

  它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback , 则原有类的每个方法调用都会转成调用实现了MethodInterceptor接口的proxy的intercept() 函数: 
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) 
   在intercept()函数里,你可以在执行Object result=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数,也可以瞒天过海,完全干 别的。说白了,就是AOP中的around advice。

  2、如何使用CGlib

  举个例子:比如DAO层有对表的增、删、改、查操作,如果要对原有的DAO层的增、删、改、查增加权限控制的话,修改代码是非常痛苦的。所以可以用AOP来实现。但是DAO层没有使用接口,动态代理不可用。这时候CGlib是个很好的选择。

TableDao.java:

 1 package com.cglib;
 2
 3 public class TableDao {
 4     public void create(){
 5         System.out.println(“create() is running…”);
 6     }
 7     public void delete(){
 8         System.out.println(“delete() is running…”);
 9     }
10     public void update(){
11         System.out.println(“update() is running…”);
12     }
13     public void query(){
14         System.out.println(“query() is running…”);
15     }
16 }

实现了MethodInterceptor接口的AuthProxy.java:用来对方法进行拦截,增加方法访问的权限控制,这里只允许张三访问。

 1 package com.cglib;
 2
 3 import java.lang.reflect.Method;
 4
 5 import net.sf.cglib.proxy.MethodInterceptor;
 6 import net.sf.cglib.proxy.MethodProxy;
 7 //方法拦截器
 8 public class AuthProxy implements MethodInterceptor {
 9     private String userName;
10     AuthProxy(String userName){
11         this.userName = userName;
12     }
13     //用来增强原有方法
14     public Object intercept(Object arg0, Method arg1, Object[] arg2,
15             MethodProxy arg3) throws Throwable {
16         //权限判断
17         if(!”张三”.equals(userName)){
18             System.out.println(“你没有权限!”);
19             return null;
20         }
21         return arg3.invokeSuper(arg0, arg2);
22     }
23 }

TableDAOFactory.java:用来创建TableDao的子类的工厂类

 1 package com.cglib;
 2
 3 import net.sf.cglib.proxy.Callback;
 4 import net.sf.cglib.proxy.Enhancer;
 5 import net.sf.cglib.proxy.NoOp;
 6
 7 public class TableDAOFactory {
 8     private static TableDao tDao = new TableDao();
 9     public static TableDao getInstance(){
10         return tDao;
11     }
12     public static TableDao getAuthInstance(AuthProxy authProxy){
13         Enhancer en = new Enhancer();  //Enhancer用来生成一个原有类的子类
14         //进行代理  
15         en.setSuperclass(TableDao.class);
16         //设置织入逻辑
17         en.setCallback(authProxy);
18         //生成代理实例  
19         return (TableDao)en.create();
20     }
21  }

测试类Client.java:

 1 package com.cglib;
 2
 3 public class Client {
 4
 5     public static void main(String[] args) {
 6 //        haveAuth(); 
 7         haveNoAuth();
 8     }
 9     public static void doMethod(TableDao dao){
10         dao.create();
11         dao.query();
12         dao.update();
13         dao.delete();
14     }
15     //模拟有权限
16     public static void haveAuth(){
17         TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy(“张三”));
18         doMethod(tDao);
19     }
20     //模拟无权限
21     public static void haveNoAuth(){
22         TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy(“李四”));
23         doMethod(tDao);
24     }
25 }

  这样就能够对DAO层的方法进行权限控制了。但是如果又改需求了,要把DAO层的query方法让所有用户都可以访问,而其他方法照样有权限控制,该如何实现呢?这可难不倒我们了,因为我们使用了CGlib。当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且不利于维护。还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法, 被哪个拦截器所拦截。下面我们就来做个过滤器用来过滤query方法。 

AuthProxyFilter.java:

 1 package com.cglib;
 2
 3 import java.lang.reflect.Method;
 4
 5 import net.sf.cglib.proxy.CallbackFilter;
 6 import net.sf.cglib.proxy.NoOp;
 7
 8 public class AuthProxyFilter implements CallbackFilter {
 9
10     public int accept(Method arg0) {
11         /
12           如果调用的不是query方法,则要调用authProxy拦截器去判断权限
13          /
14         if(!”query”.equalsIgnoreCase(arg0.getName())){
15             return 0; //调用第一个方法拦截器,即authProxy
16         }
17         /
18           调用第二个方法拦截器,即NoOp.INSTANCE,NoOp.INSTANCE是指不做任何事情的拦截器
19           在这里就是任何人都有权限访问query方法,所以调用默认拦截器不做任何处理
20          */
21         return 1;
22     }
23
24 }

  至于为什么返回0或者1,注释讲的很详细。

TableDAOFactory.java里添加如下方法:

1 public static TableDao getAuthInstanceByFilter(AuthProxy authProxy){
2         Enhancer en = new Enhancer();
3         en.setSuperclass(TableDao.class);
4         en.setCallbacks(new Callback[]{authProxy,NoOp.INSTANCE});  //设置两个方法拦截器
5         en.setCallbackFilter(new AuthProxyFilter());
6         return (TableDao)en.create();
7     }  

   这里得注意,en.setCallbacks()方法里的数组参数顺序就是上面方法的返回值所代表的方法拦截器,如果return 0则使用authProxy拦截器,return 1则使用NoOp.INSTANCE拦截器,NoOp.INSTANCE是默认的方法拦截器,不做什么处理。

  下面在测试类中添加如下方法:


1 //模拟权限过滤器
2     public static void haveAuthByFilter(){
3         TableDao tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy(“张三”));
4         doMethod(tDao);
5         tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy(“李四”));
6         doMethod(tDao);
7     }  

在main方法中调用该方法,程序运行结果如下:

create() is running…
query() is running…
update() is running…
delete() is running…
你没有权限!
query() is running…
你没有权限!
你没有权限!

  这样的话,所有用户都对query方法有访问权限了,而其他方法只允许张三访问。


相关文章
|
6月前
|
设计模式 安全 Java
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
1627 1
|
6月前
|
设计模式 算法 Java
AOP跨模块捕获异常遭CGLIB拦截而继续向上抛出异常
最近,在开发过程中,我遇到一个不易察觉的小bug。这个bug并没有直接给出报错信息,使得排查问题的根源变得困难。我希望通过分享这个经验,帮助大家避免重蹈覆辙,以免浪费不必要的时间和精力。为了避免类似的困境,我们应当时刻保持警惕,对开发过程中的每一个细节都进行严格的检查。同时,利用调试工具和日志输出等功能,可以帮助我们更快速地定位和解决问题。此外,定期进行代码审查和测试也是非常必要的,这有助于发现潜在的问题并及时解决。
128 1
|
11月前
|
缓存 Java Spring
Spring AOP中CGLIB代理对象增强通知执行原理
Spring AOP中CGLIB代理对象增强通知执行原理
73 0
|
Java Spring
手动实现aop使用的动态代理和cglib代理
spring的aop使用的是动态代理和cglib代理,在对象有实现接口的情况下使用动态代理,没有实现接口的情况下使用cglib代理。 cglib代理是继承目标对象来创建代理,所以目标对象不能使用final修饰。
|
设计模式 Java 编译器
Spring AOP【AOP的基本实现与动态代理JDK Proxy 和 CGLIB区别】
Spring AOP【AOP的基本实现与动态代理JDK Proxy 和 CGLIB区别】
Spring AOP【AOP的基本实现与动态代理JDK Proxy 和 CGLIB区别】
|
Java C++ Spring
Spring - AOP之底层实现(动态代理 JDK VS CGLIB)
Spring - AOP之底层实现(动态代理 JDK VS CGLIB)
123 0
Spring - AOP之底层实现(动态代理 JDK VS CGLIB)
|
Java Spring 容器
【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(上)
【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(上)
【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(上)
|
缓存 搜索推荐 Java
【小家Spring】详解Spring AOP的底层代理JdkDynamicAopProxy和ObjenesisCglibAopProxy的源码分析(介绍CGLIB使用中的坑)(上)
【小家Spring】详解Spring AOP的底层代理JdkDynamicAopProxy和ObjenesisCglibAopProxy的源码分析(介绍CGLIB使用中的坑)(上)
【小家Spring】详解Spring AOP的底层代理JdkDynamicAopProxy和ObjenesisCglibAopProxy的源码分析(介绍CGLIB使用中的坑)(上)
|
设计模式 监控 Java
《Spring 手撸专栏》第 12 章:炉火纯青,基于JDK和Cglib动态代理,实现AOP核心功能
实现 1. 工程结构 2. 代理方法案例 3. 切点表达式 4. 包装切面通知信息 5. 代理抽象实现(JDK&Cglib) 测试 1. 事先准备 2. 自定义拦截方法 3. 单元测试
187 0
《Spring 手撸专栏》第 12 章:炉火纯青,基于JDK和Cglib动态代理,实现AOP核心功能