代理模式

97 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

概念:委托一个代理类对另一个类进行控制(代理类中有被代理类的对象,同时可以在代理类中增强)

使用场景:aop(可以控制被代理类是否被调用,是否被代理)。

优点:

  1. 确保被代理类的隐秘性
  2. 降低耦合性(不用挨个加需要增强的方法)

缺点:

  1. 类数量的增多,结构更复杂。

1.代码实现

1.静态代理

静态代理就是把增强的方法写在代理的类中,在编译时就确定了,这样耦合性比较高,除了方法量级较小或者增强的方法固定,其他情况不推荐。

subject接口

public interface PlayLol{
    public void plya();
}

realSubject

public class IsPlay implements PlayLol{
    public void play() {
        System.out.println("开始玩");
    }
}

proxy

public class ProxySubject implements PlayLol{

    private PlayLol playLol;
	//关键,将被代理对象传进来
    public ProxySubject(final PlayLol playLol) {
        this.playLol = playLol;
    }

    public void play() {
        System.out.println("打开电脑");
        //可以增加控制 ,可以不让他玩。。。
        subject.play();
        System.out.println("15投");
    }
}

调用

public class MainClass {
    public static void main(String[] args) {
        PlayLol playLol = new IsPlay();
        playLol.paly();

        System.out.println("============");

        ProxySubject proxySubject = new ProxySubject(playLol);
        proxySubject.paly();
    }
}

2.动态代理:

1.JDK代理

代理类

public class ProxyHandler implements InvocationHandler {

    private Object object;

    public DynamicProxyHandler(final Object object) {
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("先开电脑");
        Object result = method.invoke(object, args);
        System.out.println("关电脑");
        return result;
    }
}

调用

public class MainClass {
    public static void main(String[] args) {
        PlayLol subject = new IsPlay();
        /**
         * ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
         * Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
         * InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
         * 都是固定写法
         */
        PlayLol proxySubject = (PlayLol) Proxy.newProxyInstance(PlayLol.class.getClassLoader(),
                new Class[]{PlayLol.class},
                new ProxyHandler(subject));
        proxySubject.play();
    }

2.CGLib动态代理

如果被代理类没有接口不可以使用jdk动态代理

被代理类

public class IsPlay{
    public void play() {
        System.out.println("开始玩");
    }
}

代理类

public class CglibProxy implements MethodInterceptor {
    private Object target;//业务类对象,供代理方法中进行真正的业务方法调用

       //相当于JDK动态代理中的绑定
        public Object getInstance(Object target) {
        this.target = target;  //给业务对象赋值
        Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
        enhancer.setSuperclass(this.target.getClass());  //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        // 创建动态代理类对象并返回
        return enhancer.create();
        // 以上都是固定写反
    }
    // 实现回调方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开电脑");
        proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
        System.out.println("关电脑");
        return null;
    }
}

调用

public class MainClass {
    public static void main(String[] args) {
        IsPlay play= new IsPlay();
        CglibProxy cglibProxy = new CglibProxy();
        IsPlay realSubjectProxy =
                (IsPlay) cglibProxy.getInstance(play);
        realSubjectProxy.play();
    }
}

引用:CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。