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

什么是CGLIB

        CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Spring中就有用CGLIB实现AOP,Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

为什么使用CGLIB

        上篇博客(https://my.oschina.net/Declan/blog/1787024)已经对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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值