1 简单概要
代理模式:指为其他对象提供一种代理来控制对这个对象的访问。
代理模式主要有两个目的:
①保护目标对象;
②增强目标对象。
它的类图如下:
2 分类与详解
2.1 静态代理
举个栗子:儿子正在找对象,而父母希望儿女早点找到另一半,于是在儿子找对象的同时帮他物色:
interface Person{
void findLove();
}
public class Son implements Person {
public void findLove() {
System.out.println("儿子要求:是个女的");
}
}
父亲帮找:
public class Father {
private Son son;
public Father(Son son){
this.son = son;
}
public void findLove(){
System.out.println("父亲帮找");
this.son.findLove();
}
}
Client类:
public class Client {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
运行:
我们现在想让父亲帮表妹也找找,此时代码就需要修改Father类,不符合开闭原则,所以做如下优化:
public class Father {
private Person person;
public Father(Person person){
this.person = person;
}
public void findLove(){
System.out.println("父亲帮找");
this.person.findLove();
}
}
当Father中的Son替换为Person时,我们就可以代理所有实现了Person接口的类,表妹也是人,所以只需要增加一个表妹类,Client传参时传入new BiaoMei()就可以了,此时的代码对修改关闭,对扩展开放,符合开闭原则。
2.2 动态代理
如果父亲不仅要帮儿子找对象,也要帮很多人找对象,也就是媒婆,我们不可能创建上百个想表妹一样的类,所以有没有一种更加通用的解决方案呢?也许动态代理可以帮助我们。
2.2.1 JDK方式实现动态代理(手写JDK动态代理)
创建消费者类Customer(在媒婆处消费):
interface Person{
void findLove();
}
public class Customer implements Person {
public void findLove() {
System.out.println("女的就行");
}
}
创建媒婆类(Meipo):
public class Meipo implements InvocationHandler {
//被代理的对象,保存起来方便invoke中的this.target的调用
private Object target;
public Object getInstance(Object target){
this.target = target;
Class<?> aClass = target.getClass();
//创建代理类并返回
return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
befor();
Object invoke = method.invoke(this.target, args);
after();
return invoke;
}
private void befor(){
System.out.println("媒婆帮找");
}
private void after(){}
}
创建Client类:
public class Client {
public static void main(String[] args) {
Person person = (Person) new Meipo().getInstance(new Customer());
person.findLove();
}
}
运行:
那么动态代理是如何实现的呢?
她其实就是通过字节重组,重新生成对象来代替原始对象,以达到代理的目的。
字节码重组的基本步骤如下:
①获取被代理对象的引用,利用反射获取到它的所有接口;
②JDK动态代理类Proxy重新生成一个新的类,此类要实现刚才获取到的所有接口;
③动态生成新类的Java代码;
④编译.java文件成.class文件;
⑤加载编译好的.class文件。
那么修改Client类,去查看一下生成好的.class文件:
public class Test {
public static void main(String[] args) {
try {
Person person = (Person) new Meipo().getInstance(new Customer());
person.findLove();
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream fos = new FileOutputStream(new File("$Proxy0.class"));
fos.write(bytes);
fos.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
运行:
查看$Proxy0.class($开头的类是动态生成的):
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
......
public final void findLove() throws {
try {
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(