代理模式

戏子无情 提交于 2020-01-13 18:11:32

代理模式

经过学习整理后来输出心得

静态代理

public interface Person {

    void  findLove();
}
public class Son implements Person {
    @Override
    public void findLove() {
        System.out.println("找美女");
    }
}
 //父亲就是儿子的代理类
public class Father implements Person {

    private Son son;

    public Father(Son son) {
        this.son = son;
    }

    @Override
    public void findLove() {
        before();
        son.findLove();
        after();
    }

    void before() {
        System.out.println("开始物色");
    }

    void after() {
        System.out.println("结束");
    }


}

public class Test {

    public static void main(String[] args) {
        Father father = new Father(new Son());
        father.findLove();
    }
}

输出:
在这里插入图片描述

静态代理和动态的本质区别

1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

动态代理

动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。

JDK动态代理

public class Customer implements Person {
    @Override
    public void findLove() {
        System.out.println("找对象");
    }
}
public class JDKMeipo implements InvocationHandler {

    //被代理的对象,把对象的引用保存下来
    private Object object;

    public Object getInstance(Object object) {
        this.object = object;
        Class<?> clazz = object.getClass();
        //利用反射将类的所有方法生成
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object invoke = method.invoke(this.object, args);
        after();
        return invoke;
    }

    void before() {
        System.out.println("媒婆开始物色对象");
    }

    void after() {
        System.out.println("找对象结束");
    }
}

public class Test {

    public static void main(String[] args) {
        Person instance = (Person) new JDKMeipo().getInstance(new Customer());
        instance.findLove();
    }
}

输出:
在这里插入图片描述

JDK Proxy 采用字节重组,重新生成的对象来替代原始的对象以达到动态代理
的目的。JDK Proxy 生成对象的步骤如下:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口。
3、动态生成Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)。
4、编译新生成的Java 代码.class。
5、再重新加载到JVM 中运行。
这个过程就叫字节码重组。JDK 中有一个规范,在ClassPath 下只要是$开头的class文件一般都是自动生成的。

Cglib动态代理

CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象
实现的动态代理
JDK是采用读取接口的信息
CGLib覆盖父类方法
目的:都是生成一个新的类,去实现增强代码逻辑的功能
JDK Proxy 对于用户而言,必须要有一个接口实现,目标类相对来说复杂
CGLib 可以代理任意一个普通的类,没有任何要求
CGLib 生成代理逻辑更复杂,效率,调用效率更高,生成一个包含了所有的逻辑的FastClass,不再需要反射调用
JDK Proxy生成代理的逻辑简单,执行效率相对要低,每次都要反射动态调用
CGLib不能代理final的方法

public class Customer {

    public void findLove() {
        System.out.println("要求:必须要好看");
    }

}
public class CglibMeipo implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
            throws Throwable {
        before();
        Object o1 = methodProxy.invokeSuper(o, objects);
        after();
        return o1;
    }

    void before() {
        System.out.println("媒婆物色对象");
    }

    void after() {
        System.out.println("找对象结束");
    }

}
public class Test {

    public static void main(String[] args) {
        Customer customer = (Customer) new CglibMeipo().getInstance(Customer.class);
        customer.findLove();
    }
}

输出:
在这里插入图片描述

调用过程: 代理对象调用this.findLove() 方法-> 调用拦截器
->methodProxy.invokeSuper->CGLIB$findLove$0->被代理对象findLove()方法。
此时,我们发现拦截器MethodInterceptor 中就是由MethodProxy 的invokeSuper
方法调用代理方法的,MethodProxy 非常关键.

CGLib 动态代理执行代理方法效率之所以比JDK 的高是因为Cglib 采用了FastClass 机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class 会为代理类或被代理类的方法分配一个index(int 类型)。这个index 当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高

FastClass 并不是跟代理类一块生成的,而是在第一次执行MethodProxy
invoke/invokeSuper 时生成的并放在了缓存中。

CGLib 和JDK 动态代理对比

1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和CGLib 都是在运行期生成字节码,JDK 是直接写Class 字节码,CGLib 使用ASM框架写Class 字节码,Cglib 代理实现更复杂,生成代理类比JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过FastClass 机制直接调用方法,CGLib 执行效率更高。

代理模式的优点

1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。

代理模式的缺点

1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。

Spring 中的代理

Spring 利用动态代理实现AOP 有两个非常重要的类,一个是JdkDynamicAopProxy 类和CglibAopProxy 类
在这里插入图片描述
1、当Bean 有实现接口时,Spring 就会用JDK 的动态代理
2、当Bean 没有实现接口时,Spring 选择CGLib。
3、Spring 可以通过配置强制使用CGLib,只需在Spring 的配置文件中加入如下代码:

<aop:aspectj-autoproxy proxy-target-class="true"/>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!