代理模式

代理模式

定义

在代理模式(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即可。
posted @ 2025-04-26 22:22  Eiffelzero  阅读(22)  评论(0)    收藏  举报