Java中实现AOP的两种方式 之二:使用CGLIB开源框架实现
什么是CGLIB
CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Spring中就有用CGLIB实现AOP,Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib
为什么使用CGLIB
上篇博客(https://www.geek-share.com/detail/2733253033.html)已经对JDK自带的动态代理Proxy进行说明,那为什么又要用CGLIB呢?这是因为CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。(关于Java动态代理,可以参考另外一个博主的博文 Java动态代理分析)
使用CGLIB实现AOP实例
下面我们就用CGLIB来实现一个AOP的简单例子。
这里我使用的是maven搭建的环境
1. 要使用CGLIB,当然第一步是导入CGLIB的jar包
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
2. cglib最大的优点就是不需要定义目标类的统一接口,既可以代理普通的类,也可以代理接口
这里以一个普通的类为例进行说明, 新建一个Service类
package com.declan.aop.cglib; /** * 目标类 * @author Declan */ public class Service { public void doSomething(){ System.out.println("doSomething"); } public void doElseSomething(String str){ System.out.println("doElseSomething"); } public String fixedValueMethod(){ System.out.println("fixedValueMethod"); return "fixedValueMethod"; } }
3. 新建动态代理类,该类需要实现CGLIB的 MethodInterceptor 接口
package com.declan.aop.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 实现动态代理类 用于在pointcut处添加advise * @author Declan * */ public class ServiceDynamicProxy implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。 System.out.println("-------------before-------------"); // 执行目标类方法 proxy.invokeSuper(object, args); // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。 System.out.println("-------------after--------------"); return null; } }
该类中的 intercept 方法就是用来对基本类的方法进行拦截的。
4. 新建一个工厂类,用来生成代理类
package com.declan.aop.cglib; import net.sf.cglib.proxy.Enhancer; /** * 工厂类,生成增强过的目标类 * @author Declan */ public class ProxyFactory { public static <T>T getServiceBase(Class clz) { //Enhancer既能够代理普通的class,也能够代理接口 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clz); //用来设置父类型(这里可以是一个具体的类,也可以是一个接口) // 回调方法的参数为代理类对象ServiceDynamicProxy,最后增强目标类调用的是代理类对象ServiceDynamicProxy中的intercept方法 enhancer.setCallback(new ServiceDynamicProxy()); Object retObj = enhancer.create(); //用来创建增强对象 return (T)retObj; } }
在这个类里面,最重要的就是通过CGLIB的 Enhancer 这个类,该类是CGLIB中最常用的类,该类用来创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。
5. 测试
package com.declan.aop.cglib; /** * 测试类 * * @author Declan */ public class MainTest { public static void main(String[] args) { Service serviceBase = ProxyFactory.getServiceBase(Service.class); serviceBase.doSomething(); } }
测试结果
-------------before------------- doSomething -------------after--------------
到这里就将利用CGLIB来实现AOP讲完了,接下来无非就是一些通知的添加。
6. 方法的过滤
了解JDK的动态代理的哥们都知道,jdk自带的Proxy实现AOP在进行方法过滤的时候,都需要自己写代码对每个方法进行判断,然后再决定是否需要添加通知。但是CGLIB比较强大的就是可以通过一个过滤器来对方法进行过滤,帮助用户来过滤哪些方法需要进行添加通知,哪些方法不需要,哪些方法需要返回固定值。 接下来就详细说一下利用CGLIB进行AOP方法过滤。
这里主要用的是CallbackFilter接口
1)首先自定义一个类ServiceCallbackFilter 该类需要实现CallbackFilter
package com.declan.aop.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.CallbackFilter; public class ServiceCallbackFilter implements CallbackFilter { /** * 对方法记性过滤 * 此处包含了CGLib中的3种回调方式: * (1)MethodInterceptor:方法拦截器,上一篇文章中已经详细介绍过,此处不再赘述。 * * (2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。 * * (3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值 */ public int accept(Method method) { // TODO Auto-generated method stub String name = method.getName(); if (name.equals("doSomething")) { return 0;// Callback callbacks[0] 进行拦截 } else if (name.equals("doElseSomething")) { return 1;// Callback callbacks[1] 不进行拦截 } else if (name.equals("fixedValueMethod")) { return 2;// Callback callbacks[2] 返回固定值 } return 1;// Callback callbacks[1] } }
这里所返回的 0 , 1, 2 在后面的代码中就会明白是什么意思(其实就是一个Callback[]的索引)
2)新建一个类 ConcreteClassFixedValue 该类需要实现 FixedValue接口
package com.declan.aop.cglib; import net.sf.cglib.proxy.FixedValue; /** * 固定值 * @author Declan * */ public class ConcreteClassFixedValue implements FixedValue { @Override public Object loadObject() throws Exception { System.out.println("ConcreteClassFixedValue loadObject ..."); return "ConcreteClassFixedValue String Value"; } }
3) 修改之前的ProxyFactory 类, 将过回调过滤加进去
package com.declan.aop.cglib; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.NoOp; /** * 工厂类,生成增强过的目标类 * @author Declan */ public class ProxyFactory { public static <T>T getServiceBase(Class clz) { //Enhancer既能够代理普通的class,也能够代理接口 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clz); //用来设置父类型(这里可以是一个具体的类,也可以是一个接口) //设置回调过滤器 ServiceCallbackFilter serviceCallbackFilter = new ServiceCallbackFilter(); enhancer.setCallbackFilter(serviceCallbackFilter); //设置回调方法 (回调过滤器ServiceCallbackFilter中返回的值就是 callbacks 这个数组的索引) Callback interceptor=new ServiceDynamicProxy();//(0) Callback noOp=NoOp.INSTANCE;//(1) Callback fixedValue=new ConcreteClassFixedValue();//(2) Callback[] callbacks=new Callback[]{interceptor,noOp,fixedValue}; enhancer.setCallbacks(callbacks); Object retObj = enhancer.create(); //用来创建增强对象 return (T)retObj; } }
4) 进行测试
package com.declan.aop.cglib; /** * 测试类 * * @author Declan */ public class MainTest { public static void main(String[] args) { Service serviceBase = ProxyFactory.getServiceBase(Service.class); serviceBase.doSomething(); serviceBase.doElseSomething("测试"); System.out.println(serviceBase.fixedValueMethod()); } }
显示的结果
-------------before------------- doSomething -------------after--------------doElseSomething ConcreteClassFixedValue loadObject ... ConcreteClassFixedValue String Value
从结果可以看出,对doSomething() 这个方法添加通知,对doElseSomething()没有添加通知、对fixedValueMethod() 方法的调用返回的总是固定的值。
CGLIB和Java动态代理的区别
- Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
- Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
转载于:https://my.oschina.net/Declan/blog/1787357
- 点赞
- 收藏
- 分享
- 文章举报
- Java中实现AOP的两种方式 之二:使用CGLIB开源框架实现
- Spring AOP 代理实现的两种方式: JDK动态代理 和 Cglib框架动态代理
- 两种方式实现java定时器,使用quartz定时器框架和java自带Timer定时器,编写定时任务
- Java中实现AOP的两种方式 之一: 使用JDK自带的动态代理类Proxy实现
- 【框架】[Spring]纯Java的方式实现AOP切面(拦截)技术
- 【框架】[Spring]纯Java方式实现AOP拦截-详解ThrowsAdvice异常通知
- Java的静态代理、动态代理,CGLib的动态代理,使用动态代理基于AOP的AspectJ框架—深入探究
- Asm实现静态AOP的两种方式-生成java-proxy类
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- 深入理解java动态代理的两种实现方式(JDK/Cglib)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Java实现AOP的两种方式
- JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没
- Spring AOP 使用注解的方式实现用户日志的两种方法
- java 读取文件——按照行取出(使用BufferedReader和一次将数据保存到内存两种实现方式)
- [置顶] Java的静态代理、动态代理,CGLib的动态代理,使用动态代理基于AOP的AspectJ框架—深入探究
- Spring中AOP实现的两种方式之JDK和cglib的动态代理
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理-转载
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- Spring实现AOP方式之二:使用注解配置 Spring AOP