前言

Java代理是一种设计模式,它允许在不改变原始类或接口的情况下对其进行增强或修改。


一、正常(不使用代理的情况下)

假设我们有一个名为 UserService接口和实现类,它提供了两个方法 addUser 和 deleteUser

public interface UserService {
    public void addUser(String name);
    public void deleteUser(String name);
}
public class UserServiceImpl implements UserService{
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
    @Override
    public void deleteUser(String name) {
        System.out.println("删除用户:" + name);
    }
}

现在,我们需要在 addUser 和 deleteUser 方法执行前后打印日志。一种实现方式是在每个方法的前后都打印日志,如下所示:

public class UserServiceImpl implements UserService{
    @Override
    public void addUser(String name) {
        System.out.println("Before addUser");
        System.out.println("添加用户:" + name);
        System.out.println("After addUser");
    }
    @Override
    public void deleteUser(String name) {
        System.out.println("Before deleteUser");
        System.out.println("删除用户:" + name);
        System.out.println("After deleteUser");
    }
}

虽然上面的方式可以实现对象增强,但它缺点也很明显,增强逻辑与目标方法紧密耦合在一起,侵入性较高,不易扩展和维护。

二、静态代理

代理模式可以在不修改原有代码的情况下,增加额外的功能或者对原有功能进行增强。代理模式能够更好的解耦:代理对象和被代理对象是相互独立的,这意味着我们可以在不影响被代理对象的情况下,对代理对象进行修改和优化。
静态代理是在编译时创建的代理对象,需要手动编写代理类。它需要实现与原始类相同的接口,并在代理类中调用原始类的方法。

1.编写代码

/**
 * Create by zjg on 2024/2/14
 * 接口类
 */
public interface UserService {
    void addUser(String name);
}
/**
 * Create by zjg on 2024/2/14
 * 实现类
 */
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}
/**
 * Create by zjg on 2024/2/14
 * 代理类
 */
public class UserServiceProxy implements UserService {
    private UserService userService;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void addUser(String name) {
        System.out.println("添加用户之前的日志记录");
        userService.addUser(name);
        System.out.println("添加用户之后的日志记录");
    }
}
/**
 * Create by zjg on 2024/2/14
 * 测试类
 */
public class UserServiceTest {
    // 使用代理
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(userService);
        proxy.addUser("张三");
    }
}

2.执行结果

在这里插入图片描述

三、动态代理

为了解决静态代理的问题,我们使用动态代理。动态代理可以通过Java的反射机制在运行时动态地生成代理类,无需手动编写代理类。动态代理可以代理多个接口,且可以实现通用的代理逻辑,避免了代码重复,同时也避免了静态代理中需要手动编写代理类的缺点。

1.JDK动态代理

jdk动态代理是通过2个类:java.lang.reflect.Proxy、java.lang.reflect.InvocationHandler来实现的。

/**
 * Create by zjg on 2024/2/14
 * 接口类
 */
public interface UserService {
    void addUser(String name);
}
/**
 * Create by zjg on 2024/2/14
 * 实现类
 */
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}
/**
 * Create by zjg on 2024/2/14
 * 拦截器类
 */
public class UserServiceInvocationHandler implements InvocationHandler {
    private Object target;
    public UserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 执行增强逻辑
        System.out.println("方法执行前的日志记录");
        // 调用目标方法
        Object result = method.invoke(target, args);
        // 执行增强逻辑
        System.out.println("方法执行后的日志记录");
        return result;
    }
}
/**
 * Create by zjg on 2024/2/14
 * 代理类
 */
public class UserServiceProxy {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new UserServiceInvocationHandler(target));
    }
}
/**
 * Create by zjg on 2024/2/14
 * 测试类
 */
public class UserServiceTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) UserServiceProxy.getProxy(userService);
        proxy.addUser("张三");
    }
}

在这里插入图片描述

JDK动态代理的优点在于可以代理任意实现了接口的类,不需要针对特定的类编写代理代码。但是,它只能代理实现了接口的类,无法代理没有实现接口的类。

2.CGLib代理

相比于JDK动态代理,CGLib可以对任意类进行代理,而不仅仅是接口。

2.1引入依赖库

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

2.2定义接口和实现类

/**
 * Create by zjg on 2024/2/14
 * 接口类
 */
public interface UserService {
    void addUser(String name);
}
/**
 * Create by zjg on 2024/2/14
 * 实现类
 */
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}

2.3 拦截器

/**
 * Create by zjg on 2024/2/14
 * 拦截器类
 */
public class UserServiceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 执行增强逻辑
        System.out.println("方法执行前的日志记录");
        // 调用目标方法
        Object result = proxy.invokeSuper(obj, args);
        // 执行增强逻辑
        System.out.println("方法执行后的日志记录");
        return result;
    }
}

2.4 代理类

/**
 * Create by zjg on 2024/2/14
 * 代理类
 */
public class UserServiceProxy {
    public static Object getProxy(Class<?> clazz) {
        //1.创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
        enhancer.setSuperclass(clazz);
        /*3.设置回调,需实现org.springframework.cglib.proxy.Callback接口,
        此处使用的是org.springframework.cglib.proxy.MethodInterceptor,也是一个接口,实现了Callback接口,
        当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的invoke方法处理*/
        enhancer.setCallback(new UserServiceMethodInterceptor());
        //4.获取代理对象,调用enhancer.create方法获取代理对象,这个方法返回的是Object类型.
        return enhancer.create();
    }
}

2.5 测试类

/**
 * Create by zjg on 2024/2/14
 * 测试类
 */
public class UserServiceTest {
    public static void main(String[] args) {
        //方法返回的是Object类型,需要转型
        UserService userService = (UserService) UserServiceProxy.getProxy(UserServiceImpl.class);
        userService.addUser("张三");
    }
}

在这里插入图片描述


总结

回到顶部
以上内容仅为java中代理模式的简单实现,具体原理和更详细的介绍请查看原博客
AOP使用看这里

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐