一.代理模式
在不更改源码的前提下,加入新功能,通常需要用到代理设计模式。
代理设计模式分类:
静态代理
动态代理
jdk动态代理
cglib动态代理
其中spring AOP的底层用的是动态代理。其将动态代理进行封装,方便使用。
以通过Service调用DAO,从而向数据库添加更新用户为例,添加新的事务功能。
业务模型:
1 package com.hdu.dao; 2 3 public interface IUserDao { 4 public int addrUser(); 5 public int updateUser(); 6 }
1 package com.hdu.dao.impl; 2 3 import com.hdu.dao.IUserDao; 4 5 public class UserDaoImpl implements IUserDao { 6 7 @Override 8 public int addrUser() { 9 System.out.println("UserDaoImpl.addUser()"); 10 return 1; 11 } 12 13 @Override 14 public int updateUser() { 15 System.out.println("UserDaoImpl.updateUser()"); 16 return 1; 17 } 18 }
1 package com.hdu.service; 2 3 public interface IUserService { 4 public boolean addUser(); 5 public boolean updateUser(); 6 7 }
1 package com.hdu.service.impl; 2 3 import com.hdu.dao.IUserDao; 4 import com.hdu.dao.impl.UserDaoImpl; 5 import com.hdu.service.IUserService; 6 7 public class UserServiceImpl implements IUserService { 8 //应该用spring进行解耦,本次重点在于代理,故简化操作 9 private IUserDao userDao = new UserDaoImpl(); 10 11 @Override 12 public boolean addUser() { 13 System.out.println("UserServiceImpl.addUser()"); 14 boolean flag = false; 15 int rowAffect = userDao.addrUser(); 16 if(rowAffect==1) { 17 flag = true; 18 } 19 return flag; 20 } 21 22 @Override 23 public boolean updateUser() { 24 System.out.println("UserServiceImpl.updateUser()"); 25 boolean flag = false; 26 int rowAffect = userDao.updateUser(); 27 if(rowAffect==1) { 28 flag = true; 29 } 30 return flag; 31 } 32 33 }
新增事务功能:
二.无代理(组合方式)
此时,需要改动源码,即在UserServiceImpl中加入TransactionManager,破坏了开闭原则。
1 package com.hdu.service.impl; 2 3 import com.hdu.dao.IUserDao; 4 import com.hdu.dao.impl.UserDaoImpl; 5 import com.hdu.other.TransactionManager; 6 import com.hdu.service.IUserService; 7 8 public class UserServiceImpl implements IUserService { 9 //应该用spring进行解耦,本次重点在于代理,故简化操作 10 private IUserDao userDao = new UserDaoImpl(); 11 private TransactionManager tm = new TransactionManager(); 12 13 @Override 14 public boolean addUser() { 15 tm.begin(); 16 boolean flag = false; 17 System.out.println("UserServiceImpl.addUser()"); 18 int rowAffect = userDao.addrUser(); 19 if(rowAffect==1) { 20 flag = true; 21 tm.commit(); 22 } 23 return flag; 24 } 25 26 @Override 27 public boolean updateUser() { 28 tm.begin(); 29 System.out.println("UserServiceImpl.updateUser()"); 30 boolean flag = false; 31 int rowAffect = userDao.updateUser(); 32 if(rowAffect==1) { 33 flag = true; 34 tm.commit(); 35 } 36 return flag; 37 } 38 39 }
测试:
1 package com.hdu.test; 2 3 import org.junit.Test; 4 5 import com.hdu.service.IUserService; 6 import com.hdu.service.impl.UserServiceImpl; 7 8 public class TestNoProxy { 9 @Test 10 public void testMethod1() { 11 IUserService userService = new UserServiceImpl(); 12 userService.addUser(); 13 System.out.println(); 14 userService.updateUser(); 15 } 16 }
三.静态代理
静态代理在编译期就已经写好了代理类,在编译期间确定了耦合关系。
优点:在无需改动目标类或目标对象的源码,不破坏开闭原则的前提下,增加了新的功能。
缺点:实现了实际的业务根新的功能解耦(UserServiceImpl和TransactionManager解耦),但用另一种方式静态代理的方式把(UserServiceImpl和TransactionManager耦合)。
代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。而一个静态代理类只能代理一个类,代码复用度不高。
1 package com.hdu.proxy; 2 3 import com.hdu.other.TransactionManager; 4 import com.hdu.service.IUserService; 5 6 /** 7 * 本类是一个静态代理类,职责是把业务和新功能结合在一起,尽量降低耦合. 8 * 实现接口是耦合,抽象耦合,耦合的目的,约束次代理类的功能. 9 *private TransactionManager tm;具体耦合,用组合方式 10 *private IUserService userService;抽象耦合,用组合方式 完成具体业务功能 11 */ 12 public class StaticProxy implements IUserService { 13 private TransactionManager tm; 14 private IUserService userService; 15 public StaticProxy(TransactionManager tm, IUserService userService) { 16 this.tm = tm; 17 this.userService = userService; 18 } 19 @Override 20 public boolean addUser() { 21 boolean flag = false; 22 try { 23 tm.begin(); 24 userService.addUser(); 25 tm.commit(); 26 flag = true; 27 }catch(Exception e) { 28 e.printStackTrace(); 29 tm.rollback(); 30 } 31 return flag; 32 } 33 34 @Override 35 public boolean updateUser() { 36 boolean flag = false; 37 try { 38 tm.begin(); 39 userService.updateUser(); 40 tm.commit(); 41 flag = true; 42 }catch(Exception e) { 43 e.printStackTrace(); 44 tm.rollback(); 45 } 46 return flag; 47 } 48 49 }
测试:
1 package com.hdu.test; 2 3 import org.junit.Test; 4 5 import com.hdu.other.TransactionManager; 6 import com.hdu.proxy.StaticProxy; 7 import com.hdu.service.IUserService; 8 import com.hdu.service.impl.UserServiceImpl; 9 10 public class TestStaticProxy { 11 @Test 12 public void testMethod1() { 13 //不用静态代理的方式(无事务管理) 14 IUserService userService = new UserServiceImpl(); 15 userService.addUser(); 16 userService.updateUser(); 17 } 18 19 @Test 20 //带事务管理 21 public void testMethod2() { 22 TransactionManager tm = new TransactionManager(); 23 IUserService userService = new UserServiceImpl(); 24 25 StaticProxy sp = new StaticProxy(tm, userService); 26 sp.addUser(); 27 System.out.println(); 28 sp.updateUser(); 29 30 } 31 }
无事务管理:
带事务管理:
四.动态代理
动态代理,是在运行期间来确定代理对象在运行期间确定实际业务和额外的新功能之间的关系。
jdk动态代理, 要求业务类必须有接口,没有接口无法生成代理对象;而cglib动态代理,不要求业务类必须有接口,有实现类即可。
优点:① 静态代理,每一个业务都对应一个静态的代理对象。
动态代理,InvocationHandler的子实现的复用率高。
缺点:实际业务类里的所有的方法都要添加额外的功能,不能实现只给部分方法添加额外的功能。
4.1 jdk动态代理
Proxy是JDK中用来创建代理对象的类,其中使用最多的是newProxyInstance方法来创建代理对象。
要求业务类必须有接口,没有接口无法生成代理对象。
public static Object newProxyInstance( ClassLoader loader, //由哪个类加载器加载生成代理对象 Class<?>[] interfaces, //接口对象数组,必须传入目标对象的接口 InvocationHandler h) //表示一个invocationHandler对象,当代理对象执行方法时,会调用改InvocatonHandler对象 throws IllegalArgumentException
package com.hdu.proxy; import java.lang.reflect.Proxy; public class JDKProxy { /** * 专门用来获取代理对象的方法,是通过jdk给提供的api来获取代理对象 * @param targetObject 目标对象,就是实际业务的对象, * 比如:UserServiceImpl类的对象 就是目标对象 * @return Object obj 目标对象的代理对象,代理对象是跟目标对象是兄弟关系,所以目标对象所对应的类,必须有接口 */ public static Object getProxyObject(Object targetObject) { Object obj=null; /** * Object obj就是代理对象 * 一参:目标对象的类加载器,获取到类路径 * 二参:目标对象的接口,根据接口造出接口的对象 * 三参:InvocationHandler接口的回调,实际就是实际业务和额外新功能的耦合类 */ obj=Proxy.newProxyInstance( targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), new TransactionHandler(targetObject)); return obj; } }
package com.hdu.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import com.hdu.other.TransactionManager; public class TransactionHandler implements InvocationHandler { private Object targetObject; public TransactionHandler(Object targetObject) { this.targetObject=targetObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj=null; TransactionManager tm=new TransactionManager(); try { tm.begin(); //由目标对象调用实际的功能方法 obj=method.invoke(targetObject, args);//targetObject.addUser(); tm.commit(); }catch(Exception e) { e.printStackTrace(); tm.rollback(); } return obj; } }
测试:
1 package com.hdu.test; 2 3 import org.junit.Test; 4 5 import com.hdu.entity.User; 6 import com.hdu.proxy.JDKProxy; 7 import com.hdu.service.UserService; 8 import com.hdu.service.impl.UserServiceImpl; 9 10 public class TestDynamicProxy { 11 @Test 12 public void testMethod1() { 13 //不用静态代理的方式(无事务管理) 14 UserService targetObject=new UserServiceImpl(); 15 targetObject.addUser(new User()); 16 targetObject.updateUser(new User()); 17 } 18 @Test 19 public void testMethd2() { 20 //targetObject根proxyObject是兄弟关系,是隶属于同一个接口 21 //带事务管理,用动态代理方式 22 UserServiceImpl targetObject=new UserServiceImpl(); 23 //送进去目标对象,出来的是代理对象 24 UserService proxyObject=(UserService)JDKProxy.getProxyObject(targetObject); 25 System.out.println(proxyObject.getClass()); 26 27 //是用代理对象调用方法 28 proxyObject.addUser(new User()); 29 System.out.println(); 30 proxyObject.updateUser(new User()); 31 } 32 }
4.2 cglib动态代理
不要求业务类必须有接口,有实现类即可。
需要导入jar包:
1 package com.hdu.proxy; 2 3 import net.sf.cglib.proxy.Enhancer; 4 5 public class CGLIBProxy { 6 /** 7 * 专门用来获取代理对象的方法,是通过jdk给提供的api来获取代理对象 8 * @param targetObject 目标对象,就是实际业务的对象, 9 * 比如:UserServiceImpl类的对象 就是目标对象 10 * @return Object obj 目标对象的代理对象,代理对象是跟目标对象是父子关系,目标对象是长辈, 代理对象小辈。所以目标对象所对应的类,不能是final的。 11 */ 12 public static Object getProxyObject(Object targetObject) { 13 Object obj=null; 14 Enhancer enhancer=new Enhancer(); 15 enhancer.setSuperclass(targetObject.getClass()); 16 enhancer.setCallback(new TransactionInterceptory(targetObject)); 17 obj=enhancer.create(); 18 return obj; 19 } 20 }
1 package com.hdu.proxy; 2 3 import java.lang.reflect.Method; 4 5 import com.hdu.other.TransactionManager; 6 7 import net.sf.cglib.proxy.MethodInterceptor; 8 import net.sf.cglib.proxy.MethodProxy; 9 10 public class TransactionInterceptory implements MethodInterceptor{ 11 private Object targetObject; 12 public TransactionInterceptory(Object targetObject) { 13 this.targetObject=targetObject; 14 } 15 @Override 16 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 17 Object obj=null; 18 try { 19 TransactionManager tm=new TransactionManager(); 20 tm.begin(); 21 obj=method.invoke(targetObject, args); 22 tm.commit(); 23 }catch(Exception e) { 24 e.printStackTrace(); 25 } 26 return obj; 27 } 28 }
测试:
1 package com.hdu.test; 2 3 import org.junit.Test; 4 5 import com.hdu.entity.User; 6 import com.hdu.proxy.CGLIBProxy; 7 import com.hdu.service.UserService; 8 import com.hdu.service.impl.UserServiceImpl; 9 10 public class TestDynamicProxy { 11 @Test 12 public void testMethod1() { 13 //不用静态代理的方式(无事务管理) 14 UserService targetObject=new UserServiceImpl(); 15 targetObject.addUser(new User()); 16 targetObject.updateUser(new User()); 17 } 18 @Test 19 public void testMethd2() { 20 //targetObject根proxyObject是父子关系 21 //带事务管理,用动态代理方式 22 UserServiceImpl targetObject=new UserServiceImpl(); 23 //送进去目标对象,出来的是代理对象 24 UserService proxyObject=(UserService)CGLIBProxy.getProxyObject(targetObject); 25 System.out.println(proxyObject.getClass()); 26 //是用代理对象调用方法 27 proxyObject.addUser(new User()); 28 System.out.println(); 29 proxyObject.updateUser(new User()); 30 31 } 32 }
五.小结
代理实现方式 |
说明 |
静态代理 |
一个类一个代理,重复代码仍然较多,复用度低。 静态代理:事先写好代理对象类,在程序发布前就已经存在了。 调用真实业务方法 |
jdk 动态代理 |
动态代理:应用程序发布后,通过动态创建代理对象。 通过反射进行invoke回调 要求业务类必须有接口,没有接口无法生成代理对象 class com.sun.proxy.$Proxy4 |
cglib 动态代理 |
不要求业务类必须有接口,有实现类即可 底层采用asm字节码生成框架生成代理类的字节码,它创建慢,但一旦创建要比jdk方式快。 cglib使用继承,因此父类最好不要有final属性,final阻止继承和多态。 class com.hdu.service.impl.UserServiceImplEnhancerByCGLIB12d88161 |
ASM 字节码生成框架 |
ASM是一个Java字节码生成框架,它能够以二进制形式修改已有类欧哲动态生成类。ASM可也直接产生二进制class文件,也可以在类被加载如JVM前动态改变类行为,ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇报指令级别。这要求就高了,对开发者要对class的结构和JVM指令要有一定了解。 |