概念:
通俗来讲:中介、黄牛等都是一种代理,他们有你的信息,来为你提供额外的支持。映射到程序上来讲就是代理对象持有被代理对象的引用,以控制对这个对象的访问。
代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
代理分为静态代理和动态代理
静态代理
静态就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了
租客接口
public interface Renter {
/**
* 租房
*/
void rentHouse();
}
北京租客(被代理对象):
public class BeiJingRenter implements Renter {
private String name;
@Override
public void rentHouse() {
System.out.println("我是:"+name);
System.out.println("我租房的要求是一个卧室即可");
}
public BeiJingRenter(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
代理类(持有被代理对象的引用):
public class StaticProxy implements Renter {
private BeiJingRenter beiJingRenter;
public StaticProxy(BeiJingRenter beiJingRenter) {
this.beiJingRenter = beiJingRenter;
}
@Override
public void rentHouse() {
System.out.println("我是中介");
beiJingRenter.rentHouse();
System.out.println("找到房子");
}
public static void main(String[] args) {
BeiJingRenter beiJingRenter = new BeiJingRenter("zhangsan");
StaticProxy staticProxy = new StaticProxy(beiJingRenter);
staticProxy.rentHouse();
}
}
执行结果:这样就可以在不修改被代理对象的同时对其进行前后置的扩展。
静态代理类优缺点
- 优点:
- 业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
- 缺点:
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理:
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
动态代理分为两种:jdk的动态代理和cglib动态代理
jdk动态代理:
还是以上面的租客举例子,下面是jdk动态代理下代理对象的实现:
public class Intermediary implements InvocationHandler {
/**
* 被代理对象的引用,这里指的是需要帮忙租房的
*/
private Renter renter;
public Object newInstance(Renter renter){
this.renter = renter;
Class clazz = renter.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是中介,我要帮忙找房子");
method.invoke(renter,args);
System.out.println("找完了");
return null;
}
}
测试:
public static void main(String[] args) throws Exception {
BeiJingRenter beiJingRenter = new BeiJingRenter("zhangsan");
Intermediary intermediary = new Intermediary();
Renter renter = (Renter) intermediary.newInstance(beiJingRenter);
renter.rentHouse();
}
结果:
实现重点:
1 被代理对象需要有接口
2 代理对象需要持有被代理对象的引用
3 代理对象需要实现InvocationHandler 并重写它的invoke方法
InvocationHandler中有说明:当代理对象的某个方法被调用时,会调用invoke方法,通过反射来进行执行
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
原理:
JDK Proxy 生成对象的步骤如下:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接 口。
3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体 现)。
4、编译新生成的 Java 代码.class。
5、再重新加载到 JVM 中运行。
当调用代理对象的某个方法时,通过this.h.invoke(this, m5, null);从而定位到代理对象的invoke方法来通过反射进行执行,而我们要做的也仅仅是重写inoke方法即可实现动态代理的功能。
感兴趣的话可以看一下生成的代理类,通过下面的方法来生成(jdk8):
public static void writeClassToDisk(String path) {
byte[] classFile = ProxyGenerator.generateProxyClass("$proxy4", new Class[]{BeiJingRenter.class});
FileOutputStream fos = null;
try {
fos = new FileOutputStream(path);
fos.write(classFile);
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
生成出来的代理类通过反编译查看,可以看到生成的代理类继承了proxy类,并实现了BeiJingRenter所实现的接口:
public final class $proxy4 extends Proxy
implements BeiJingRenter
{
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m10;
private static Method m2;
private static Method m5;
private static Method m8;
private static Method m7;
private static Method m9;
private static Method m11;
private static Method m0;
private static Method m6;
public $proxy4(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void setName(String paramString)
throws
{
try
{
this.h.invoke(this, m4, new Object[] { paramString });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String getName()
throws
{
try
{
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void notify()
throws
{
try
{
this.h.invoke(this, m10, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void rentHouse()
throws
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void wait(long paramLong)
throws InterruptedException
{
try
{
this.h.invoke(this, m8, new Object[] { Long.valueOf(paramLong) });
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void wait(long paramLong, int paramInt)
throws InterruptedException
{
try
{
this.h.invoke(this, m7, new Object[] { Long.valueOf(paramLong), Integer.valueOf(paramInt) });
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final Class getClass()
throws
{
try
{
return (Class)this.h.invoke(this, m9, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void notifyAll()
throws
{
try
{
this.h.invoke(this, m11, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void wait()
throws InterruptedException
{
try
{
this.h.invoke(this, m6, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("setName", new Class[] { Class.forName("java.lang.String") });
m3 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("getName", new Class[0]);
m10 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("notify", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m5 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("rentHouse", new Class[0]);
m8 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("wait", new Class[] { Long.TYPE });
m7 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("wait", new Class[] { Long.TYPE, Integer.TYPE });
m9 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("getClass", new Class[0]);
m11 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("notifyAll", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m6 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("wait", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
cglib动态代理
下面是cglib动态代理下代理对象的实现:
public class Intermediary implements MethodInterceptor {
private Object target;
public Object newInstance(Object target) throws Exception{
this.target = target;
Enhancer enhancer = new Enhancer();
//设置生成的实例时继承了被代理对象
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("我是中介,我要帮忙找房子");
//执行代理对象父类的方法,即被代理对象的方法
methodProxy.invokeSuper(o,args);
System.out.println("找完了");
return null;
}
}
测试:
public static void main(String[] args) {
try {
BeiJingRenter beiJingRenter = (BeiJingRenter) (new Intermediary().newInstance(new BeiJingRenter("zhangsan")));
beiJingRenter.rentHouse();
}catch (Exception e){
e.printStackTrace();
}
}
结果:
实现重点:
1 被代理对象可以被继承,final方法不可以被代理
2 代理对象需要持有被代理对象的引用
3 代理对象需要实现MethodInterceptor 并重写它的intercept方法
原理:
Proxy根据被代理对象生成一个代理类,该类继承了被代理对象,并重写了他的所有方法。
注:可以用过在main方法中加入下面这句话将生成的代理对象cglib的代理类生成到该目录下。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"C://cglib_proxy_classes");
如有有兴趣的话可以看一下生成的代理类。这儿先不做研究。
jdk动态代理与cglib动态代理对比
1.JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象。
2.JDK和CGLib都是在运行期生成字节码, JDK是直接写Class字节码, CGLib使用ASM
框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用, CGLib是通过FastClass机制直接调用方法,
CGLib执行效率更高。