代理模式(Proxy)
特点
对于被代理人来说, 这件事情是一定要做的, 但是我自己又不想做或者没有时间做。
对于代理人而言, 需要获取到被代理的人个人资料,并且只参与整个过程的某个或几个环节。
应用场景
为其他对象提供一种代理以控制对这个对象的访问。
从结构上来看和 Decorator 模式类似,但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。Spring 的 Proxy 模式在 AOP 中有体现,比如JdkDynamicAopProxy 和 Cglib2AopProxy。
穷举:租房中介、 售票黄牛、 婚介、经纪人、 快递、 事务代理、 非侵入式日志监听
静态代理
在代理之前,所有东西必须都是已知的(代理过程需要人工完成)。上代码:
//通用接口:人
public interface Person {
public void findLove();
public void findJob();
//......
}
//被代理类:儿子
public class Son implements Person{
public void findLove(){
//找对象的要求
System.out.println("找对象,肤白貌美大长腿");
}
@Override
public void findJob() {
}
}
//代理类:父亲
public class Father {
private Person person;
public Father(Person person){
this.person = person;
}
//手动完成代理过程
public void findLove(){
System.out.println("根据你的要求物色");
this.person.findLove();
System.out.println("双方父母是不是同意");
}
}
//测试类
public class StaticProxyTest {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
输出结果:
上述代码描述的场景,是父亲帮儿子找对象的场景。相当于代理模式,通过静态代理,我们需要在父亲对象中手动写findLove(),完成代理过程。缺点:很麻烦,不够智能化。
动态代理
在代理之前,所有东西都是未知的。(自动化,智能化)。
jdk动态代理
被代理类需要实现接口,代理类通过实现相同的接口,进行动态代理。上代码:
//场景:找工作
//被代理类 Jeremy
public class Jeremy implements Person{
public void findLove(){
System.out.println("高富帅");
System.out.println("身高180cm");
System.out.println("胸大,6块腹肌");
}
@Override
public void findJob() {
System.out.println("月薪20K-50k");
System.out.println("找工作");
}
}
//代理类 58同城
public class JDK58 implements InvocationHandler{
//被代理的对象,把引用给保存下来
private Person target;
public Object getInstance(Person target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
//稍后深入底层来给大家讲解字节码是如何重组的
//用来生成一个新的对象(字节码重组来实现)
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是58:我要给你找工作,现在已经拿到你的简历");
System.out.println("开始投递");
method.invoke(this.target,args);
System.out.println("安排面试");
return null;
}
}
//测试类
public class JDKProxyTest {
public static void main(String[] args) {
try {
Person obj = (Person)new JDK58().getInstance(new Jeremy());
System.out.println(obj.getClass());
obj.findJob();
//JDK中有个规范,只要要是$开头的一般都是自动生成的
//通过反编译工具可以查看源代码
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
FileOutputStream os = new FileOutputStream("D://$Proxy0.class");
os.write(bytes);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:
深入分析
反编译后的$Proxy0.class文件:
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
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 void findLove() throws {
try {
super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void findJob() throws {
try {
super.h.invoke(this, m0, (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, m3, (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("com.jeremy.www.pattern.proxy.staticed.Person").getMethod("findLove");
m4 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("com.jeremy.www.pattern.proxy.staticed.Person").getMethod("findJob");
m3 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
源码分析可以参照代理模式源码分析,讲的不错。以后在jdk源码分析中,我也会写自己的文章,到时再修改链接。
jdk动态代理是通过字节码重组来实现的。
总结下代码中字节码重组的步骤:
- 拿到被代理对象的引用,并且获取到它的所有的接口,反射获取;
- JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口;
- 动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现);
- 编译新生成的Java代码.class
- 再重新加载到JVM中运行
cglib动态代理
cglib代理类通过继承被代理类,达到代理的效果。
//被代理人:张三 代理业务:找对象
public class ZhangSan {
public void findLove(){
System.out.println("肤白貌美大象腿");
}
}
//代理人:媒婆
public class CglibMeipo implements MethodInterceptor{
public Object getInstance(Class<?> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
//要把哪个设置为即将生成的新类父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//业务的增强
System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
System.out.println("开始物色");
methodProxy.invokeSuper(o,objects);
System.out.println("如果合适的话,就准备办事");
return null;
}
}
//测试类
public class CglibTest {
public static void main(String[] args) {
try {
ZhangSan obj = (ZhangSan)new CglibMeipo().getInstance(ZhangSan.class);
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果: