按照代理的创建时期,代理类可以分为两种。
静态代理:就是为了在不入侵原本类的基础上,对功能进行扩展,或者过滤等等,是在编译期间已经存在于class文件里面。
动态代理:在程序运行时,运用反射机制动态创建。
今天我们只说动态代理,并分别讲述两种实现方式:
首先,我们先说明要被代理的对象,为接下来的工作做铺垫。
我们有这样一个接口(jdk动态代理必须有接口,后面说):
public interface MyInterface {
public String getName();
public int getAge();
}
有这么一个实现类:
public class MyInterfaceImpl implements MyInterface {
@Override
public String getName() {
System.out.println("tom");
return "Tom";
}
@Override
public int getAge() {
System.out.println(11);
return 11;
}
}
代理就是在被代理的类的方法执行之前或者执行之后做一些事情,甚至过滤掉。所以我们在方法里输出一句话,便于展示。
JDK的动态代理
jdk的动态代理只有一个类和一个接口
InvocationHandler接口中只有一个方法
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
Proxy类是专门用来完成代理的操作类,可以为一个或多个接口生成实现类,具体方法为:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:全部的接口
InvocationHandler h:InvocationHandler接口的实现类对象
动态代理类的字节码在程序运行时由Java反射机制动态生成,不需要手动编写代码,java反射机制可以生成任意类型的代理类,所以不仅用起来简单,而且扩展性得到了提高。我们看一个实例:
InvocationHandler接口的实现类:
public class MyInvocationHandler implements InvocationHandler {
//要被代理的对象
private Object target;
MyInvocationHandler(){
}
MyInvocationHandler(Object target){
super();
this.target=target;
}
/**
*
* @param proxy 代理者
* @param method 被执行的方法
* @param args 执行方法时,要用到的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if("getName".equals(name)){
System.out.println("----before "+method.getName());
Object res = method.invoke(target, args);
System.out.println("----after"+method.getName());
return res;
}else{
return method.invoke(target,args);
}
}
}
我们只在getName方法之前和之后输出一些东西。
测试代码:
public static void main(String[] args) {
MyInterface myInterface=new MyInterfaceImpl();
MyInvocationHandler myInvocationHandler= new MyInvocationHandler(myInterface);
MyInterface inter= (MyInterface) Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),myInterface.getClass().getInterfaces(),myInvocationHandler);
inter.getName();
inter.getAge();
}
结果:
----before getName
tom
----aftergetName
11
从上述测试代码可以看出,要想使用JDK的动态代理,被代理类一定要实现某个接口,这是一个很大的缺陷。对于没有实现接口的类,我们要动态代理,就要使用下面的方法。
Cglib动态代理
Cglib是针对实现类来代理的,被代理者不需要实现接口,它对目标类生成一个子类,并覆盖其中的方法,以实现方法的增强。
cglib的主要方法拦截接口 MethodInterceptor,需要用户自己实现:
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
参数说明:
前三个参数与jdk中InvocationHandler中的Invoke相同:
Object var1:指被代理的对象。
Method var2:要调用的方法
Object[] var3:方法调用时所需要的参数
MethodProxy var4: JDK的java.lang.reflect.Method类的代理类,可以实现对源对象方法的调用。后面的例子将会帮助理解。
net.sf.cglib.proxy.Enhancer:cglib主要的增强类,下面简单看下其中方法:
setSuperclass: 设置被代理的对象。
setCallback: 设置回调函数,一般为MethodInterceptor的实现类。
creat: 创建一个代理对象,你可以直接使用创建的代理对象调用原本的方法。
看个实例:
public class MyCglibInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----------before----"+method.getName());
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("----------after----"+method.getName());
return o1;
}
}
之前说了,cglib是创建一个子类,并覆盖目标类方法,所以我们调用方法时,是使用methodProxy.invokeSuper()。我们在每个方法之前之后都输出一些语句。
测试代码:
MyCglibInterceptor myCglibInterceptor=new MyCglibInterceptor();
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(myInterface.getClass());
enhancer.setCallback(myCglibInterceptor);
MyInterface res = (MyInterface) enhancer.create();
res.getName();
res.getAge();
结果:
----------before----getName
tom
----------after----getName
----------before----getAge
11
----------after----getAge