代理模式分为静态代理、JDK 动态代理(接口代理)、CGLIB 动态代理(类代理,无需接口)。
作用:通过代理对象控制对真实对象的访问,在不修改目标类代码的前提下增加日志、权限、事务等附加逻辑。
1. 业务接口 & 真实目标类
// 业务接口
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
// 真实业务实现类(被代理对象)
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("【真实对象】执行新增用户:" + username);
}
@Override
public void deleteUser(String username) {
System.out.println("【真实对象】执行删除用户:" + username);
}
}
2. 静态代理
代理类手动编写,包装目标对象,调用前后做增强。
/**
* 静态代理类,实现相同业务接口
*/
public class UserServiceStaticProxy implements UserService {
// 持有真实目标对象
private final UserService target;
public UserServiceStaticProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String username) {
// 前置增强
System.out.println("【静态代理】前置:记录操作日志");
// 调用真实方法
target.addUser(username);
// 后置增强
System.out.println("【静态代理】后置:操作结束\n");
}
@Override
public void deleteUser(String username) {
System.out.println("【静态代理】前置:权限校验");
target.deleteUser(username);
System.out.println("【静态代理】后置:操作结束\n");
}
}
// 测试静态代理
class StaticProxyTest {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = new UserServiceStaticProxy(target);
proxy.addUser("张三");
proxy.deleteUser("李四");
}
}
缺点:每一个目标类都要手写代理类,接口变更时代理类也要同步修改。
3. JDK 动态代理(基于接口)
java.lang.reflect.Proxy,目标必须实现接口,运行时动态生成代理类字节码。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理调用处理器
*/
public class JdkInvocationHandler implements InvocationHandler {
// 被代理的真实对象
private final Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
/**
* 代理对象调用任何方法,都会进入invoke
* @param proxy 代理对象
* @param method 被调用的方法
* @param args 方法入参
* @return 方法返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK动态代理】前置增强:方法名称=" + method.getName());
// 执行真实对象方法
Object result = method.invoke(target, args);
System.out.println("【JDK动态代理】后置增强\n");
return result;
}
// 获取代理实例
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
}
// JDK动态代理测试
class JdkProxyTest {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
JdkInvocationHandler handler = new JdkInvocationHandler(target);
// 强转为接口类型
UserService proxy = (UserService) handler.getProxyInstance();
proxy.addUser("王五");
proxy.deleteUser("赵六");
}
}
4. CGLIB 动态代理(无需接口,继承目标类)
JDK 代理要求接口;CGLIB 通过继承目标类生成子类做代理,目标类不能是 final。 Spring AOP:有接口用 JDK,无接口用 CGLIB。
需要引入依赖(Maven)
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGLIB 方法拦截器
*/
public class CglibMethodInterceptor implements MethodInterceptor {
private final Object target;
public CglibMethodInterceptor(Object target) {
this.target = target;
}
/**
* 所有方法调用都会进入intercept
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("【CGLIB代理】前置增强:" + method.getName());
// 调用父类(真实对象)方法
Object res = proxy.invokeSuper(obj, args);
System.out.println("【CGLIB代理】后置增强\n");
return res;
}
// 创建CGLIB代理对象
public Object getCglibProxy() {
Enhancer enhancer = new Enhancer();
// 设置父类(被代理类)
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
}
// CGLIB测试
class CglibProxyTest {
public static void main(String[] args) {
// 不需要实现接口的普通类也可以代理
UserServiceImpl target = new UserServiceImpl();
CglibMethodInterceptor interceptor = new CglibMethodInterceptor(target);
UserServiceImpl proxy = (UserServiceImpl) interceptor.getCglibProxy();
proxy.addUser("小明");
proxy.deleteUser("小红");
}
}
三种代理对比
Spring AOP 默认逻辑:
如果目标对象实现接口,使用 JDK 动态代理;
如果目标没有实现接口,使用 CGLIB;
SpringBoot 可以配置强制使用 CGLIB。
本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。
原文链接
欢迎访问