代理模式
代理模式
定义
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。
代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
Demo
只用最基础的IoC容器,不用AOP注解,直接用ProxyFactory手动创建代理
我们要代理一个 UserService,在方法执行前后打印日志。
1. 定义接口和实现类
public interface UserService {
void createUser(String name);
}
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void createUser(String name) {
System.out.println("【UserServiceImpl】Creating user: " + name);
}
}
2. 创建一个拦截器(MethodInterceptor)
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("【LogMethodInterceptor】Before method: " + invocation.getMethod().getName());
Object result = invocation.proceed(); // 调用目标方法
System.out.println("【LogMethodInterceptor】After method: " + invocation.getMethod().getName());
return result;
}
}
3. 配置代理对象(用ProxyFactory)
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ProxyConfig {
@Bean(name = "userServiceProxy")
public UserService userServiceProxy(UserServiceImpl userServiceImpl) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(userServiceImpl);
proxyFactory.addAdvice(new LogMethodInterceptor());
return (UserService) proxyFactory.getProxy();
}
}
4. 测试
import jakarta.annotation.Resource;
import org.example.design_pattern.Proxy.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DesignPatternApplicationTests {
@Resource(name = "userServiceProxy")
private UserService userService;
@Test
public void testProxy() {
userService.createUser("Mike");
}
}
5. 输出
【LogMethodInterceptor】Before method: createUser
【UserServiceImpl】Creating user: Mike
【LogMethodInterceptor】After method: createUser
JDK动态代理的写法
接口和实现类不变
1. 写代理处理器(InvocationHandler)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogInvocationHandler implements InvocationHandler {
private final Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK Proxy】Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("【JDK Proxy】After method: " + method.getName());
return result;
}
}
2. 测试
import org.example.design_pattern.Proxy.LogInvocationHandler;
import org.example.design_pattern.Proxy.UserService;
import org.example.design_pattern.Proxy.UserServiceImpl;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Proxy;
class DesignPatternApplicationTests {
@Test
public void testProxy() {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[]{UserService.class},
new LogInvocationHandler(target)
);
proxy.createUser("Mike");
}
}
3. 结果
【JDK Proxy】Before method: createUser
【UserServiceImpl】Creating user: Mike
【JDK Proxy】After method: createUser
CGLIB动态代理写法
接口和实现类不变
1. CGLIB 需要依赖库(如果是 Spring Boot 项目已经自带了cglib),或者你手动加
<!-- Maven依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2.定义目标类(不需要接口)
public class UserService {
public void createUser(String name) {
System.out.println("【UserServiceImpl】Creating user: " + name);
}
}
3.写MethodInterceptor(CGLIB拦截器)
如果JDK大于9 启动的时候加上
--add-opens java.base/java.lang=ALL-UNNAMED
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("【CGLIB Proxy】Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("【CGLIB Proxy】After method: " + method.getName());
return result;
}
}
4. 输出
【CGLIB Proxy】Before method: createUser
【UserServiceImpl】Creating user: Mike
【CGLIB Proxy】After method: createUser
项目 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
是否需要接口 | 必须有接口 | 不需要接口 |
代理方式 | 基于接口生成 | 基于继承生成子类 |
Spring AOP 默认选择 | 有接口时用JDK | 没接口时用CGLIB |
性能 | JDK Proxy稍快(接口调用) | 早期较慢,现在优化后差不多 |
小结一下:
-
CGLIB 是通过生成 被代理类的子类 来实现代理功能的,使用 ASM 字节码技术直接操作字节码生成新的 class 文件。
-
为什么不需要接口?
因为它不靠接口,而是通过继承目标类来创建代理对象。它会生成一个字节码类 TargetClass$$EnhancerByCGLIB,并在里面对方法做增强处理。 -
JDK 动态代理依赖接口,是基于接口来生成代理类的。它通过反射机制生成一个实现了指定接口的类,并在这个类中通过 InvocationHandler 实现方法增强。
-
为什么必须有接口?
因为 Proxy.newProxyInstance() 方法是通过 动态生成一个类,这个类必须实现你传入的接口们,否则它不知道要代理哪些方法。 -
JDK代理打印类似:
class com.sun.proxy.$Proxy...
- CGLIB代理打印类似:
class org.example.design_pattern.Proxy.UserService$$EnhancerByCGLIB$$...
- Spring通过ProxyFactory可以非常灵活地控制是走JDK动态代理还是CGLIB动态代理,只需要简单设置proxyTargetClass即可。