zoukankan      html  css  js  c++  java
  • 设计模式-代理模式

    分类:

      代理模式通常来说有三种类型,分别是静态代理、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.
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

        

  • 相关阅读:
    第三届蓝桥杯C++B组国(决)赛真题
    第二届蓝桥杯C++B组国(决)赛真题
    第二届蓝桥杯C++B组国(决)赛真题
    第二届蓝桥杯C++B组国(决)赛真题
    第二届蓝桥杯C++B组国(决)赛真题
    第二届蓝桥杯C++B组国(决)赛真题
    Java实现矩阵相乘问题
    Java实现矩阵相乘问题
    Java实现矩阵相乘问题
    Java实现矩阵相乘问题
  • 原文地址:https://www.cnblogs.com/lewskay/p/7228191.html
Copyright © 2011-2022 走看看