核心思想:使用代理对象来替代原始对象完成对象访问,这样就可以在不修改原始对象代码的前提下,拓展原始对象的额外功能。
1. 静态代理
静态代理实现步骤:
- 定义一个接口和实现类。
- 定义一个代理类也实现该接口。
- 将目标对象注入到代理类中,然后在代理类的方法中调用对应目标类中的方法。
例子:
定义发短信的接口及其实现类
public interface SmsService { String send(String message); } public class SmsServiceImpl implements SmsService { @Override public String send(String message) { System.out.println("send message:" + message); return message; } }
创建代理类
public class SmsProxy implements SmsService { private final SmsServiceImpl smsServiceImpl; 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"); } } // 输出 // before method send() // send message:java // after method send()
缺点:
- 接口每新增一个接口,对应的目标类和代理类都需要修改。
- 需要对每个目标类都单独写一个代理类。
2. 动态代理
在运行时利用反射机制动态生成类字节码,并加载到JVM中的。
2.1 JDK动态代理
核心: InvocationHandler接口和Proxy类
JDK动态代理实现步骤:
- 定义一个接口及其实现类。
- 自定义
InvocationHandler
并重写invoke
方法,在invoke
方法中会调用原生方法并进行拓展逻辑处理。 - 通过
Proxy.newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler h)
方法创建代理对象。
例子:
定义一个接口及其实现类。
public interface SmsService { String send(String message); } public class SmsServiceImpl implements SmsService { @Override public String send(String message) { System.out.println("send message:" + message); return message; } }
定义JDK动态代理类
public class DebugInvocationHandler implements InvocationHandler { /** * 代理类中的真实对象 */ private final Object target; public DebugInvocationHandler(Object target) { this.target = target; } // invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。 @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; } }
获取代理对象的工厂,使用Proxy.newProxyInstance()方法创建代理对象
public class JdkProxyFactory { public static Object getProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目标类的类加载 target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个 new DebugInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler ); } }
使用Proxy.newProxyInstance()方法创建代理对象
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl()); smsService.send("java"); // 输出 // before method send // send message:java // after method send
缺点:
- 只能代理实现了接口的类。
2.2 cglib动态代理
**核心:通过继承的方式实现代理,MethodIntercept接口和Enhancer类。
cglib动态代理实现步骤:
- 定义一个类。
- 自定义
MethodIntercept
并重写intercept
方法,intercept
用于拦截原生类的方法,类似于JDK动态代理中的invoke
。 - 通过
Enhancer
的create()
创建代理对象。
例子:
定义一个类
public class AliSmsService { public String send(String message) { System.out.println("send message:" + message); return message; } }
自定义MethodIntercept并重写intercept方法
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; } }
通过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"); // 输出 // before method send // send message:java // after method send
2.3 JDK动态代理和cglib动态代理区别
- JDK动态代理只能代理实现了接口的类,而cglib可以代理未实现任何接口的类。cglib通过生成原生类的子类来拦截被代理类的的方法调用,因此cglib不能代理被final修饰的类和方法。
- jdk效率更高。SpringAOP中如果原生类实现了接口,默认使用JDK动态代理,否则使用cglib。
参考文献:
https://snailclimb.gitee.io/j...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。