代理模式
我们使用代理对象来代替真实对象的访问,这样可以在不修改原目标对象的前提下,提供额外的功能,扩展目标对象的功能。
代理模式的主要作用是扩展目标对象的功能。比如说在目标对象的某个方法的执行前后增加自定义操作。
静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的,不灵活且麻烦。
实现步骤: 1、定义一个接口及其实现类;
public interface SmsService {
String send(String message);
}
2、创建一个代理类同样实现这个接口
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
3、将目标对象注入进代理类
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method send()");
smsService.send(message);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method send()");
return null;
}
}
实际使用:
public class Main {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsProxy smsProxy = new SmsProxy(smsService);
smsProxy.send("java");
}
}
其中,java的代理类通过接受一个接口的实现类进一步封装体现了java的多态。
动态代理
相比静态代理来说,动态代理更加灵活。不需要每个目标类都创建一个代理类,也不需要必须实现接口,我们呢可以直接代理实现类(CGLIB动态代理机制)
从JVM角度说,动态代理是在运行时动态生成类字节码,并加载到JVM中。
Spring AOP、RPC框架都依赖了动态代理。
JAVA动态代理用两种: 一是JDK动态代理,通过反射机制实现; 优点:基于接口易于理解。 缺点:只能代理实现类接口的类。 二是CGLIB动态代理,实现方式是字节码技术生成子类。 优点:不需要代理类必须实现接口,更加灵活。 缺点:基于字节码,比JDK动态代理性能开销大。
相比来说,CGLIB比JDK动态代理有更高的初始化开销,调用开销相差不大。
JDK动态代理
在Java动态代理机制中InvocationHandler接口和Proxy类是核心 Proxy类中使用频率最高的方法是: newProxyInstance(),这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgmentException
{
...
}
该方法有三个参数 loader 类加载器,用于加载代理对象 interfaces 被代理类实现的一些接口 h 实现类InvocationHandler接口的对象
要实现动态代理,还必须实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。
public interface InvocationHandler {
/**
* 当你使用代理对象调用方法的时候实际会调用到这个方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
{
...
}
}
invoke()有三个参数: proxy:动态生成的代理类 method:与代理类对象调用方法相对应 args:当前method方法参数 通过Proxy类的newProxyInstance()创建的代理对象在调用方法的时候,是机会调用实现InvocationHandler接口的类的invoke()方法。可以在invoke()方法中自定义处理逻辑。
JDK动态代理类使用步骤 1、定义一个接口及其实现类;
public interface SmsService {
String send(String message);
}
2、自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法并自定义一些处理逻辑;
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
3、定义一个JDK动态代理类,通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author shuang.kou
* @createTime 2020年05月11日 11:23:00
*/
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return result;
}
}
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new DebugInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}
}
实际使用:
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");
CGLIB动态代理机制
JDK动态代理致命问题在于只能代理实现了接口的类。
CGLIB使用步骤 1、定义一个类
package github.javaguide.dynamicProxy.cglibDynamicProxy;
public class AliSmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
2、自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定义MethodInterceptor
*/
public class DebugMethodInterceptor implements MethodInterceptor {
/**
* @param o 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return object;
}
}
3、通过 Enhancer 类的 create()创建代理类;
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new DebugMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
实际使用:
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");