代理模式

代理模式

定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

好处

  • 中介隔离作用

    在某些情况下,一个客户端类不想或者不能直接引用一个委托对象,而代理类可以在客户端和委托类之间起到中介作用,其特征是代理类和委托类实现相同的接口。

  • 开闭原则,增强功能

    代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

分类

我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。

区别

  • 静态代理需要为每一个服务新建代理类、接口一旦发生变化代理类也需要修改。工作量大,不易维护。
  • 动态代理类仅支持interface类型的代理,代理类是实现接口的另一个新类;
  • cglib代理支持支持class类型的代理,代理类是委托类的一个子类
1.静态代理
// 接口
public interface UserService {
    void save();
}
// 实现类&委托类
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存用户");
    }
}
// 代理类
public class UserServiceStaticProxyFactory implements UserService{
    private UserService userService;

    public UserServiceStaticProxyFactory(UserService userService){
        this.userService = userService;
    }
    
    @Override
    public void save() {
        System.out.println("打开事务!");
        userService.save();
        System.out.println("提交事务!");
    }
}
// test 
public static void main(String[] args) {
        UserServiceStaticProxyFactory proxyFactory = new UserServiceStaticProxyFactory(new UserServiceImpl());
        proxyFactory.save();
    }

2.动态代理

调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。

注意Proxy.newProxyInstance()方法接受三个参数:

  • ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler:动态处理器,执行目标对象的方法时,会触发事件处理器的方法
public class UserServiceProxyFactory {

    public <T> Object getProxy(Class<T> clz) {
        try {
            Object impl = clz.newInstance();
            // 生成动态代理
            return Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    before();
                    Object invoke = method.invoke(impl, args);
                    after();
                    return invoke;
                }
            });
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }
    
    //可以重写
    public void before() {
        System.out.println("之前");
    }

    public void after() {
        System.out.println("之后");
    }
}
3.cglib代理
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
public class UserServiceProxyFactory2 implements MethodInterceptor {

    public <T> Object getProxy(Class<T> clz) {
        Enhancer en = new Enhancer();// 帮我们生成代理对象
        en.setSuperclass(clz);// 设置对谁进行代理
        en.setCallback(this);//回调函数
        return en.create();// 创建代理对象;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 打开事务
        System.out.println("打开事务!");
        // 调用原有方法
        Object invokeSuper = methodProxy.invokeSuper(o, objects);
        // 提交事务
        System.out.println("提交事务!");
        return invokeSuper;
    }
}
// Test  
public static void testAop() {
        UserServiceProxyFactory2 factoryContext = new UserServiceProxyFactory2();
        UserService userServiceProxy = (UserService) factoryContext.getProxy(UserServiceImpl.class);
        userServiceProxy.save();
        System.out.println(userServiceProxy instanceof UserServiceImpl);// 判断是否属于被代理对象类型
    }

posted on 2019-12-17 16:15  齊帥  阅读(102)  评论(0编辑  收藏  举报

导航