调用者不能直接访问目标对象,可以通过使用代理访问,在代理中创建目标对象并调用其方法,并做增强
静态代理:
角色: 抽象主题(业务接口),具体主题(业务实现),代理
//抽象主题 interface Subject { void Request(); } //真实主题 class RealSubject implements Subject { public void Request() { System.out.println("访问真实主题方法..."); } } //代理 class MyProxy implements Subject { private RealSubject realSubject; public void Request() { if (realSubject == null) { realSubject = new RealSubject(); //在代理中创建对象 } preRequest(); realSubject.Request(); postRequest(); } public void preRequest() { System.out.println("访问真实主题之前的预处理。"); } public void postRequest() { System.out.println("访问真实主题之后的后续处理。"); } }
静态代理的缺点:
- 代理与被代理主题一一对应,被代理主题增加,就必须也要增加代理。
- 在使用代理之前,真实主题就必须存在,扩展不灵活
动态代理:
适用: aop的运用
- 1、JDK动态代理
//抽象主题 interface Subject { void Request(); } //真实主题 class RealSubject implements Subject { public void Request() { System.out.println("访问真实主题方法..."); } } // 代理类: public class MyProxy implements InvocationHandler { private Object target; //被代理类,就是上面的实现类 public MyProxy(Object target) { this.target = target; } public <T> T getProxy() { T proxyObject = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return proxyObject; //该方法生成了一个代理对象,该对象也实现了target实现的接口,代理了target对象。 本例中的target就是RealSubject 实例对象。 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); Object result = method.invoke(target, args); //执行了target中的方法返回的结果 System.out.println("after"); return result; } }
如上在MyProxy中,在运行时根据传入的被代理对象,动态的生成代理对象。 下面来看看底层源代码
原理解析:
生成代理类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
{
if (h == null) { //如果h为空直接抛出异常,所以InvocationHandler实例对象是必须的,因为在被拦截时需要执行该 handler的invoke方法
throw new NullPointerException();
}
//拷贝真实主题实现的所有接口
final Class<?>[] intfs = interfaces.clone();
//一些安全的权限检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//获得代理类的字节码-此时代理类中有了被代理类的所有方法
Class<?> cl = getProxyClass0(loader, intfs);
//获取代理类的构造函数,,其中参数constructorParames为常量值-就是装InvocationHandler的一个Class数组:
private static final Class<?>[] constructorParams = { InvocationHandler.class };
final Constructor<?> cons = cl.getConstructor(constructorParames);
final InvocationHandler ih = h;
//使用构造函数来创建代理对象-这一步将代理类与InvocationHandler联系在一起,真正执行方法时候,就会通过InvocationHandler中的invoke方法来执行了,而我们的切入逻辑就在invoke方法中。
return newInstance(cons, ih);
}
上面getProxyClass0又是怎样获得代理类的字节码呢?
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
//被代理接口数不得超过65535个
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//代理类缓存,如果缓存中有代理类了直接返回,否则将由ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
如果真实主题实现了多个接口,是如何把所有方法加入到代理类中呢?如果有相同方法呢?
下面是调用过程中部分代码:
//获得所有接口中的所有方法,并将方法添加到代理方法中
for(var1 = 0; var1 < this.interfaces.length; ++var1) {
Method[] var2 = this.interfaces[var1].getMethods();
for(var3 = 0; var3 < var2.length; ++var3) {
//这里把所有接口中的所有方法都加入到代理类中,如果有相同的方法呢,看第4步
this.addProxyMethod(var2[var3], this.interfaces[var1]);
}
}
Iterator var7 = this.proxyMethods.values().iterator();
List var8;
while(var7.hasNext()) {
var8 = (List)var7.next();
checkReturnTypes(var8); //验证具有相同方法签名的的方法的返回值类型是否一致,因为不可能有两个方法名相同,参数相同,而返回值却不同的方法
};
如果接口有相同的方法,加入代理类时会如何?
private void addProxyMethod(Method var1, Class var2) {
String var3 = var1.getName(); //方法名
Class[] var4 = var1.getParameterTypes(); //方法参数类型数组
Class var5 = var1.getReturnType(); //返回值类型
Class[] var6 = var1.getExceptionTypes(); //异常类型
String var7 = var3 + getParameterDescriptors(var4); //方法签名,确定方法唯一
Object var8 = (List)this.proxyMethods.get(var7); //根据方法签名却获得代理中的该方法
if(var8 != null) { //如果加入的方法在代理类已经有了,但是可能异常处理不一样,进行统一归约处理-我理解就是使用这些方法中最高的异常级别
Iterator var9 = ((List)var8).iterator();
while(var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
if(var5 == var10.returnType) {
//这里处理异常
ArrayList var11 = new ArrayList();
collectCompatibleTypes(var6, var10.exceptionTypes, var11);
collectCompatibleTypes(var10.exceptionTypes, var6, var11);
var10.exceptionTypes = new Class[var11.size()];
//将ArrayList转换为Class对象数组
var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
return;
}
}
} else { //如果加入的方法在代理类中还没有,就加入
var8 = new ArrayList(3);
this.proxyMethods.put(var7, var8);
}
((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
/*24~27行的意思就是如果var8为空,就创建一个数组,并以方法签名为key,proxymethod对象数组为value添加到proxyMethods*/
}
- 2、cglib代理
//抽象主题
interface Subject {
void request();
}//真实主题
class RealSubject implements Subject {
public void request() {
System.out.println("访问真实主题方法...");
}
}//实现MethodInterceptor接口,定义方法拦截器
public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method,Object[] arg,MethodProxy proxy)throws Throwable{ System.out.println("before:"); Object object = proxy.invokeSuper(obj,arg); System.out.println("After:"); return object; } }
//调用
public static void main(String[] args) {
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback(new MyMethodInterceptor()); Subject subject = (RealSubject)enhancer.create(); subject.request(); }