与之前写的Java随笔一样,此篇博客主要是用来记录我之前存放在本地的Word文档中的一些Java的自身理解,由于水平有限所以可能只适合自己加深理解与记忆。
目录
本篇博客中,主要是介绍动态代理的两种方式。
动态代理的实现方式,我学习了两种,一种是基于java.lang.reflect.InvocationHandler的,另外一种是基于Cglib的,cglib需要引入对应的jar包。Spring中也是使用了这两种代理方式。
如果被代理的类实现了接口,那么使用InvocationHandler相关的类可以实现动态代理,而如果被代理的类并没有实现接口,那么就要使用Cglib来实现了。
使用接口的类的动态代理实现
接口类:
package com.zhl.mytest;
public interface Student {
public void sayHello();
}
实现类:
package com.zhl.mytest;
public class RealStudent implements Student{
public void sayHello(){
System.out.println("Hello!");
}
}
如上,假如我们想实现动态代理RealStudent 可以使用如下方式。
package com.zhl.mytest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test implements InvocationHandler{
private Object obj;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Before Invoke");
//执行方法之前做一些操作
Object returnObj = method.invoke(obj, args);
//执行方法之后做一些操作
System.out.println("After Invoke");
return returnObj;
}
public Object bind(Object obj){
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
Test test = new Test();
Student stu = (Student) test.bind(new RealStudent());
stu.sayHello();
}
}
可以看到 我们一个类 实现了InvocationHandler接口,并且实现了invoke函数,在里面我们可以在调用 代理对象方法 之前和之后做一些操作。Obj保存 被代理类的对象,然后使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)获取到 代理后的对象, 方法参数 为 类加载器,被代理类实现的接口类数据,以及代理处理的InvocationHandler 对象,这里也就是Test类的实例对象。将获得的对象转为 想要的接口的实现类,就能跟使用 被代理类的对象一样 使用被增强的类的对象,如果想处理不同的方法,还能在invoke中 判断Method 来判定是否要代理之类的。
未使用接口的类的动态代理实现
假设我们有下面这样一个需要被代理的类:
package com.zhl.mytest;
public class RealStudent {
public void sayHello(){
System.out.println("Hello!");
}
public void sayHelloTwo(){
System.out.println("Hello Two!");
}
}
因为它没有实现接口,所以不能用 代理类实现InvocationHandler 接口然后 Proxy.newProxyInstance方法 来获取代理类,因为 第二个参数被代理类的接口是空的。如果强行将获得的对象转为RealStudent 会报错:
所以要使用Cglib的方式,如下:
package com.zhl.practice.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class RealStudentProxy implements MethodInterceptor{
@Override
public Object intercept(Object object, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
//调用方法之前进行一些处理
System.out.println("Before Invoke!");
Object returnObj = methodProxy.invokeSuper(object, objects);
//调用方法之后进行一些处理
System.out.println("After Invoke!");
return returnObj;
}
}
然后调用方式如下:
package com.zhl.practice.cglib.test;
import net.sf.cglib.proxy.Enhancer;
import org.junit.Test;
import com.zhl.mytest.RealStudent;
import com.zhl.practice.cglib.RealStudentProxy;
public class CglibTest {
@Test
public void testCglib(){
RealStudentProxy studentProxy = new RealStudentProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealStudent.class);
enhancer.setCallback(studentProxy);
RealStudent reStudent = (RealStudent) enhancer.create();
reStudent.sayHello();
}
}
使用Enhancer 将 代理类 和 被代理类 联系在一起 然后 使用enhancer.create() 获取到增强后的 类的对象,从而实现了动态代理。Cglib是使用ASM来动态修改byteCode来实现的动态代理。
使用Cglib实现不同方法进行不同代理
如果想对不同的方法进行不同的拦截可以使用如下代码:
首先定义另外一个代理类:
package com.zhl.practice.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class AnotherRealStudentProxy implements MethodInterceptor{
public Object delegate;
@Override
public Object intercept(Object object, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
Object returnObj = null;
//下面三种调用方式都能执行
returnObj = methodProxy.invokeSuper(object, objects);
returnObj = method.invoke(delegate, objects);
returnObj = methodProxy.invoke(delegate, objects);
System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
return returnObj;
}
}
methodProxy.invokeSuper调用原始类的方法,invoke执行子类的方法,如果使用methodProxy,invoke(object,objects)或者method.invke(object,objects) 会出现循环调用 直接程序报错。
然后定义一个CallbackFilter过滤,根据不同的方法名来 返回不同的Callback的索引:
package com.zhl.practice.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class ProxyFilter implements CallbackFilter{
@Override
public int accept(Method method) {
if("sayHello".equals(method.getName())){
return 0 ;
}
return 1;
}
}
调用方式如下:
package com.zhl.practice.cglib.test;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import org.junit.Test;
import com.zhl.mytest.RealStudent;
import com.zhl.practice.cglib.AnotherRealStudentProxy;
import com.zhl.practice.cglib.ProxyFilter;
import com.zhl.practice.cglib.RealStudentProxy;
public class CglibTest {
@Test
public void testCglib(){
RealStudentProxy studentProxy = new RealStudentProxy();
AnotherRealStudentProxy anotherProxy = new AnotherRealStudentProxy();
anotherProxy.delegate = new RealStudent();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealStudent.class);
enhancer.setCallbacks(new Callback[]{studentProxy,anotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new ProxyFilter());
RealStudent reStudent = (RealStudent) enhancer.create();
reStudent.sayHello();
reStudent.sayHelloTwo();
}
}
设置Callbacks 传递 数组 这里传递不同的 代理类实例,NoOp.INSTANCE代表空代理类,
ProxyFilter根据不同的方法 返回不同的索引,这里索引 对应Callbacks中,因此在调用sayHello会使用RealStudentProxy而调用其他方法(如sayHelloTwo)会使用AnotherRealStudentProxy来进行代理。
这样设置在构造函数中调用对应方法也会被拦截,如下在RealStudent类 里面添加一个构造函数
public RealStudent(){
sayHello();
}
执行上面 的测试代码 发现 sayHello 被拦截了 两次,也就是构造函数中的 sayHello方法也被拦截处理了,如果不想让构造函数中调用的方法被拦截,那么可以使用:enhancer.setInterceptDuringConstruction(false);这里默认是ture,改为false就能让构造函数中调用方法时不被拦截。