代理模式

一、解决何种问题?

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

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

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

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

三、以使用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代理;

原文地址:https://www.cnblogs.com/zhangxianming/p/9903288.html