何为代理模式(Proxy Pattern)
程序设计中的一种设计模式
角色:抽象角色;代理角色;真实角色
优点:中介隔离;职责清晰;高可扩展;开闭原则
分类:静态代理(Static Proxy);动态代理(Dynamic Proxy)
静态代理(Static Proxy)
优点:简单
缺点:需要创建太多代理类,工作量太大,接口变化,代理类也要修改。
Subject
package com.zlj.test.proxy.statics;
public interface Subject {
boolean takeCar();
}
RealSubject
package com.zlj.test.proxy.statics;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class RealSubject implements Subject {
@Override
public boolean takeCar() {
try {
TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextLong(100l, 200l));
if (ThreadLocalRandom.current().nextBoolean()) {
System.out.println("take a fast car");
return true;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
}
ProxySubject
package com.zlj.test.proxy.statics;
import java.time.LocalDateTime;
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public boolean takeCar() {
LocalDateTime start = LocalDateTime.now();
System.out.println(String.format("take car start:%s", start));
boolean result = subject.takeCar();
LocalDateTime end = LocalDateTime.now();
System.out.println(String.format("take car end: %s, result: %b ", end, result));
return result;
}
}
Client
package com.zlj.test.proxy.statics;
public class Client {
public static void main(String[] args) {
Subject real = new RealSubject();
Subject proxy = new ProxySubject(real);
proxy.takeCar();
}
}
JDK 动态代理(JDK Dynamic Proxy)
JDK动态代理基于Java反射机制实现,主要涉及一下两个类:
java.lang.reflect.InvocationHandler
java.lang.reflect.Proxy
InvocationHandler是一个接口,通过实现这个接口定义一个横切的逻辑!然后通过反射机制调用目标类的方法,这样就能动态的把非业务逻辑和业务逻辑动态的拼接在一起!
Proxy则利用InvocationHandler创建代理实例,来间接的调用代理的方法!
注:JDK 动态代理需要基于接口实现,被代理的对象必须有接口。
package com.zlj.test.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKDynamicProxy implements InvocationHandler {
private Object delegate;
public JDKDynamicProxy(Object delegate) {
this.delegate = delegate;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy start");
Object result = method.invoke(delegate, args);
System.out.println("proxy end");
return result;
}
public Object proxy(Class clazz) {
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
}
}
Proxy.newProxyInstance JDK1.8内部实现
@CallSensitive 注解,参考https://blog.csdn.net/HEL_WOR/article/details/50199797
- SecurityManager
每个Java应用都可以有自己的安全管理器,它是防范恶意攻击的主要安全卫士。
自己写应用,一般不会开启,此处校验不会执行。
https://nicky-chen.github.io/2018/07/13/java-securitymanager/
checkProxyAccess 检查创建一个proxy class 需要的权限。
Reflection.getCallerClass()返回调用者的class
判断 CallerClass 的ClassLoader 和 interface的ClassLoader
判断interface 的package 权限 - Class<?> cl = getProxyClass0(loader, intfs) 获取代理类
先从缓存中获取,缓存中没有则通过ProxyClassFactory生成并缓存。private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { ...... return proxyClassCache.get(loader, interfaces); } public V get(K key, P parameter) { Objects.requireNonNull(parameter); expungeStaleEntries(); Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); ...... Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value; } } if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } ...... supplier = factory; ...... } } /** * Proxy 类 * a cache of proxy classes */ private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // prefix for all proxy class names private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { ...... String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }
代理类的前缀 private static final String proxyClassNamePrefix = “$Proxy”;
代理类序号 private static final AtomicLong nextUniqueNumber = new AtomicLong();
生成代理类class文件 class ProxyGenerator.generateProxyClass
加载并返回代理类 private static native Class<?> defineClass0(…) - checkNewProxyPermission(Reflection.getCallerClass(), cl);
如果开启SecurityManager 需要验证生成的proxy class 权限。 - 生成代理类的构造器,参数为InvocationHandler
如果代理类的修饰符非public时需要设置 accessible 为 true
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
值为 false 则指示反射的对象应该实施 Java 语言访问检查。
实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 。
由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的。
扩展 AccessController https://www.jianshu.com/p/81985bc2bfa3 - 根据构造器生成代理类对象
return cons.newInstance(new Object[]{h});
h为自己实现的InvocationHandler子类的实例。
CGLIB(Code Generator Library )
CGLIB基于继承来实现代理,他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。
但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。
maven 依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
主要涉及的类:
net.sf.cglib.proxy.MethodInterceptor
net.sf.cglib.proxy.Enhancer
package com.zlj.test.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
public class CGLIBMain {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("cglib proxy start");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("cglib proxy end");
return result;
});
Hello hello = (Hello) enhancer.create();
hello.say("zlj");
}
}