参考地址:https://blog.csdn.net/wy9717/article/details/80210203
动态代理-两种实现方式
按照代理的创建时期,代理类可以分为两种。
静态代理:就是为了在不入侵原本类的基础上,对功能进行扩展,或者过滤等等,是在编译期间已经存在于class文件里面。
动态代理:在程序运行时,运用反射机制动态创建。
今天我们只说动态代理,并分别讲述两种实现方式:
首先,我们先说明要被代理的对象,为接下来的工作做铺垫。
我们有这样一个接口(jdk动态代理必须有接口,后面说):
-
public interface MyInterface {
-
public String getName();
-
public int getAge();
-
}
有这么一个实现类:
-
public class MyInterfaceImpl implements MyInterface {
-
-
public String getName() {
-
System.out.println( "tom");
-
return "Tom";
-
}
-
-
-
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
-
*/
-
-
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 {
-
-
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