定义:使用一个代理将对象包装起来,然后用该代理对象取代原始对象;任何对原始对象的调用都要通过代理;代理对象决定是否以及何时将方法调用到原始对象上。
使用代理模式的目的
- 保护目标对象。
- 增强目标对象。before()、after()
- 降低了系统的耦合度。
弊端:降低了访问效率 。
可通过加缓存缓解上述弊端:缓存已处理过的业务,当来了一个新的业务访问时,先看缓存中有没有,没有再调用被代理类的方法。
静态代理
代理类和被代理类在编译器就被确定。
public class MyTest {
public static void main(String[] args) {
NikeClothFactory nikeClothFactory = new NikeClothFactory();
MyProxy myProxy = new MyProxy(nikeClothFactory);
myProxy.produceCloth();
/**
* 运行结果
* 方法调用前
* 我是被代理类
* 方法调用后
*/
}
}
//代理类
class MyProxy implements ClothFactory{
private ClothFactory factory;
public MyProxy(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth() {
before();
factory.produceCloth();
after();
}
public void before(){
System.out.println("方法调用前");
}
public void after(){
System.out.println("方法调用后");
}
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("我是被代理类");
}
}
interface ClothFactory{
void produceCloth();
}
动态代理
- 根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
- 通过代理对象调用的方法,动态的去调用被代理类中的同名方法。
JDK动态代理
必须有接口。
实现步骤:
- 业务接口。
- 实现接口的被代理类。
- 实现InvocationHandler接口的代理类,重写invoke(...)方法。
- 通过Prox.newProxyInstance(...)获取代理类。
代码实现 :
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class a {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
MyProxy my = new MyProxy();
my.bindObject(superMan);
//Proxy.newProxyInstance 传入被代理类的类加载器、被代理类实现的接口、动态代理方法在执行时,会调用InvocationHandler里面的invoke方法去执行
Object o = (HUman)Proxy.newProxyInstance(superMan.getClass().getClassLoader(), superMan.getClass().getInterfaces(), my);
String out = ((HUman) o).Belief();
System.out.println(out);
}
}
class MyProxy implements InvocationHandler{
private Object obj;
public void bindObject(Object obj){
this.obj = obj;
}
@Override
//invoke(Object proxy, Method method, Object[] args) 传入代理的对象、要调用的方法、以及传入方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValues = method.invoke(obj,args);
return returnValues;
}
}
//被代理类
class SuperMan implements HUman{
@Override
public String Belief() {
return "fly";
}
}
//接口
interface HUman{
String Belief();
}
原理解析:
1.从Proxy.newProxyInstance(...)方法入手:
2.getProxyClass0()方法
3.WeakCache中的get方法,也就是上面proxyClassCache.get()方法。
先说说这个里面的缓存结构。
map: 一级缓存;其中key存储的的由被代理类的类加载器决定的一个CacheKey对象,value存储的是二级缓存map对象(在源码中这个二级缓存对象的变量名叫做valueMap)。
subKeyFactory:二级缓存的key;存储被代理的类加载器和其实现接口的一个标识符。
valueFactory:二级缓存的value;存储对应的代理类工厂。
4.ProxyClassFactory创建代理类
流程梳理
首先从Proxy.newProxyInstance()方法开始;
1. 在这个方法里面首先调用一个名为getProxyClass0()的静态方法,这个静态方法的作用就是查找或生成指定的代理类。若缓存中有所需的代理类,直接获取,没有则创建。
具体怎么获取或者创建呢?
getProxyClass0()的返回值是proxyClassCache.get()方法的返回值。
proxyClassCache.get() 中有两个缓存,
一级缓存;key存储的的由被代理类的类加载器决定的一个CacheKey对象;value存储的是二级缓存map对象。(用来区分classLoader)
二级缓存:key存储被代理的类加载器和其实现接口的一个标识符;value存储对应的代理类工厂。(用来区分实现的接口)
1.1在这个方法中首先根据代理类的类加载器获取一级缓存中的value对象(也就是二级缓存)。
如果得到二级缓存为空,就创建一个二级缓存(此时它里面为空)存入到一级缓存中;
若不为空直接获取。
1.2根据被代理类实现的接口数组生成对应的二级缓存的key(key0,key1...) 。
1.3根据这个key在二级缓存中获取对应代理类工厂。
若这个代理类工厂存在,进行线程是否安全验证之后,直接返回缓存值。
若这个代理类工厂不存在,先创建一个factory对象,此时里面还没有代理类。然后调用ProxyClassCache中apply方法。创建代理对象。
ProxyClassCache创建代理类过程:
a.判断被代理类是否实现接口。
b.判断当前接口是否重复。
c.定义生成代理类访问权限--public final。
d.看接口的访问权限是否为public,如果是,生成的代理类和他放在同一个包下;如果不是放在com.sun.proxy包下。
e.创建代理类名称。
f.生成代理类字节码文件。(生成的代理类继承了Proxy,实现了传入的业务接口)
g.加载字节码文件,生成指定代理类Class对象。
获取到代理类class对象后,通过 有参构造,将实现了InvocationHandler接口的对象当作参数创建代理类对象。
当我们通过代理类调用代理业务时,代理类会自动帮我们调用InvocationHandler中的invoke()方法。