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

    一、解决何种问题?

      将 “主要业务”和“次要业务”做解耦合处理

    二、“主要业务”和“次要业务”的区分

      “主要业务”:要实现的关键性任务

      “次要业务”:起到辅助功能,辅助“主要业务”顺利实现,在项目中,“次要业务”往往大量重复出现。因此大量重复编写“次要业务”往往会影响开发效率

    三、以使用JDBC操作数据库为例,来划分“主要业务”和“次要业务”

      1、加载数据库驱动(次要业务)

      2、建立连接(次要业务)

      3、执行sql语句(主要业务)

      4、遍历结果集(次要业务)

      5、关闭资源(次要业务)

    四、JDK动态代理

      1、组成

        · 接口:声明需要被监听的行为

        · 代理实现类(实现InvocationHandler接口):将“次要业务”和"主要业务"绑定执行

        · 代理目标对象:执行“主要业务”

      2、特点:

        · 代理对象,不需要实现接口;

        · 代理对象的生成,是利用JDK  API, 动态的在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型;

        · 动态代理, JDK代理, 接口代理;  

        · 代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能用动态代理!

      3、代码示例

        · 创建Interface

    1 package com.kkb.jdk.proxy;
    2 
    3 /**
    4  * 声明需要被监听的方法:findByShopId、findByUserId
    5  */
    6 public interface IOrderDao {
    7     Object findByShopId(int shopId);
    8     Object findByUserId(int userId);
    9 }

         · 创建实现类

     1 package com.kkb.jdk.proxy;
     2 
     3 public class OrderDao implements IOrderDao{
     4     @Override
     5     public Object findByShopId(int shopId) {
     6         System.out.println("执行根据 shopId 查询订单的 sql 语句");
     7         return null;
     8     }
     9 
    10     @Override
    11     public Object findByUserId(int userId) {
    12         System.out.println("执行根据 userId 查询订单的 sql 语句");
    13         return null;
    14     }
    15 }

        · 创建代理实现类

     1 package com.kkb.jdk.proxy;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 
     6 /**
     7  * 代理实现类(实现InvocationHandler接口):用于“主要业务”和“次要业务”绑定执行
     8  */
     9 public class Agent implements InvocationHandler {
    10 
    11     private Object target;
    12 
    13     public Agent(Object target){
    14         this.target = target;
    15     }
    16 
    17     @Override
    18     public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    19         Object returnValue  = null;
    20         prePrimaryService();//执行次要业务
    21         returnValue  = method.invoke(target,params);//执行主要业务(method:需要执行的目标对象的方法,target:目标对象 params:目标对象的方法所需要的参数)
    22         afterPrimaryService();//执行次要业务
    23         return returnValue ;
    24     }
    25 
    26     /**
    27      * 执行在主要业务之前的次要业务
    28      */
    29     private void prePrimaryService(){
    30         System.out.println("加载数据库驱动");
    31         System.out.println("建立连接");
    32     }
    33 
    34     /**
    35      * 执行在主要业务之后的次要业务
    36      */
    37     private void afterPrimaryService(){
    38         System.out.println("遍历结果集");
    39         System.out.println("关闭资源");
    40     }
    41 }

        · 创建代理类工厂

     1 package com.kkb.jdk.proxy;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Proxy;
     5 
     6 /**
     7  * 代理实现类的工厂
     8  */
     9 public class ProxyFactory {
    10     public static Object getInstance(IOrderDao orderDao){
    11         //将目标对象传给代理实现类
    12         InvocationHandler handler = new Agent(orderDao);
    13         //ClassLoader loader: 代理对象的字节码文件的地址
    14         //Class<?>[] interfaces: 需要监听的接口
    15         //InvocationHandler h :代理实现类
    16         return Proxy.newProxyInstance(orderDao.getClass().getClassLoader(),orderDao.getClass().getInterfaces(),handler);
    17 
    18     }
    19 }

        · 创建测试类

     1 package com.kkb.jdk.proxy;
     2 
     3 public class Client {
     4     public static void main(String[] args) {
     5         //代理目标对象
     6         IOrderDao orderDao = new OrderDao();
     7         //代理对象
     8         IOrderDao proxy = (IOrderDao)ProxyFactory.getInstance(orderDao);
     9 
    10         proxy.findByShopId(0);
    11         System.out.println("
    ----------------------------");
    12         proxy.findByUserId(0);
    13     }
    14 }

        · 打印测试结果

    加载数据库驱动
    建立连接
    执行根据 shopId 查询订单的 sql 语句
    遍历结果集
    关闭资源
    
    ----------------------------
    加载数据库驱动
    建立连接
    执行根据 userId 查询订单的 sql 语句
    遍历结果集
    关闭资源

     五、Cglib动态代理

      1、组成

        · 代理实现类(实现MethodInterceptor接口):将“次要业务”和"主要业务"绑定执行

        · 代理目标对象:执行“主要业务”

      2、特点

        · Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展;

        · 目标对象不需要实现接口;

        · 需要引入需要引入cglib – jar文件;

        · 代理的类不能为final, 否则报错。目标对象的方法如果为final/static, 那么就不会被拦截;

      3、代码实例

        · 代理类

     1 package com.kkb.cglib.proxy;
     2 
     3 import net.sf.cglib.proxy.Enhancer;
     4 import net.sf.cglib.proxy.MethodInterceptor;
     5 import net.sf.cglib.proxy.MethodProxy;
     6 import sun.management.MethodInfo;
     7 import sun.reflect.MethodAccessor;
     8 
     9 import java.lang.reflect.Method;
    10 
    11 /**
    12  * Cglib 代理实现类,实现MethodInterceptor接口
    13  */
    14 public class CglibAgent implements MethodInterceptor {
    15     //代理目标对象
    16     private Object target;
    17 
    18     public CglibAgent(Object target) {
    19         this.target = target;
    20     }
    21 
    22     //给目标对象创建代理对象
    23     public  Object getProxyInstance(){
    24         //工具类
    25         Enhancer enhancer = new Enhancer();
    26         //设置父类
    27         enhancer.setSuperclass(target.getClass());
    28         //设置回调函数
    29         enhancer.setCallback(this);
    30         //创建子类
    31         return enhancer.create();
    32     }
    33 
    34     @Override
    35     public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
    36         prePrimaryService();//执行次要业务
    37         //执行主要业务(method:需要执行的目标对象的方法,target:目标对象 params:目标对象的方法所需要的参数)
    38         Object returnValue = method.invoke(target,params);
    39         afterPrimaryService();//执行次要业务
    40         return returnValue;//目标对象方法的返回值
    41     }
    42 
    43     /**
    44      * 执行在主要业务之前的次要业务
    45      */
    46     private void prePrimaryService(){
    47         System.out.println("加载数据库驱动");
    48         System.out.println("建立连接");
    49     }
    50 
    51     /**
    52      * 执行在主要业务之后的次要业务
    53      */
    54     private void afterPrimaryService(){
    55         System.out.println("遍历结果集");
    56         System.out.println("关闭资源");
    57     }
    58 }

        · 创建测试类

     1 package com.kkb.cglib.proxy;
     2 
     3 import com.kkb.jdk.proxy.ProxyFactory;
     4 
     5 public class Client {
     6     public static void main(String[] args) {
     7         //代理目标对象
     8         IOrderDao orderDao = new OrderDao();
     9         //代理
    10         IOrderDao proxy = (IOrderDao) new CglibAgent(orderDao).getProxyInstance();
    11 
    12         proxy.findByShopId(0);
    13         System.out.println("
    ----------------------------");
    14         proxy.findByUserId(0);
    15     }
    16 }

        ·打印测试结果

     1 加载数据库驱动
     2 建立连接
     3 执行根据 shopId 查询订单的 sql 语句
     4 遍历结果集
     5 关闭资源
     6 
     7 ----------------------------
     8 加载数据库驱动
     9 建立连接
    10 执行根据 userId 查询订单的 sql 语句
    11 遍历结果集
    12 关闭资源

    六、总结

      在Spring的AOP编程中,如果加入容器的目标对象有实现接口,用JDK代理;如果目标对象没有实现接口,用Cglib代理;

  • 相关阅读:
    应用程序框架实战十三:DDD分层架构之我见
    Util应用程序框架公共操作类(三):数据类型转换公共操作类(扩展篇)
    Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)
    不能使用 float 和 double 来表示金额等精确的值
    JVM 字节码指令手册
    MyBatis: Invalid bound statement (not found)错误的可能原因
    Oracle:ORA-01219:database not open:queries allowed on fixed tables/views only
    手写 Spring MVC
    8080 端口被占用的解决方法 netstat -ano;taskkill (命令行)
    Java 工具类 IpUtil
  • 原文地址:https://www.cnblogs.com/zhangxianming/p/9903288.html
Copyright © 2011-2022 走看看