java的三种代理模式

1.代理模式很好地将两个直接关联的类进行了解耦,而且还能够在代理类中添加额外的代码,以进行特殊的处理,若是不采用代理模式,当两个类关联式,就须要再代码中直接调用另外一个类B,这样若是须要添加一些特殊的处理,就通用要直接写在某一个类A的代码中,当有不少类都须要这些特殊处理时,每一个类都必须编写相应的特殊处理的代码,就不能进行代码的复用,采用代理模式就能避免这些问题。java

在程序开发过程当中,常常会遇到一些和具体的业务逻辑无关的代码控制,如日志,权限,事务处理等,每段代码都添加日志或权限,事务处理的代码,程序编写起来会很麻烦,若是能有一个代理类,统一进行额外的处理,就能够放更多精力在业务逻辑代码中了。spring

代理模式的关键点:代理对象与目标对象。代理对象是对目标对象的扩展,并会调用目标对象。编程

代理模式就是给一个对象提供一个代理对象,由这个代理对象控制对原对象的引用,使代理类在客户端和原对象之间起到一个中介的做用。代理模式主要由3部分组成:抽象目标类,具体目标类和代理类。框架

2.静态代理ide

静态代理由咱们本身去生成的固定的代码进行编译。须要定义接口或者抽象的父类做为抽象目标类,具体目标类和代理类一块儿实现相同的接口或者是继承相同的类,而后经过调用相同的方法来调用目标对象的方法。函数

代码示例:测试

2.1)抽象目标类ui

/**
 * IUserDao interface
 * 接口
 * @author guanhuifang
 * @date 2018/2/24 下午3:35
 **/
public interface IUserDao {
    void save();
}

2.2 )具体目标类,即被代理对象this

/**
 * UserDao class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午3:36
 * 目标对象
 **/
public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("==已经保存数据啦");
    }
}

2.3)代理类代理

/**
 * UserDaoProxy class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午3:38
 **/
public class UserDaoProxy implements IUserDao {
    private IUserDao iUserDao;


    public UserDaoProxy(IUserDao iUserDao) {
        this.iUserDao = iUserDao;
    }

    @Override

    public void save() {
        System.out.println("开始事务。。。");
        iUserDao.save(); //执行目标对象
        System.out.println("提交事务。。。");
    }
}

测试类

public class App {

    public static void main(String[] args) {
        //目标对象
        UserDao user = new UserDao();
        //代理对象,将目标对象传给代理对象,创建代理关系
        UserDaoProxy proxy = new UserDaoProxy(user);
        proxy.save(); //执行的是代理的方法
    }
}

运行结果:

开始事务。。。
==已经保存数据啦
提交事务。。。

静态代理能够在不修改目标对象的功能前提下,对目标功能进行扩展。

缺点是:

由于代理对象须要与目标对象实现同样的接口,因此会有不少的代理类,类太多。同时,一旦接口增长方法,目标对象与代理对象都要维护。

解决此缺点的方法就是可使用动态代理方式

3.动态代理

动态代理模式:在程序运行期间动态生成代理类。spring的aop面向切面编程就是使用动态代理模式来实现的。

JVM能够本身建立代理类,这样不只提升了效率,并且更灵活。动态代理的3个主要的类:

proxy类、InvocationHandler、Method

经过proxy类和InvocationHandler接口能够生成JDK动态代理类和动态代理对象。

对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。能够兑实际的实现进行一些特殊的处理,像spring aop中的各类Adevice.

Proxy类中主要有3个方法:

protected Proxy(InvocationHandler h) //构造方法

 public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

每一个代理实例都必须指定一个调用处理器,代理对象调用方法时,该方法会指派到调用处理器的invoke()中去。代理的方法封装成invoke中的method对象,其中的参数封装成Object[].

动态代理实现的步骤:

1)写一个代理类实现InvocationHandler接口,经过构造函数把代理对象(具体目标类)传入到此处理器中,在invoke方法中增长method.invoke(realSubject,args)。

2)在调用方法时,经过java.lang.reflect.Proxy的newProxyInstance来获取代理实现类,生成代理对象时,直接调用方法便可。

代码示例:

3.1)被代理的接口

/**
 * BusinessInterface class
 * 代理接口
 * @author guanhuifang
 * @date 2018/2/24 下午5:49
 **/
public interface BusinessInterface {

    public void doWork();
}

3.2)具体实现类,被代理对象

/**
 * Business class
 *具体实现类,被代理对象
 * @author guanhuifang
 * @date 2018/2/24 下午5:50
 **/
public class Business implements BusinessInterface {
    @Override
    public void doWork() {
        System.out.println("进行业务逻辑的处理");
    }
}

3.3)动态代理类

/**
 * BusinessHandler class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午5:51
 **/
public class BusinessHandler implements InvocationHandler {

    private BusinessInterface businessInterface;

    public BusinessHandler(BusinessInterface businessInterface) {
        this.businessInterface = businessInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("处理业务逻辑以前。");
        method.invoke(businessInterface, args);
        System.out.println("处理业务逻辑以后。");
        return null;
    }
}

3.4)调用

/**
 * Client class
 *
 * @author guanhuifang
 * @date 2018/2/24 下午5:54
 **/
public class Client {

    public static void main(String[] args) {
        //具体实现类对象
        Business business = new Business();

        //生成代理类对象,利用proxy的静态方法Proxy.newProxyInstance()来为一组接口动态的生成代理类及对象,也就是动态生成代理对象的方法
        BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(business.getClass().getClassLoader(),business.getClass().getInterfaces(),new BusinessHandler(business));
        proxy.doWork();  //调用代理类的对象
      
     
    }
}

参数说明:
 business.getClass().getClassLoader()  为具体实现类business的classLoader,负责加载动态代理类
 business.getClass().getInterfaces()   为具体实现类business的全部接口
 new BusinessHandler(business)   代理类,把方法调用转到代理上

运行结果:

处理业务逻辑以前。
进行业务逻辑的处理
处理业务逻辑以后。

Process finished with exit code 0

4.Cglib代理

因为静态代理和动态代理模式的目标对象都是实现一个接口的目标对象,但有时候目标对象是一个单独的对象,并无任何实现的接口,这个时候就用上了CGLIB代理模式。

其实spring中的AOP,有两种代理模式,一种是jdk动态代理,另外一种是cglib代理。jdk动态代理要求被代理的目标对象必须是接口,cglib动态代理,也叫子类代理,它是在内存中建立一个子类对象来实现对目标类功能的扩展。CGLIB代理会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理,如前置处理和后置处理。在CGLIB底层,实际上是借助了ASM这个强大的java字节码生成框架。

示例代码:

引入cglib依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>

被代理类:

/**
 * Hello class
 *
 * @author guanhuifang
 * @date 2018/2/25 上午11:52
 **/
public class Hello {
    public void sayHello() {
        System.out.println("Hello World!");
    }
}

实现MethodInterceptor接口生成方法拦截器

/**
 * HelloMethodInterceptor class
 *
 * @author guanhuifang
 * @date 2018/2/25 上午11:55
 **/
public class HelloMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("以前"+method.getName());
        methodProxy.invokeSuper(o,objects);
        System.out.println("以后"+method.getName());
        return null;
    }
}

客户端验证:

/**
 * Client class
 *
 * @author guanhuifang
 * @date 2018/2/25 上午11:59
 **/
public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Hello.class); //继承被代理类
        enhancer.setCallback(new HelloMethodInterceptor()); //设置回调
        Hello hello = (Hello) enhancer.create(); //生成代理类对象
        hello.sayHello();
    }
}

运行结果:

以前sayHello
Hello World!
以后sayHello

Process finished with exit code 0

在enhance.create()建立完代理类对象以后,在代理类调用方法中会被咱们实现的方法拦截器HelloMethodInterceptor拦截。若是被代理类hello被final修饰,则hello类不能被继承,即不能被代理。一样,若是被代理类hello类存在final修饰的方法,那么该方法不能被代理。

相关文章
相关标签/搜索