Jdk动态代理,CGLib字节码生成
如果目标对象实现了接口,可以使用jdk动态代理和CGLib。
如果没有实现接口,只能使用CGLib。
一,jdk动态代理
1.java动态代理用到了一个类(Proxy)和一个接口(InvocationHandler)
2.jdk动态代理是通过实现目标类所实现的接口来产生代理类并生成代理对象的
3.类Proxy:该类用于创建目标类的代理对象。
Proxy类常用的方法有:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
该方法是一个static的方法,用于返回目标对象的代理对象
1.参数loader就是加载目标类的classloader
2.参数interfaces就是目标类所实现的接口
3.参数handler,就是接口InvocationHandler实现类的对象,我们要自己去实现该接口,
并实现其方法Object invoke(Object proxy, Method method, Object[] args)
4.InvocationHandler用官方话说就是:是代理实例的调用处理程序 实现的接口
用我的话来说就是,代理对象调用某个方法时,就会调用handler的invoke方法,
该方法中拥有目标对象的引用,而且该方法中拥有权限判断等需要在目标方法被调用之前或之后进行的操作
在该方法中,会先进行权限判断,然后再真真正正的去调用目标对象对应的方法
5.要实现动态代理,首要条件是目标类是面向接口的
核心代码:
InvocationHandler:
package com.test.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler{ //target就是目标对象 private Object target; public void setObject(Object target) { this.target = target; } //proxy就是代理对象 //method就是对应于在代理对象上调用的接口方法的 Method 实例,调用method.invoke()方法时才真真的去调用目标类的方法 //args是要往目标方法中传入的参数,一般来源于代理对象调用方法时传入的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method.getName()可以返回要调用的目标方法的名称 System.out.println("---------------目标方法:"+method.getName()+"执行之前do some thing---------------------"); //调用目标类的目标方法,returnObj是目标方法返回的值 Object returnObj = method.invoke(target, args); System.out.println("---------------目标方法:"+method.getName()+"执行之后do some thing---------------------"); return returnObj; } }
proxy:获取代理对象
package com.test.dynamicProxy; import java.lang.reflect.Proxy; public class GetProxyObj { private Object obj; private MyInvocationHandler handler; public GetProxyObj(Object obj,MyInvocationHandler handler){ this.obj = obj; handler.setObject(obj); this.handler = handler; } /** * 用于返回目标对象的代理类 * @return */ public Class getProxyClass(){ //obj.getClass().getClassLoader() 返回目标对象的classloader // obj.getClass().getInterfaces()返回目标类所实现的接口数组 Class clazz = Proxy.getProxyClass(obj.getClass().getClassLoader(), obj.getClass().getInterfaces()); return clazz; } /** * 用于返回目标对象的代理对象 * obj.getClass().getClassLoader() 返回目标对象的classloader * obj.getClass().getInterfaces()返回目标类所实现的接口数组 * @return */ public Object getProxyObject(){ return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } }
二,CGlib动态代理
cglib是Spring、Hibernate依赖的核心包,
但是cglib还算不上最底层的,因为它是在asm包之上做了封装,以增强易用性。
据了解,Hibernate3.2已经不再依赖cglib了,而直接依赖asm。
这些都不影响cglib的强大功能。
核心代码:
MyMethodInterceptor.java
package com.test.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println(">>>MethodInterceptor start..."); Object result = methodProxy.invokeSuper(object,args); System.out.println(">>>MethodInterceptor ending..."); return result; } }
测试:
package com.test.cglib; import net.sf.cglib.proxy.Enhancer; public class Test { public static void main(String rags[]){ //Target target = new Target(); Test test = new Test(); Target proxyTarget = (Target)test.createProxy(Target.class); String res=proxyTarget.execute(); System.out.println("result:"+res); } public Object createProxy(Class targetClass){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(new MyMethodInterceptor()); return enhancer.create(); } }
三,以一个实例在简单介绍下cglib的应用 ,我们模拟一个虚拟的场景,模拟对表的操作(代码参见附件dynamicProxyDemo.rar)
1. 开始我们对表提供了CRUD方法,TableDAO.java
2. 创建一个DAO工厂,用来生成DAO实例,TableDAOFactory.java
3. 创建客户端,用来调用CRUD方法,Client.java
OK,完成了,CRUD方法完全被调用了。
当然这里并没有CGlib的任何内容。
问题不会这么简单的就结束,新的需求来临了
=====================================================================
Boss告诉我们这些方法不能开放给用户,只有“张三”才有权使用。
怎么办,难道我们要在每个方法上面进行判断吗
对了对了Proxy可能是最好的解决办法。jdk的代理就可以解决了。 好了我们来动手改造吧。等等jdk的代理需要实现接口
我们的dao类需要改变了。既然不想改动dao又要使用代理,我们这就请出CGlib
1. 只需新增一个权限验证的方法拦截器,AuthProxy.java
2. 对我们的dao工厂进行修改,我们提供一个使用代理的实例生成方法,getAuthInstance()
3. 客户端添加了两个方法用来验证不同用户的权限,haveAuth(),haveNoAuth()
OK,"张三"的正常执行,"李四"的没有执行。
看到了吗?简单的aop就这样实现了
=====================================================================
Boss又来训话了,不行不行,
现在除了"张三"其他人都用不了了,现在不可以这样。他们都来向我反映了,必须使用开放查询功能
CGlib给我们提供了方法过滤器(CallbackFilter)
CallbackFilter可以明确表明,被代理的类中不同的方法, 被哪个拦截器所拦截
1. 下面我们就来做个过滤器用来过滤query方法,AuthProxyFilter.java
2. 在工场中新增一个使用了过滤器的实例生成方法,getAuthInstanceByFilter()
3. 客户端添加了个方法用来验证过滤器,haveAuthByFilter()
setCallbacks中定义了所使用的拦截器,
其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器,
他们是有序的。一定要和CallbackFilter里面的顺序一致。
上面return返回的就是返回的顺序。也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截
ok,现在"李四"也可以使用query方法了,其他方法仍然没有权限。
哈哈,当然这个代理的实现没有任何侵入性,无需强制让dao去实现接口