动态代理个人理解
首先为什么要用动态代理?
-
动态代理是设计模式中的代理模式:
定义:为其它对象提供一种代理以控制对这个对象的访问控制;在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。
-
动态代理的作用
主要用来做方法的增强,让你可以在不修改源码(指愿方法)的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。
前提条件:
要实现代理,逻辑类必须去实现InvocationHandler接口,(1.建立代理对象和真实对象的关系,2.实现代理逻辑方法,真正的核心都在这个invoke()方法里)。
静态代理与动态代理的区别
静态代理
静态代理类:由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。
静态代理类通常只代理一个类。
静态代理事先知道要代理的是什么。
动态代理
动态代理类:在程序运行时,通过反射机制动态生成。
动态代理类通常代理接口下的所有类。
动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。
动态代理的调用处理程序必须实现InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。
动态代理实现中的关键点解释
存在一个接口和实现类
public interface Subject {
public void doSomething();
}
public class RealSubject implements Subject {
public void doSomething(){
System.out.println( "call doSomething()" );
}
}
动态代理绑定和代理逻辑实现
public class MyInvocationHandler implements InvocationHandler {
private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象
public MyInvocationHandler(){
}
public MyInvocationHandler(Object obj){
this.obj = obj;
}
//建立代理对象和真实对象的代理关系,返回代理对象
Public Object bind(Object obj){
This.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this) ;
}
//参数说明:类加载器和接口用于在Proxy类中获得代理类使用,处理器用于在Proxy类中通过构造方法的形式创建对象时使用。
//这个方法不是我们显示的去调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("before calling " + method);
method.invoke(obj, args);
System.out.println("after calling " + method);
return null;
}
}
这里的invoke()方法中的第一个参数,也就是代理实例作用
- 可以使用反射获取代理对象的信息(也就是proxy.getClass().getName())。
- 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象。
补充:Method 类的invoke()方法传参就是1:对象,2:形参(方法可能重构了许多,根据参数指定方法)
客户端:生成代理实例,并调用了doSomething()方法
public class Client {
public static void main(String[] args) throws Throwable{
Subject rs=new RealSubject();//这里指定被代理类
InvocationHandler ds=new MyInvocationHandler ();
Subject proxy = (Subject)ds.bind(rs);//返回代理对象
proxy .doSomeThing(); //调用方法
}
}
上面的bind()方法中Proxy.newProxyInstance()方法最为关键
在此方法中有如下几步
-
生成动态代理类,此类命名为$Proxy+数字,在生成这个类时,会先在缓存中查看此类是不是已经存在,存在直接返回,不存在就创建一个,简单说明一下有源码可以看,大概就是这样。
Class<?> cl = getProxyClass0(loader, intfs);
-
创建代理类的实例(对象),通过反射创建一个类的实例有两种方法
(1) Class.forName(“类名”).newInstance() ;//调用无参构造创建对象
(2) Class.getConstructor(“参数”).newInstance();//调用有参构造创建对象
Proxy.newProxyInstance()方法用的是第二种。
return (Object) cons.newInstance(new Object[] { h });//这里就已经创建了一个动态代理类实例。
在客户端中代理类对象有了,就可以直接调用接口中的方法了,这个方法是被重写的,我们来看看那个动态生成的代理类。
public final class $Proxy0 extends Proxy implements Subject{
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
//静态块
static {
try {
m1=Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3=Class.forName("***.RealSubject").getMethod("doSomething", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
//构造方法
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
//重写equal方法
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//重写hashcode方法
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//这个是关键部分,动态代理对象中重写接口中的方法
@Override
public final void doSomething() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//重写toString方法
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
这个动态代理类有如下特点,他会重写实现接口中的所有方法,并且重写equals(),hashCode(),toString()仅此三个方法。
接着把得到的 P r o x y 0 实 例 强 制 转 换 成 S u b j e c t , 并 将 引 用 赋 给 s u b j e c t 。 当 执 行 s u b j e c t . d o S o m e t h i n g ( ) 方 法 时 , 就 调 用 了 Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.doSomething()方法时,就调用了 Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.doSomething()方法时,就调用了Proxy0类中的doSomething()方法,进而调用父类Proxy中的h的invoke()方法.即MyInvocationHandler.invoke(),为什么当执行super.h.invoke()方法时,就调用了咱们自己写的那个MyInvocationHandler中的invoke()方法,因为$Proxy0在创建对象时调用的是有参构造,而这个代理类的有参构造中是super(invocationhandler),而这个invocationHandler是在Proxy.newProxyInstance()时就传进来的自己写的那个调用处理器,此时Proxy类中的成员InvocationHandler h,就会被赋值为自己写的那个调用处理器,于是在执行super.h.invoke()方法时,实际就是在执行自己写的那个调用处理器。
一个典型的动态代理创建对象过程可分为以下四个步骤:
- 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
- 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…}); - 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class}); - 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))