一 Proxy 代理
代理:字面上讲,在别人的授权范围内,代别人处理。也就是说,不直接和你要访问的人或者对象打交道,而是和他的代理人或者代理对象打交道。比如说常见的代理服务器和微商代理。用代理服务器举例,你访问代理服务器,代理服务器再从真实服务器获取数据或者资源返给你。这期间,你是不知道代理服务器背后的真实服务器的,所以你也不会真实服务器进行交互。这样做的好处有利于保护真实服务器的安全。
代理模式:为真实对象提供一个代理,然后外部通过访问代理对象,从而实现对真实对象的访问。
代理分类:
虚拟代理:根据需要创建开销很大的对象,在对象只有在需要的时候才会被真正创建。
保护代理:控制对真实对象的保护。
根据代理类是否是在程序运行过程中产生的,区分为静态代理和动态代理
二 静态代理
静态代理:代理类在程序运行之前就已经存在了,即我们手动编写的代理类。
特点:
# 既然代理类要进行代理真实对象,就需要保持和被代理对象(真实对象)相同的接口
# 代理类需要持有被代理,在构造代理类的时候,就需要指定他代理的是谁,真正的操作还是北代理对象去做,代理类也就做一些辅助性质的工作
public interface Subject {
void execute();
void invoke();
}
public class RealSubject implements Subject {
public void execute() {
System.out.println("执行execute方法...");
}
public void invoke() {
System.out.println("执行invoke方法...");
}
}
public class ProxySubject implements Subject {
private RealSubject real;
public ProxySubject(RealSubject real) {
this.real = real;
}
public void execute() {
this.real.execute();
}
public void invoke() {
this.real.invoke();
}
}
三 动态代理
动态代理:当代理类需要程序的运行过程中才能产生,这个代理就属于动态代理,常见的动态代理:
JDK 动态代理:只能代理实现了某接口的类,如果是继承的父类的方式,则不能代理
CGLib动态代理:可以对继承的类进行代理的方式,这种方式不是JDK自带的,是属于第三方,内部封装了一个java字节码生成框架ASM
3.1 JDK动态代理
public interface Subject {
void execute();
void invoke();
}
public class RealSubject implements Subject {
public void execute() {
System.out.println("执行execute方法...");
}
public void invoke() {
System.out.println("执行invoke方法...");
}
}
public class DynamicProxyFactory implements InvocationHandler {
private Object target;
private Object getProxyInterface(Object target) {
this.target = target;
ClassLoader classLoader = this.target.getClass().getClassLoader();
Class<?>[] interfaces = this.getClass().getInterfaces();
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, this);
return proxy;
}
/**
* 之所以可以被调用,是因为产生的代理类$proxy0实现了InvocationHandler和继承了Proxy
* 那么$Proxy中代理的接口方法,会先调用父类的Proxy的InvocationHandler接口的invoke方法
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this.target, args);
}
}
特点:
# 代理需要实现InvocationHandler接口,因为代理类封装后的接口方法会调用InvocationHandler的invoke方法
# 通过Proxy.newProxyInstance方法产生代理实例
# 只能代理实现了某个接口的类
内部原理:
其实就是通过反射:构造器调用newInstance方法,创建代理实例
3.2 CGLib动态代理
CGLib:Code Generation Library的缩写。它的底层通过字节码处理框架ASM,可以直接产生二进制 class文件,也可以在类被加载到JAVA虚拟机以前,改变类行为。
3.2.1 创建代理
CGLib使用Enhancer创建代理,创建代理有2种方式:
方式一:使用Enhancer对象来创建
/** 通过Enhancer的对象来创建动态代理类 */
public class CGLibProxy2 implements MethodInterceptor {
private static final Logger logger = Logger.getLogger(CGLibProxy2.class);
public Object getProxyInstance(Object obj) {
return Enhancer.create(obj.getClass(), this);
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T)enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
logger.info("调用CGLibProxy2方法:"+methodProxy.getSignature().getName());
Object result = methodProxy.invokeSuper(obj, args);
logger.info("结束CGLibProxy2方法:"+methodProxy.getSignature().getName());
return result;
}
}
方式二:使用Enhancer的静态方法创建,底层还是通过第一种方式创建的。
/** 直接用Enhancer的静态方法生成 */
public class CGLibProxy1 implements MethodInterceptor {
private static final Logger logger = Logger.getLogger(CGLibProxy1.class);
public Object getProxyInstance(Object obj) {
return Enhancer.create(obj.getClass(), this);
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> clazz) {
return (T)Enhancer.create(clazz, this);
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
logger.info("调用CGLibProxy1方法:"+methodProxy.getSignature().getName());
Object result = methodProxy.invokeSuper(obj, args);
logger.info("结束CGLibProxy1方法:"+methodProxy.getSignature().getName());
return result;
}
}
我们需要注意的是:
# 如果被代理的对象的无参构造私有,将无法被代理,因为Enhancer需要根据被代理类无参构造来创建代理
# 如果被代理的类是final,也就是无法继承,或者方法final无法被重写,那么对应的类或者方法也无法被代理(CGLib是基于继承的)
# Enhancer调用create方法创建代理的时候,可以使用无参的create犯法,他创建会根据被代理类的无参构造来创建,比如:
enhancer.create();
如果希望使用待参数的构造方法进行初始化,你可以create方法中指定构造参数类型和参数值。比如:
enhancer.create(new Class<?>[]{String.class,Integer.class}, new Object[]{"nicky",24});
3.2.2 CGLib的拦截器
CGLib除了比JDK更效率之外,他还可以进行过滤,即我可以针对不同方法产生不同的代理效果,换句话说,不同方法交给不同的代理类进行代理。我们可以Enhancer设置CallbackFilter进行过滤,示例代码如下:
public class CGLibProxy3 {
private static Callback[] callbacks = {new CGLibProxy1(), new CGLibProxy2()};
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
// 将回调数组设置到Enhancer,下面会根据算法选择不同的回调,从而针对不同方法获取不同的代理类
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
// 通过过滤算法,返回要使用的代理类的下标,从而使得可以在控制方法选用不同的代理更加方便一点
public int accept(Method method) {
String methodName = method.getName();
return "execute".equals(methodName) ? 1 : 0;
}
});
return (T)enhancer.create();
}
}