代理模式
一、解决何种问题?
将 “主要业务”和“次要业务”做解耦合处理
二、“主要业务”和“次要业务”的区分
“主要业务”:要实现的关键性任务
“次要业务”:起到辅助功能,辅助“主要业务”顺利实现,在项目中,“次要业务”往往大量重复出现。因此大量重复编写“次要业务”往往会影响开发效率
三、以使用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("\n----------------------------"); 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("\n----------------------------"); 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代理;