设计模式学习---代理模式

代理模式---咱们编程的代码能够分为业务代码与非业务代码,非业务代码可能有日志记录,事务管理,权限校验等,使用到代理模式把业务代码和非业务代码区分开来帮助下降耦合且具备良好的扩展性,就是把一些事情不须要业务类执行的操做交给代理类执行java

代理模式主要分为三种,静态代理、动态代理、Cglib代理编程

1、静态代理数组

静态代理的条件是目标类与代理类必须实现同一个接口而后经过调用相同的函数完成对目标函数的调用 ,业务操做由目标类实现,非业务操做由代理类实现maven

1.接口ide

public interface Test {
    void test();
}

2.目标类函数

public class TestImpl implements Test{

    @Override
    public void test() {
        System.out.println("业务代码静态代理测试");
    }
}

3.代理类工具

public class TestProxy implements Test{

    private Test test;

    public TestProxy(Test test) {
        this.test = test;
    }

    @Override
    public void test() {
        System.out.println("业务代码执行以前执行");
        test.test();
        System.out.println("业务代码执行以后执行");
    }
}

下面是测试代理的方法,须要经过代理类指向一个目标类测试

public static void main(String[] args) {
    Test test = new TestProxy(new TestImpl());
    test.test();
}

执行以后结果:this

使用静态代理使不一样职能的代码区分降耦合,并且提供了良好的扩展性,可是代理类也必须实现接口,若是某个业务又须要记录日志,又须要管理实务,并且A类代理只能代理A类目标,这样就会多出不少类,而不能经过一个代理类完成对全部目标类的代理spa

2、动态代理

动态代理解决类静态代理A类代理只能代理A类目标的问题,由于动态代理的代理类不须要实现与目标类相同的接口,而是经过Java JDK提供的API java.lang.reflect.Proxy 实现,虽然代理类不用再与目标类实现同一个接口,可是目标类仍是须要依赖接口实现才能完成代理。主要是经过ClassLoader对象来指定须要被代理的类,经过Interface[]来声明须要代理的函数再实现InvocationHandler重写invoke()并使用它进行代理

1.接口

public interface Test {
    String test();
}

2.目标类

public class TestImpl implements Test {
    @Override
    public String test() {
        System.out.println("业务代码动态代理测试");
        return "动态代理返回值";
    }
}

3.代理类,这里经过代理工厂获取代理类

public class ProxyFactory {

    private Object obj;

    public ProxyFactory(Object obj) {
        this.obj = obj;
    }

    public Object getInstance(){
        // 实现InvocationHandlet接口建本身的理器
        return Proxy.newProxyInstance(
                // ClassLoader指定须要被代理的                obj.getClass().getClassLoader(),
                // Interfaces数组来代理里面全部函                obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    // 重 invoke() 编写代理的逻辑
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("业务代码执行以前执行");
                        Object returnObj = method.invoke(obj, args);
                        System.out.println("业务代码执行以后执行");
                        return returnObj;
                    }
                });
    }

}

下面是测试代理的方法,须要经过代理类指向一个目标类

public static void main(String[] args) {
    Test test = new TestImpl();
    Test instance = (Test)new ProxyFactory(test).getInstance();
    instance.test();
}

执行结果:

这样就可使用一个代理类代理多种目标类,不用实现相同接口,可是目标类依然要实现接口

3、Cglib代理( Code Generation Library )

Cglib代理也是动态代理的另一种实现方案,JDK的动态代理有个缺陷,就是目标类必须实现一个接口才能被代理,这也是早期SpringAOP必须实现接口的一个缘由,目前Spring支持JDK动态代理(下面称为Java Proxy)也支持Cglib动态代理

Cglib代理须要引入Cglib包,目标类与代理类都不须要再实现接口,首先须要引入maven包,它的主要原理是在内存中动态修改咱们的class字节码文件,而且代理类不能是final,目标类的函数也不能是fianl或者static修饰

 

1.不须要实现接口的目标类

public class Test {
    public void test(){
        System.out.println("业务代码Cglib代理测试");
    }
}

2.代理类工厂

public class ProxyFactory implements MethodInterceptor {

    private Object obj;

    public ProxyFactory(Object obj) {
        this.obj = obj;
    }

    //标对建一代理    public Object getInstance(){
        //1.CGlib工具        Enhancer en = new Enhancer();
        //2.置父类(目标类)
        en.setSuperclass(obj.getClass());
        //3.置回        en.setCallback(this);
        //4.建子(代理)
        return en.create();

    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("业务代码执行以前执行");
        Object returnObj = method.invoke(obj, objects);
        System.out.println("业务代码执行以后执行");
        return returnObj;
    }
}

3.测试函数

public static void main(String[] args) {
    Test test = (Test)new ProxyFactory(new Test()).getInstance();
    test.test();
}

执行结果:

SpringAOP其实使用的就是代理模式,它目前同时支持CGlib和java Proxy的动态代理,Spring在之前一些旧版本中不支持CGlib的状况下,咱们的Service都会实现一个接口,从而方便AOP管理,可是如今一些不必实现接口的类可使用CGlib动态代理,减小代码量,除非某个函数真的有多种实现方式,这个时候咱们才选择java Proxy的代理模式

总结

其实不管java Proxy动态代理或者CGlib动态代理都是经过新增咱们的class字节码动态改变代码结构实现的,例如CGlib的类不能使用fianl的缘由,是由于CGlib会帮咱们的目标类建立一个子类来进行代理,若是类或者函数是final修饰的则没法被继承致使没法使用CGlib代理,下面简单写个例子

1.目标类

public class Test {
    public void test(){
        System.out.println("自编简单继承代理");
    }
}

2.代理类

public class TestProxy extends Test{

    @Override
    public void test() {
        System.out.println("业务代码执行以前执行");
        super.test();
        System.out.println("业务代码执行以后执行");
    }
}

测试函数

public static void main(String[] args) {
    Test test = new TestProxy();
    test.test();
}

执行结果

若是咱们在目标类的test()加上final来修饰

public final void test(){
    System.out.println("自编简单继承代理");
}

public static void main(String[] args) {
    Test test = new TestProxy();
    test.test();
}

上面的测试结果与CGlib代理的结果是同样的,因此CGlib代理原理就是在咱们代码编译的时候动态建立子类,且继承须要代理的函数从而实现动态代理。例以下面的图

可是java Proxy虽然也是在编译的时候动态建立代理类,不过与CGlib不同的是,java Proxy会动态建立一个实现了与目标类相同接口的代理类,例以下面的图

Java Proxy与CGlib 都是经过新增class字节码来完成动态代理,可是它们实现新增字节码的方式是不同的,java Proxy是直接操做字节码,而CGlib则是经过ASM操做字节码。固然也存在修改class字节码来完成代理的解决方案(Aspect,javaagent)

相关文章
相关标签/搜索