代理模式

代理模式

基本介绍

  • 1.代理模式,为目标对象提供一个替身,以控制这个对象的访问。即通过代理对象访问目标对象,这样做的好处是,可以在目标对象功能的基础上,增加额外的操作,即扩展目标对象的功能。
  • 2.目标对象(被代理对象)可以是远程对象,创建开销大的对象或者需要安全控制的对象。
  • 3.代理模式有不同的三种形式,有静态代理,动态代理(JDK代理,接口代理),Cglib代理(可以在内存中动态的创建对象,而不用实现接口,本身也是动态代理的范畴)
  • 4.代理模式示意图

静态代理

基本介绍

静态代理在使用中,代理对象类和目标对象(被代理对象)类,需要实现相同的接口,或继承相同的父类。

案例引入

要求:

  • 1.定义一个ITeacherDao接口。
  • 2.TeacherDao类和TeacherDaoProxy类都实现这个接口。
  • 3.TeacherDaoProxy类中,持有TeacherDao对象。
  • 4.使用代理对象的目标方法,完成方法的调用,在代理对象内部还是使用的TeacherDao对象完成,不过可以新增一些额外的操作。
类图

代码
//接口
public interface ITeacherDao {

    public void teach();

}
//被代理类
public class TeacherDao implements ITeacherDao{
    @Override
    public void teach() {
        System.out.println(" 老师授课中.... ");
    }
}
//代理类
public class TeacherDaoProxy implements ITeacherDao{

    private ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target){
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("代理开始...");
        target.teach();
        System.out.println("代理结束...");
    }
}
//测试
public class Client {
    public static void main(String[] args) {
        TeacherDao teacherDao = new TeacherDao();

        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
        teacherDaoProxy.teach();
    }
}
优缺点分析
  • 1.优点,在不修改目标对象的功能前提下,通过代理对象对目标对象的功能进行了扩展。
  • 2.缺点,静态代理需要让代理对象和目标对象实现一样的接口,或者继承相同类,如果要对目标对象的功能有不同的扩展,则需要创建很多代理类。
  • 3.一旦接口或类增加方法,目标类,和代理类都要维护。

动态代理

基本介绍
  • 1.代理对象,不需要实现接口,但是目标对象要实现接口,否则不能使用动态代理。
  • 2.代理对象的生成,是利用java中的API,动态的在内存中,创建代理对象。
  • 3.动态代理,也叫JDK代理,接口代理。
JDK中生成代理对象的API
  • 1.代理类,java.lang.reflect.Proxy。
  • 2.JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
案例引入

要求:使用动态代理,实现静态案例中的案例。

类图

代码
public interface ITeachDao {

    public void teach();

}
public class TeachDao implements ITeachDao{
    @Override
    public void teach() {
        System.out.println("老师上课..");
    }
}
public class ProxyFactory {

    private Object target;

    public ProxyFactory(TeachDao target){
        this.target = target;
    }

    public Object getProxyInstance(){
        /**
         *     public static Object newProxyInstance(ClassLoader loader,
         *                                           Class<?>[] interfaces,
         *                                           InvocationHandler h)
         * 说明:
         *     1.ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法固定。
         *     2.Class<?>[] interfaces: 目标对象实现的接口组的class对象组成的数组,使用泛型方法确认类型。
         *     3.InvocationHandler h: 事情处理器,执行目标对象的方法时,会触发当前重写的事情处理器方法,该方法会把当前执行的目标对象(proxy)作为参数传入。
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy,method,args) -> {
                        System.out.println("JDK代理开始....");
                        Object result = method.invoke(target, args);
                        System.out.println("JDK代理结束....");
                        return result;
                });
    }
}
public class Client {
    public static void main(String[] args) {
        TeachDao target = new TeachDao();

        ProxyFactory proxyFactory = new ProxyFactory(target);
        ITeachDao proxyInstance = (ITeachDao)proxyFactory.getProxyInstance();
        proxyInstance.teach();

        System.out.println(proxyInstance.getClass());
    }
}

Cglib代理

Cglib代理模式的基本介绍
  • 1.静态代理和JDK代理都要求目标对象继承类,或实现一个接口,但是很可能目标对象就只是一个单独的类,没有实现任何接口,继承任何类,这个时候可以使用目标对象子类来实现代理,这就是Cglib代理。
  • 2.Cglib代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展。
  • 3.Cglib是一个强大的高性能的代码生成包,它可以在运行期间扩展Java类与实现接口,它广泛的被许多AOP的框架使用,例如Spring AOP实现方法拦截。
  • 4.在AOP编程中如何选择代理模式:
  • 4.1 目标对象需要实现接口,用JDK代理。
  • 4.2 目标对象不需要实现接口,用Cglib代理。
  • 5.Cglib包的底层是通过使用字节码处理框架ASM转换字节码并生成新的类。
Cglib代理模式实现步骤
  • 1.需要引入Cglib的jar文件,或Maven坐标(Spring框架底层会使用到Cglib框架,可以直接引入Cglib的坐标就可以使用了)。
  • 2.在内存中动态构架子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException。
  • 3.目标对象的方法如果为final/static,那么就不会被拦截,既不会执行目标对象额外的业务方法。
案例引入

要求:将前面的案例用Cglib代理模式实现。

uml类图

public class SunDao {

    public void sunLightToEarth(){
        System.out.println("太阳普照大地");
    }

}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @author 长名06
 * @version 1.0
 */
public class ProxyFactory implements MethodInterceptor {

    private SunDao target;

    public ProxyFactory(SunDao target){
        this.target = target;
    }

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

    //重写intercept方法,代理对象调用方法时,会调用到该方法,类似JDK代理中传入的匿名内部类的InvocationHandler h中重写的invoke方法。
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理模式 开始");
        //执行代理方法,获取返回值
        Object resultVal = method.invoke(target, args);
        System.out.println("cglib代理模式 结束");
        return resultVal;
    }
}
public class Client {
    public static void main(String[] args) {
        //创建目标对象
        SunDao target = new SunDao();
        //获取到代理对象,并且将目标对象传递给代理对象
        SunDao proxyInstance = (SunDao)new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法,触发intecept方法,从而实现对目标对象的调用
        proxyInstance.sunLightToEarth();
    }
}

常见的代理模式,介绍几种变体

1.防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
2.缓存代理:当请求图片文件等资源时,先到缓存代理取,如果取到资源则返回。取不到资源,再到公网或者数据库取,然后缓存。
3.远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用,远程代理通过网络和真正的远程对象沟通信息。
4.同步代理:主要使用在多线程编程中,完成多线程间异步工作。

只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。

posted @ 2023-09-24 23:39  长名06  阅读(83)  评论(0编辑  收藏  举报