上一篇文章《设计模式--动态代理(JDK)》已经写了JDK中动态代理的应用,这篇文章将介绍动态代理在CGLIB中应用。,从上篇文章中我们知道使用JDK动态代理是有一定限制。就是被代理类必须至少实现一个接口,因为JDK为我们生成的动态代理也是实现这个接口的。所以如果没有接口,JDK基本就歇菜了。CGLIB正好弥补了JDK的这个缺陷,他的被代理类是无需实现接口,因为它的实现机制是继承被代理类,从而创建一个代理对象(继承被代理对象)的。
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。将它的jar包引入到我们的程序就可以使用了。需要注意的是CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。所以cglib包中必须包含asm,否则会报错可以选择其他的CGLIB版本,比如cglib-nodep,它里面就含有asm包。
在网上看了好多例子都说CGLIB运用了回调,虽然知道他们讲了什么,但是不明白为什么调用代理的方法就直接跳转到AuthProxy中的intercept方法中。网上总是说当调用代理对象的方法的时候,会自动调用intercept方法,但是WHY,就算把生成的代理反编译了,迷迷糊糊看不懂啊。然后化整为零从回调方法开始一个个击破,最后画出了相同的类图。现在对CGLIB的回调才稍微明白点了。
下面写一个例子,也是在网上看到的,但是个人感觉很不错,在这里和大家分享。
1 代理类对表的各类操作
package com.tgb.cglibTable;
public class TableDao {
public void create(){
System.out.println("create() is running...");
}
public void delete(){
System.out.println("delete() is running...");
}
public void update(){
System.out.println("update() is running...");
}
public void query(){
System.out.println("query() is running...");
}
}
package com.tgb.cglibTable;
import net.sf.cglib.proxy.Enhancer;
public class TableDAOFactory {
private static TableDao tDao = new TableDao();
public static TableDao getInstance(){
return tDao;
}
public static TableDao getAuthInstance(AuthProxy authProxy){
Enhancer en = new Enhancer(); //Enhancer用来生成一个原有类的子类(代理类)
//进行代理
en.setSuperclass(TableDao.class);
//设置织入逻辑
en.setCallback(authProxy); //注册回调函数
//生成代理实例
return (TableDao)en.create();
}
}
Enhancer是CGLIB的核心包,它是生成代理类的工具。在生成代理类的同时根据我们的参数对代理类进行设置,比如setSuperclass方法就是调用Enhancer的方法让生成的代理对象继承TabbleDao(被代理类)。setCallback就是设置回调。最后create方法,生成最后的代理类。如果想查看的生成的代理可以根据4中客户端中代码“System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"F:\\class");”获取,通过反编译工具jd-gui,可以查看生成的代理类。
package com.tgb.cglibTable;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class AuthProxy implements MethodInterceptor {
private String userName;
AuthProxy(String userName){
this.userName = userName;
}
//实现MethodInterceptor接口的intercept方法
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
//权限判断
if(!"张三".equals(userName)){
System.out.println("你没有权限!");
return null;
}
return arg3.invokeSuper(arg0, arg2);//把方法封装成了一个对象,然后回调父类(代理类中对应的方法)
}
}
在用CGLIB生成的代理类中,当调用代理类中的方法时,会调用intercept方法。因为在生成的代理类中(create、delete、update、query)的方法都会调用intercept,在intercept方法我们就可以增加我们想要添加的各种操作。最后的invokeSuper使用反射机制回调父类相应的方法。
4 客户端调用
package com.tgb.cglibTable;
import net.sf.cglib.core.DebuggingClassWriter;
public class Client {
public static void main(String[] args) {
//将新生成的所有类存放到"F:\class",为了看生成的代理类,用反编译工具编译后可以看具体生成的代理类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"F:\\class");
haveAuth();
//haveNoAuth();
//
}
//执行代理的增删改查
public static void doMethod(TableDao dao){
dao.create();
dao.query();
dao.update();
dao.delete();
}
//模拟有权限
public static void haveAuth(){
TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("张三"));
doMethod(tDao);
}
//模拟无权限
public static void haveNoAuth(){
TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("李四"));
doMethod(tDao);
}
}
5 总结:
(1)CGLIB的使用网上虽然有很多,但是却没有说为什么要这样写。所以看源码是很有必要的,但是哎。
(2)问题较多无法解决的时候,需要从简单处入手,各个击破。