提到设计模式,我们不得不提下代理模式,这里给出其定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。说白了就是我们不能直接操作被代理的对象,而只能通过操作代理对象而间接操作被代理的对象。这就好比水厂、送水代理点、用户三者的关系,水厂就是被代理的对象,送水代理点就是代理对象,用户就是要访问被代理对象的客户,用户不能直接跟水厂买水,得去代理点买水,代理点卖水给用户,然后再去水厂进水。spring的aop就是应用的代理模式。
在java中代理模式分为静态代理模式和动态代理模式。
静态代理相对动态代理比较简单容易理解,就是代理类同样实现目标对象所实现的接口,并且内置一个目标对象的实例,代理对象的所有方法的实现都是通过调用这个内置实例的相同方法来完成的。静态代理虽然简单容易实现,但是其存在一个弊端,就是每给一个类做代理,就得重新定义一个代理对象,这显然是不可取的。所以就产生了动态代理。
动态代理就我目前接触的有俩种实现方式,一种是jdk自带的proxy、invocationhandler方式,一种是基于字节码技术cglib的方式。下面分别介绍下。
一、proxy、invocationhandler
package org.lujunfa.proxyDemon;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理 相对于静态代理,不用实现接口中的每一个方法
* @author lujunfa
*
*/
public class DynamicProxyDemon {
public static void main(String[] args) {
//设置ProxyGenerator的saveGeneratedFiles为true,动态生成中间代理对象的二进制文件,
//通过反编译该二进制文件,我们能看到这个代理对象的实现逻辑
//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
realSubject subject = new realSubject();
Subject proxySubject = (Subject) Handler.bind(subject);
System.out.println("className:"+proxySubject.getClass().getName());
proxySubject.doSomething1();
proxySubject.doSomething2();
proxySubject.equals("sdfsd");
}
}
//处理器
class Handler implements InvocationHandler{
//被代理的实例对象
Object subject = null;
public Handler(Object object){
super();
this.subject = object;
}
//返回动态生成的代理对象
public static Object bind(Object subject) {
return (Subject) Proxy
.newProxyInstance(Handler.class.getClassLoader(),
subject.getClass().getInterfaces(), new Handler(subject));
}
//调用代理对象的方法时,实际是调用该方法,然后通过反射机制调用目标对象的对应方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before do something");
// TODO Auto-generated method stub
//反射机制调用目标对象的相应方法
Object returnObj = method.invoke(subject, args);
System.out.println("after do something");
return returnObj;
//
}
}
//目标接口
interface Subject{
void doSomething1();
void doSomething2();
}
//被代理的目标类
class realSubject implements Subject{
public void doSomething1() {
// TODO Auto-generated method stub
System.out.println("say hello");
}
public void doSomething2() {
// TODO Auto-generated method stub
System.out.println("saygoodbuy");
}
}
大致说下通过proxy、proxy实现动态代理的过程。
①创建一个自定义的处理器对象,并继承invocationhandler类,通过复写invocationhandler的invok方法,实现我们自己的事前、事后处理逻辑。
②通过调用Proxy的newProxyInstance方法,传入目标对象的类加载器,目标接口和自定义的处理器对象,返回一个动态生成的代理实例对象,这个代理对象时jdk临时生成的对象,它并不存在工作空间中,只要程序运行完,这个对象就会消失,如果我们想看下这个代理的实现逻辑,我们可以通过System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");设置ProxyGenerator代理生成器的saveGeneratedFiles属性为true,则会产生一个该临时对象的二进制文件(注意:事前我们要创建一个和目标接口所在包名路径一致的文件夹,否则将报找不到路径的错误),然后通过jad,jd-gui类似的反编译工具就能反编译成java文件。
③将Proxy.newProxyInstance生成的代理对象想转换成目标接口对象,在执行里面的方法。
知其然必将知其所以然,否则我们永远也学不到里面的精髓,现在通过阅读源码一探究竟。首先我们看下这个生成的中间代理对象里面究竟有什么。
package org.lujunfa.proxyDemon;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements Subject
{
public $Proxy0(InvocationHandler invocationhandler)
{
//将invacationhandler实例对象传给Proxy的InvocationHandler全局属性
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
//这里的h是继承至Proxy的Invocationhandler h属性的
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void doSomething1()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void doSomething2()
{
try
{
super.h.invoke(this, m4, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
//这里的h是继承至Proxy的Invocationhandler h属性的
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m4;
private static Method m2;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("org.lujunfa.proxyDemon.Subject").getMethod("doSomething1", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m4 = Class.forName("org.lujunfa.proxyDemon.Subject").getMethod("doSomething2", 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());
}
}
}
从代码中可以看出这个中间代理类继承了Proxy类并且实现了目标接口,在它被加载的时候就通过反射机制获得了目标对象所有方法的实例对象,并且实现了目标接口的所有方法,我们在调用这个临时代理对象的方法时,实际是调用继承至Proxy的InvocationHandler实例的invok方法,invok方法在通过反射调用目标对象的对应方法。注意:Proxy的InvocationHandler 全局属性是执行newProxyInstance时传递的handler对象,然后在传递给临时代理对象,在临时代理对象的构造方法中在赋给Proxy的InvocationHandler 全局属性。
为了节省篇幅,这里不在逐步讲解Proxy和InvocationHandler的源码了,有兴趣的可以自己去看jdk源码。
jdk自带的动态代理实现方式虽然规避了静态代理的弊端,但它是面向接口代理的,也就是说如果我们要代理的对象它没实现接口,那我们就不能通过这种方式实现动态代理,这时候就得通过基于字节码技术的cglib来实现动态代理了。
二 、cglib动态代理
因为最近工作比较忙,关于cglib实现动态代理的方式下会讲解