设计模式之代理模式
-
简介
在某些场景下,我们需要增强某个对象的使用,比如我们在执行某个方法前加输出一条日志,但是我们不能直接改这个类,我们可以用代理对象来实现这个功能 -
模式应用
Spring AOP/日志/事务 等 -
类图
-
源码实现
静态代理
jdk动态代理
点击查看代码
package designpattern.proxy.jdk_proxy;
public interface SellInterface {
void sell();
}
点击查看代码
package designpattern.proxy.jdk_proxy;
public class Sell implements SellInterface {
@Override
public void sell() {
System.out.println("sell");
}
}
点击查看代码
package designpattern.proxy.jdk_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Sell sell = new Sell();
// Proxy.newProxyInstance(Sell.class.getClassLoader(),Sell.class.getInterfaces(),);
SellInterface obj = (SellInterface)Proxy.newProxyInstance(sell.getClass().getClassLoader(), sell.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke...");
return method.invoke(sell,args);
}
});
System.out.println(obj.getClass());
obj.sell();
// System.out.println(Sell.class == sell.getClass());
while (true){
}
}
}
使用arthas工具查看动态代理类的源码
选择进程7
用sc命令查询jvm已加载的类
我们程序Client后运行输出的代理类名称是 com.sun.proxy.$Proxy0,在这个地方
用jad命令反编译,得到源码
源码如下:
点击查看代码
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* designpattern.proxy.jdk_proxy.SellInterface
*/
package com.sun.proxy;
import designpattern.proxy.jdk_proxy.SellInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0
extends Proxy
implements SellInterface {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sell() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("designpattern.proxy.jdk_proxy.SellInterface").getMethod("sell", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
稍微整理下源码,把toString hashCode equals这些方法删掉
点击查看代码
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* designpattern.proxy.jdk_proxy.SellInterface
*/
package com.sun.proxy;
import designpattern.proxy.jdk_proxy.SellInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0
extends Proxy
implements SellInterface {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final void sell() {
this.h.invoke(this, m3, null);
return;
}
static {
m3 = Class.forName("designpattern.proxy.jdk_proxy.SellInterface").getMethod("sell", new Class[0]);
return;
}
}
接下我们使用另一种方式实现动态代理:cglib
项目加入依赖:
点击查看代码
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
点击查看代码
package designpattern.proxy.cglib_proxy;
public class Sell {
public void sell() {
System.out.println("sell");
}
}
测试程序:
点击查看代码
package designpattern.proxy.cglib_proxy;
import designpattern.proxy.static_proxy.Proxy;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Client implements MethodInterceptor {
//被代理的对象
private Sell target = new Sell();
public static void main(String[] args) {
Client client = new Client();
//cglib 对象 类型jdk的Proxy类
Enhancer enhancer = new Enhancer();
//设置要被代理的类
enhancer.setSuperclass(Sell.class);
enhancer.setCallback(client);
//创建代理对象
Sell sell = (Sell) enhancer.create();
System.out.println(sell.getClass());
sell.sell();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理执行中...");
method.invoke(target,objects);
return null;
}
}
对比分析:
jdk动态代理需要被代理的类实现接口,jdk的代理对象实现了这个接口,jdk方式只能代理接口中有的方法
cglib动态代理是基于继承的方式,代理对象继承了被代理对象,如果类final修饰将不能被代理,如果方法是final修饰,将不执行代理对象的增强功能