您的位置:首页 > 编程语言 > Java开发

Java中实现AOP的两种方式 之二:使用CGLIB开源框架实现

2020-01-13 19:22 519 查看

什么是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动态代理的区别

  1. Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

转载于:https://my.oschina.net/Declan/blog/1787357

  • 点赞
  • 收藏
  • 分享
  • 文章举报
chengjingpa8098 发布了0 篇原创文章 · 获赞 0 · 访问量 248 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐