深入浅出设计模式——代理模式

1.代理模式介绍

2.用代码演示代理模式

3.总结

1.代理模式介绍

定义
1)代理模式,为代理对象提供一个替身,以控制这个对象的访问,就是通过代理对象来访问对象。这样做的好处是:可以在目标对象的基础上,再增加额外的增强操作。
2)被代理的对象可以是远程对象,创建开销大的字段和需要安全控制的字段
3)代理模式有三种不同的形式,静态代理,动态代理,和cglib代理。

描述
代理最著名的使用场景就是人尽皆知的AOP,就拿我们很熟悉的事务注解@Transactional来说,我们知道加上了这个注解后,函数方法就会成为事务操作。

我们都知道,用sql写事务的时候, 如果我们要进行事务操作,需要写两个这样的sql语句:

start transaction;

//中间执行业务逻辑

commit;

这时我们写好了中间的业务逻辑,加上注解之后,相当于把这个对象交给了一个代理对象,让代理对象执行开启事务和结束时的逻辑,我们只要编写业务逻辑就好了。同时这个方法也得到了增强,有了前置和后置方法,有了事务的效果,增强额外的功能操作。

2.用代码演示依代理模式

1)静态代理

静态代理模式的基本介绍:

静态代理在使用的时候,需要定义接口或者父类,被代理对象(目标对象)与代理对象一起实现共同的接口或者是继承相同的父类。

具体要求
1)定义一个接口ITeacherDao
2)目标对象TeacherDAO实现接口ITeacherDAO
3)定义一个代理对象TeacherProxy,也实现ITeacherDao
4)调用的时候通过调用代理对象的方法来调用目标对象
5)代理对象与目标要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

代码展示

ITeacherDao:


public interface ITeacherDao {

    void teach();//授课的方法
}

TeacherDao:

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("老师开始上课了!");
    }
}

TeacherProxy:

public class TeacherProxy implements ITeacherDao {

    //先把要代理的对象传进来
    private ITeacherDao target = new TeacherDao();
    
    public TeacherProxy(ITeacherDao target) {
        this.target = target;
    }
    
    //执行代理方法,在方法前后进行增强
    @Override
    public void teach() {
        System.out.println("代理开始");
        target.teach();
        System.out.println("代理结束");
    }
}
    public static void main(String[] args) {
        //被代理对象
        ITeacherDao iTeacherDao = new TeacherDao();
        //代理对象
        TeacherProxy teacherProxy = new TeacherProxy(iTeacherDao);
        //执行代理对象的方法
        teacherProxy.teach();

    }

优点:符合开闭原则,能对目标对象进行扩展。

缺点:为每一个需要增强的服务都需要创建类,容易形成类爆炸。

2)动态代理

动态代理基本介绍:

1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能使用
2)代理对象的生成,是利用jdk的api,动态的在内存中构建代理对象
3)动态代理也叫作:jdk代理,接口代理

代码实现:

ITeacherDao:

public interface ITeacherDao {

    void teach();//授课的方法
}

TeacherDao:

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("老师开始上课了!");
    }
}

ProxyFactory:

public class ProxyFactory {

    //维护一个目标对象
    private Object target;

    //创建构造器对一个目标对象进行初始化
    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        //当前对象使用的目标类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //目标对象实现的接口类型,使用泛型的方式确认类型
        Class[] classes = target.getClass().getInterfaces();
        //执行目标对象的方法时,会出发事情处理方法,会把当前执行的对象当做参数传入
        InvocationHandler invocationHandler = null;
        return Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强逻辑
                System.out.println("JDK代理开始");
                //原方法执行逻辑,执行完之后返回值
                Object invoke = method.invoke(target, args);
                System.out.println("JDK代理结束");
                return invoke;
            }
        });

    }
}

要注意一下Proxy.newProxyInstance() 方法有三个参数,
Proxy.newProxyInstance()方法接受三个参数:

ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的

Class[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型

InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

只需要按照上述的注释传入就可以了,用法也是十分简单。

优点:动态代理大大减少了我们的开发任务,同时减少了业务接口依赖。

缺点:始终无法摆脱interface代理的桎梏,无法实现对class的动态代理。

3)cglib代理

1)需要引入cglib的依赖
2)通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

代码展示:

TeacherDao:

public class TeacherDao {

    public void teach() {
        System.out.println("老师开始上课了!");
    }


}

CglibProxyFactory:

public class CglibProxyFactory implements MethodInterceptor {
    //要增强的目标类
    private Object target;

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

    public Object getProxyInstance() {
        // 创建工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建子类对象,即代理对象
        return enhancer.create();
    }

    //重写intercept,会调用目标对象的方法
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //增强的逻辑
        System.out.println("cglib开始");
        //执行完毕后返回的参数
        Object invoke = methodProxy.invokeSuper(obj, args);
        //增强的逻辑
        System.out.println("cglib结束");
        //返回参数
        return invoke;
    }

}

    public static void main(String[] args) {
        //目标代理对象
        TeacherDao target = new TeacherDao();
        //代理工厂
        CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target);
        //从代理工厂返回代理对象
        TeacherDao proxyInstance = (TeacherDao) cglibProxyFactory.getProxyInstance();
        //执行代理对象的方法
        proxyInstance.teach();
    }

优点:能对class进行增强,CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高。

缺点:CGLIB创建代理对象时所花费的时间比JDK多得多,而且编码不是非常方便,同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

3.总结

代理模式就是spring底层AOP的实现方法,目的就是为了把一些增强切入到业务逻辑代码里,并将这个切面统一管理。

你可能感兴趣的