Java基础 代理模式

代理模式是很常见的一种设计模式,大量使用在AOP,他的主要目的是:

  • 控制目标对象的访问
  • 修改目标对象的方法(网上常见的说法叫增强,其实也可以削弱,所以我认为"修改"更恰当)
  • 遵循开闭原则

本文将通过几个简单Demo层层深入代理模式的设计思想。

代理模式角色划分

目标对象:target object
代理对象:proxy object
静态代理与动态代理的区别
静态代理:代理类由程序员手动创建
动态代理:代理类由程序动态生成

模型类User
public class User {
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
}

静态代理

继承方式

业务类UserService
public class UserService {
    public int insertUser(User user) {
        System.out.println("假装访问数据库,插入一条User数据");
        return 1;
    }
}
启动类Application
public class Application {
    public static void main(String[] args) {
        UserService userService = new UserService();
        User user = new User("tbryant", 18);
        userService.insertUser(user);
    }
}

如果需要记录insertUser方法的执行时间,我们可以在insertUser方法内部添加代码,但考虑到以下两点,不建议这么做。
1.在没有业务类源码的情况下,无法对方法进行修改
2.有多处调用insertUser方法,但并非所有地方都要记录执行时间
这个时候就能使用代理,最简单的方式就是新写一个类,继承UserService,记录执行时间。

记录执行时间类UserServiceTime
public class UserServiceTime extends UserService {
    @Override
    public int insertUser(User user) {
        System.out.println("=====记录执行时间=====");
        return super.insertUser(user);
    }
}

这样可以在调用的地方控制,如果不需要记录执行时间就用UserService类,需要则用UserServiceTime类。
换一个需求,如果需要为insertUser方法添加日志,就要再新写一个类,继承UserService,记录执行日志。

记录执行日志类UserServiceLog
public class UserServiceLog extends UserService {
    @Override
    public int insertUser(User user) {
        System.out.println("=====记录执行日志=====");
        return super.insertUser(user);
    }
}

如果需求又变了,要先记录执行时间,再记录执行日志,那就再写一个类,继承UserServiceLog类。

先记录执行时间类,再记录执行日志类UserServiceTimeLog
public class UserServiceTimeLog extends UserServiceLog {
    @Override
    public int insertUser(User user) {
        System.out.println("=====记录执行时间=====");
        return super.insertUser(user);
    }
}

如果需求变成先记录执行日志,再记录执行时间,那么继承方式的静态代理是不能调整顺序的,只能再写一个UserServiceLogTime类,继承UserServiceTime类。。。这样围绕一个业务类就会衍生无数个类,这就是继承方式的缺陷,因此需要使用聚合方式。

聚合(接口)方式

聚合方式有两个特点:目标对象和代理对象实现同一个接口;代理对象包含目标对象。

业务接口IUserService
public interface IUserService {
    public int insertUser(User user);
}
业务类UserService
public class UserService implements IUserService {
    @Override
    public int insertUser(User user) {
        System.out.println("假装访问数据库,插入一条User数据");
        return 1;
    }
}
记录执行时间类UserServiceTime
public class UserServiceTime implements IUserService {
    IUserService target;

    public UserServiceTime(IUserService target) {
        target = target;
    }

    @Override
    public int insertUser(User user) {
        System.out.println("=====记录执行时间=====");
        return target.insertUser(user);
    }
}
记录执行日志类UserServiceLog
public class UserServiceLog implements IUserService {
    IUserService target;

    public UserServiceLog(IUserService target) {
        target = target;
    }

    @Override
    public int insertUser(User user) {
        System.out.println("=====记录执行日志=====");
        return target.insertUser(user);
    }
}
启动类Application
public class Application {
    public static void main(String[] args) {
        User user = new User("tbryant", 18);
        System.out.println("demo1:原逻辑");
        IUserService target = new UserService();
        target.insertUser(user);
        System.out.println("demo2:记录执行时间");
        IUserService proxyTime = new UserServiceTime(target);
        proxyTime.insertUser(user);
        System.out.println("demo3:记录执行日志");
        IUserService proxyLog = new UserServiceLog(target);
        proxyLog.insertUser(user);
        System.out.println("demo4:先记录执行日志,再记录执行时间");
        IUserService proxyLogTime = new UserServiceLog(proxyTime);
        proxyLogTime.insertUser(user);
        System.out.println("demo5:先记录执行时间,再记录执行日志");
        IUserService proxyTimeLog = new UserServiceTime(proxyLog);
        proxyTimeLog.insertUser(user);
    }
}
执行结果

在这里插入图片描述
聚合方式相比继承方式,其优点是:在代理每种功能的时候只需要写一个类,然后可以在调用的地方控制顺序,能够灵活应对需求变化。但demo中只代理了一个接口,如果有多个接口,仍然存在类过多的问题,因此需要使用动态代理。

动态代理

在spring中大量运用代理,所用的技术是JDK动态代理和CGLib动态代理。实现动态代理的技术还有很多,本文只讨论JDK动态代理和CGLib动态代理两种。

JDK动态代理

基于聚合方式。
特点:代理类与目标类实现同一个接口。
本文仅展示简单Demo,手动模拟JDK动态代理,请参考此链接Java基础 代理模式 实现JDK动态代理

业务接口IUserService
public interface IUserService {
    public int insertUser(User user) throws Throwable;
}
业务类UserService
public class UserService implements IUserService {
    @Override
    public int insertUser(User user) {
        System.out.println("假装访问数据库,插入一条User数据");
        return 1;
    }
}
代理逻辑类IUserServiceInvocationHandler
public class IUserServiceInvocationHandler implements InvocationHandler {
    private Object target;

    public IUserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理:调用IUserServiceInvocationHandler.invoke方法");
        return method.invoke(target, args);
    }
}
启动类Application
public class Application {
    public static void main(String[] args) throws Throwable {
        User user = new User("tbryant", 18);
        // JDK动态代理
        IUserService jdkProxy = (IUserService) Proxy.newProxyInstance(Application.class.getClassLoader(),
                new Class[]{IUserService.class}, new IUserServiceInvocationHandler(new UserService()));
        jdkProxy.insertUser(user);
    }
}

CGLib动态代理

基于继承方式。底层使用ASM技术操作.class文件。
特点:代理类继承目标类,代理类实现EnhancedConfiguration接口。

添加依赖build.gradle
    // https://mvnrepository.com/artifact/cglib/cglib
    compile group: 'cglib', name: 'cglib', version: '3.2.11'
业务类UserService
public class UserService {
    public int insertUser(User user) {
        System.out.println("假装访问数据库,插入一条User数据");
        return 1;
    }
}
代理逻辑类UserServiceMethodInterceptor
public class UserServiceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLib动态代理:调用UserServiceMethodInterceptor.intercept方法");
        return proxy.invokeSuper(obj, args);
    }
}
启动类Application
public class Application {
    public static void main(String[] args) {
        User user = new User("tbryant", 18);
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new UserServiceMethodInterceptor());
        enhancer.setCallbackType(UserServiceMethodInterceptor.class);
        UserService proxy = (UserService) enhancer.create();
        proxy.insertUser(user);
    }
}

源码地址:ProxyPatternDemo模块

发布了14 篇原创文章 · 获赞 3 · 访问量 875

猜你喜欢

转载自blog.csdn.net/qq_37956177/article/details/103540167