在介绍代理模式之前,博主想说点儿“题外”话,博主喜欢玩游戏,特别喜欢玩英雄联盟,尤其喜欢玩压缩,博主的压缩玩的可以说“出神入化”,博主的朋友都叫博主“托儿索”,话说回来,博主的压缩虽然玩的出神入化,特别喜欢浪,但是补刀不咋滴,作为一个程序员,博主决定统计一下楼主的补刀数。
上代码:
public class YaSuo implements Mendable{
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
new YaSuo().mend();
}
}
interface Mendable {
int mend();
}
代码很简单,说这个“YaSuo”,有一个补刀的方法,在随缘的补刀。问题来了,我想记录一下这个“YaSuo”补刀的个数,我想大家已经想出来了,不就是加个日志,打印一下补刀的个数嘛,来上代码:
public class YaSuo implements Mendable{
@Override
public int mend() {
System.out.println("YaSuo killing batman...");
int killNum = new Random().nextInt(100);
System.out.println("YaSuo 10 min mend:" + killNum);
return killNum;
}
public static void main(String[] args) {
new YaSuo().mend();
}
}
interface Mendable {
int mend();
}
来看下结果,博主的随缘补刀大法:
YaSuo killing batman...
YaSuo 10 min mend:67
及格了,博主的补刀能力大增,话说以前都是10 min 补刀 四五十刀。
那么问题又来了,博主不想修改源码或者根本无法修改源码,那怎么办呢 ?
有人说了可以用继承嘛,来上继承:
public class YaSuo implements Mendable {
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
new YaSuo1().mend();
}
}
class YaSuo1 extends YaSuo {
@Override
public int mend() {
System.out.println("YaSuo killing batman...");
int killNum = super.mend();
System.out.println("YaSuo 10 min mend:" + killNum);
return killNum;
}
}
interface Mendable {
int mend();
}
继承虽然可以实现结果,但是继承有缺点,耦合度太高,破坏了封装性,我们应该慎用继承。
那该怎么办呢?回到我们今天的重点:代理模式。
用代理来实现,上代码:
public class YaSuo implements Mendable {
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
new YaSuoProxy(new YaSuo()).mend();
}
}
class YaSuoProxy implements Mendable {
YaSuo yaSuo;
public YaSuoProxy(YaSuo yaSuo) {
this.yaSuo = yaSuo;
}
@Override
public int mend() {
System.out.println("YaSuo killing batman...");
int killNum = yaSuo.mend();
System.out.println("YaSuo 10 min mend:" + killNum);
return killNum;
}
}
interface Mendable {
int mend();
}
这就是静态代理。
那么问题又来了,作为一个好的程序应该具备良好 的扩展性,现在我统计补刀数,那我们还想统计杀人数、助攻数,那怎么办呢?
那我们可以再加一个代理类来代理统计助攻数,代码如下:
public class YaSuo implements Mendable {
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
new YaSuoProxy(new YaSuo()).mend();
}
}
class YaSuoProxy implements Mendable {
YaSuo yaSuo;
public YaSuoProxy(YaSuo yaSuo) {
this.yaSuo = yaSuo;
}
@Override
public int mend() {
System.out.println("YaSuo killing batman...");
int killNum = yaSuo.mend();
System.out.println("YaSuo 10 min mend:" + killNum);
return killNum;
}
}
class YaSuoAssistProxy implements Mendable {
Mendable m;
public YaSuoAssistProxy(Mendable m) {
this.m = m;
}
@Override
public int mend() {
System.out.println("YaSuo assist...");
int killNum = m.mend();
System.out.println("YaSuo assist:" + killNum);
return killNum;
}
}
interface Mendable {
int mend();
}
那我们现在想灵活组合统计补刀数、助攻数,怎么 做呢?来看代码:
public class YaSuo implements Mendable {
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
new YaSuoProxy(new YaSuoAssistProxy(new YaSuo())).mend();
}
}
class YaSuoProxy implements Mendable {
Mendable m;
public YaSuoProxy(Mendable m) {
this.m = m;
}
@Override
public int mend() {
System.out.println("YaSuo killing batman...");
int killNum = m.mend();
System.out.println("YaSuo 10 min mend:" + killNum);
return killNum;
}
}
class YaSuoAssistProxy implements Mendable {
Mendable m;
public YaSuoAssistProxy(Mendable m) {
this.m = m;
}
@Override
public int mend() {
System.out.println("YaSuo assist...");
int killNum = m.mend();
System.out.println("YaSuo assist:" + killNum);
return killNum;
}
}
interface Mendable {
int mend();
}
有没有觉得眼熟呢?不错,就是装饰者模式--Decorator。很像装饰者模式。
作为一个优秀的程序,它不应该只能代理一个方法、一种类型,应该还可以代理任何其他可以代理的类型 Object,该怎么办呢?既然有静态代理,有没有动态代理?回答是肯定的。来看动态代理:
public class YaSuo implements Mendable {
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
YaSuo yaSuo = new YaSuo();
Mendable m = (Mendable)Proxy.newProxyInstance(YaSuo.class.getClassLoader(),
new Class[]{Mendable.class}, (proxy, method, args1) -> {
System.out.println("YaSuo killing batman...");
Object o = method.invoke(yaSuo, args1);
System.out.println("YaSuo 10 min mend:" + o);
return o;
}
);
m.mend();
}
}
interface Mendable {
int mend();
}
我们也可以写成这样:
public class YaSuo implements Mendable {
@Override
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
public static void main(String[] args) {
YaSuo yaSuo = new YaSuo();
Mendable m = (Mendable)Proxy.newProxyInstance(YaSuo.class.getClassLoader(),
new Class[]{Mendable.class}, new MyHandler(yaSuo));
m.mend();
}
}
class MyHandler implements InvocationHandler {
Mendable m;
public MyHandler(Mendable m) {
this.m = m;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("YaSuo killing batman...");
Object o = method.invoke(m,args);
System.out.println("YaSuo 10 min mend:" + o);
return o;
}
}
interface Mendable {
int mend();
}
可以看到代码量很少,我们上面的代码有自己实现的代理类,如:YaSuoProxy、YaSuoAssistProxy,那动态代理有没有实现代理类呢?有的,在内存里,当然我们也可以生成出来。通过以下方式:
public class ProxyTest {
public static void main(String[] args) {
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Mendable.class});
try {
FileOutputStream fileOutputStream = new FileOutputStream("D://$Proxy0.class");
fileOutputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}
生成到D盘。用反编译工具打开来。
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 Mendable {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int mend() throws {
try {
return (Integer)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.wangkai.design.structure.proxy.v4.Mendable").getMethod("mend");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
这就是我们生成的代理类。可以看到把 InvocationHandler 传到了构造函数当中,生成了equals方法、toString方法、hashCode方法。当然必须有我们的mend方法。当我们生成的代理对象$Proxy0调用mend方法的时候,会去调用InvocationHandler的invoke方法,在invoke方法调用了我们被代理类的mend方法,所以我们才可以在调用被代理类的mend方法前后加自己的代码,比如统计接口时间,比如添加权限校验等等一系列操作,这就是jdk动态代理的逻辑。
可以看到的是,jdk动态代理 被代理类是需要实现接口的。
看完动态代理,再看一下cglib代理:
cglib(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库。
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(YaSuo.class);
enhancer.setCallback(new TimeMethodInterceptor());
YaSuo yaSuo = (YaSuo)enhancer.create();
yaSuo.mend();
}
}
class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(o.getClass().getSuperclass().getName());
System.out.println("YaSuo killing batman...");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("YaSuo 10 min mend:" + result);
return result;
}
}
class YaSuo {
public int mend() {
int killNum = new Random().nextInt(100);
return killNum;
}
}
用cglib代理,需要在pom.xml文件加上
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来直接操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。
cglib代理是通过一个Enhancer和一个MethodInterceptor来实现对方法的拦截,这里的TimeMethodInterceptor 相当于jdk动态代理的InvocationHandler,被代理类没有实现任何接口,那么我们怎么知道生成的代理对象要生成哪个方法呢?其实我们生成的代理对象是被代理类的子类,也就是intercept方法中的参数o,我们打印一下o的父类:
com.wangkai.design.structure.proxy.v5.YaSuo
YaSuo killing batman...
YaSuo 10 min mend:49
可以看到生成的代理类的父类是YaSuo这个类。那么现在问题来了,既然生成的代理类是被代理类的子类,那么如果被代理类是final的话,还能用cglib代理生成代理类吗?我们试一下:
可以看到报错了。所以是不能的。
jdk代理和cglib代理都介绍完了,我们来对比一下它们:
1、jdk动态代理是实现了被代理对象的接口,在调用具体方法前调用InvokeHandler来处理。如果被代理对象实现了接口,默认情况下会采用jdk的动态代理实现AOP。
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。并且cglib不可以代理final修饰的类。
2、jdk动态代理是Java自带的,cglib动态代理是第三方jar包提供的。
3、jdk动态代理实现的逻辑是目标类和代理类都实现同一个接口,目标类和代理类是平级的。而cglib动态代理实现的逻辑是继承目标类,是目标类的子类,目标类和代理类是父子继承关系。