设计模式-代理模式

分类:

  代理模式通常来说有三种类型,分别是静态代理、JDK动态代理、Cglib代理

组成:
  抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

代理模式主要功能:

  为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

简单来说就是屏蔽掉用户的直接操作对象,提供一个第三方api来完成操作。

 

静态代理DEMO:

  接口-抽象类:

public interface IUserDao {
    void save();
}

  实现类:

public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("数据插入到数据库!");
    }

}

  代理类:

public class DaoProxy implements IUserDao{

    private IUserDao userDao;

    public DaoProxy(IUserDao userDao) {
        this.userDao=userDao;
    }


    @Override
    public void save() {
        System.out.println("开启事物");
        userDao.save();
        System.out.println("关闭事物");
    }
}

  测试:

public class MainTest {
    public static void main(String[] args){

        DaoProxy proxy= new DaoProxy(new UserDao());
        proxy.save();
    }
}
//打印数据
  开启事物
  数据插入到数据库!
  关闭事物

  这是静态代理,可以在不修改目标对象代码的情况下,对目标进行扩展。

  但是静态代理维护起来比较麻烦,如果在接口中增加方法,实现类与代理类同时需要维护,

  并且静态代理基本都是作用于类已知的状态下,目标类是已知才可以使用静态代理,如果目标类很多的话,很多时候就要写很多代理类,这就比较麻烦。

JDK的动态代理:

  先来看一下需要用到的类,接上一章反射用到的类

  java.lang.reflect.Proxy //Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

 

  看一下这个类的API,有这么一个方法,使用这个方法可以返回代理类的实例;

 

  java.lang.reflect.InvocationHandler //看一下这个接口的api介绍

  

  实现这个类为我们具体执行方法的处理器类。

在接静态代理的基础上,去除掉针对每个目标类的代理类,建立一个类工厂

  工厂类:

public class ProxyFactory {

    //维护一个目标对象
    private Object target;

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

    //给目标对象生成代理对象
    public Object getProxyInstance() {
        Object proxy = null;
        Class classObj=  target.getClass(); //动态代理里面反射是必须的
        proxy= Proxy.newProxyInstance(classObj.getClassLoader(),//获取类加载器
                classObj.getInterfaces(),
                new InsertHandler(target)//根据传入的对象创建处理器类
        );
        return proxy;
    }
}

  处理器类:

public class InsertHandler implements InvocationHandler {

    private Object target;

    public InsertHandler( Object target) {
        this.target=target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始事物!");
        Object invoke = method.invoke(target, args);
        System.out.println("提交事物!");
        return invoke;
    }
}

  测试:

public class MainTest {
    public static void main(String[] args){
        IUserDao target = new UserDao();
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        proxy.save();//执行这个方法的时候实际上是去执行了代理类中的invoke方法
        System.out.println("---------分割线-----------");
        proxy.delete();
    }
}

//打印
  开始事物!
  数据插入到数据库!
  提交事物!
  ---------分割线-----------
  开始事物!
  数据库删除数据!
  提交事物!

  使用jdk的动态代理还是很方便的,不过有个前提条件,就是目标对象一定要实现接口。

 Cglib动态代理:

  看一下介绍:

  CGLIB是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

  CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping
工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(mock)对象
来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。
  CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。(摘自百度百科)
  
  我没有在网上找到它的api或者官方文档,只是一个开源项目。
  而且单独的使用CGLIB的话需要引用其jar包,不过因为我是在web中写的,在spring-core包含着CGLIB的包,就不贴怎么加jar包了。
  
  注意:
  要代理的方法不能为final fina方法无法执行代理操作,同时static方法 也无法被代理,static方法是属于类的,不是对象的。
  
  DEMO:
  修改目标类,无接口或者有接口。
  
public class UserDao {
    public void save() {
        System.out.println("数据插入到数据库!");
    }

    public void delete() {
        System.out.println("数据库删除数据!");
    }
    public static void update() {
        System.out.println("修改数据库数据!");
    }
    public final void finalMethod() {
        System.out.println("final方法!");
    }

}

  代理工厂类:

  

public class ProxyFactory implements MethodInterceptor {


    private Object target; //维护一个目标对象

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

    /**
     * 给目标对象创建一个代理对象
     * @return
     */
    public Object getProxyInstance() {
        Enhancer en = new Enhancer();
        en.setSuperclass(target.getClass());
        en.setCallback(this);
        return en.create();

    }
    /**
     * 用于目标方法
     * @return
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始事务");
        Object invoke = method.invoke(target, objects);
        System.out.println("提交事务");
        return invoke;
    }
}

  测试:

public class MainTest {
    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        UserDao proxy = (UserDao) new ProxyFactory(userDao).getProxyInstance();
        proxy.save();
        System.out.println("--------分割线---------");
        proxy.delete();
        System.out.println("--------分割线---------");
        proxy.finalMethod();

    }
}

//打印
  开始事务
  数据插入到数据库!
  提交事务
  --------分割线---------
  开始事务
  数据库删除数据!
  提交事务
  --------分割线---------
  final方法!

 

  
  像spring-mybatis中事物控制,就是使用的动态代理,类似的aop编程,默认是有接口使用jdk动态代理,没有接口则使用的CGLIB,不过可以更改其默认强制使用CGLIB.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

 

 

 

 

 

 

    

 

posted @ 2017-07-24 11:55  LewsKay  阅读(216)  评论(2编辑  收藏  举报