代理模式:
作用:为目标对象提供一个代理对象以控制对目标对象的访问。说明:代理对象存在的价值:主要用于拦截对真实业务对象(目标对象)的访问应用:拦截器等
静态代理:
代理类和目标类必须实现相同的接口或者是继承相同父类。静态代理模式的缺点: 1,如果一个系统中有100个目标(被代理)对象,则要创建100个代理对象 2,如果一个目标对象中有很多方法需要事务,则代理对象的很多方法中都需要写事务相关的代码,重复代码比较多 3,当我们在目标类中增加了一个方法时,代理类中也要增加相应方法。 3,总结:静态代理中,代理对象的重用性不强***代理模式与装饰模式的比较: 1)在装饰模式中,装饰实现类和被装饰的类实现了共同的原始接口;在代理模式中,代理类和目标类实现了相同的接口或继承了相同的父类 2)装饰模式是给被装饰的类对象添加一个或多个功能(即增强功能);代理模式是对代理对象的使用加以控制,并不提供对象本身的增强功能。eg: // 实现了UserDao接口的目标类 public class UserDaoImpl implements UserDao{ [@Override](https://my.oschina.net/u/1162528) public void deleteUser() { System.out.println("delete user"); } } // 实现了UserDao接口的代理类 public class UserDaoImplProxy implements UserDao { // 代理的目标对象 private UserDao userDao; public UserDaoImplProxy(UserDao userDao) { this.userDao = userDao; } [@Override](https://my.oschina.net/u/1162528) public void deleteUser() { System.out.println("== 开始事务 =="); userDao.deleteUser(); System.out.println("== 提交事务 =="); } } // 测试类 public class ProxyTest { [@Test](https://my.oschina.net/azibug) public void test() { // 创建目标对象 UserDao userDaoImpl = new UserDaoImpl(); // 创建代理对象 UserDao userDaoImplProxy = new UserDaoImplProxy(userDaoImpl); // 执行方法(使用的是代理对象) userDaoImplProxy.deleteUser(); // 使用代理对象 } } console: == 开始事务 == delete user == 提交事务 ==
动态代理:
1,代理对象和目标对象实现了共同的接口 jdk动态代理($Proxy): 1)JVM可以在运行期间动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类 2)JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理 3)Java提供了一个java.lang.reflect.Proxy类,调用它的newProxyInstance方法可以生成目标对象的代理对象 public static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException 参数: loader - 定义代理类的类加载器,一般采用跟目标对象相同的类加载器 interfaces - 代理类要实现的接口列表 h - 指派方法调用的调用处理程序,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法 返回:一个带有指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口 4)每个代理实例都具有一个关联的调用处理程序,这个调用处理程序需要实现InvocationHandler接口。代理对象调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法。 java.lang.reflect包下的InvocationHandler接口中的invoke方法: Object invoke(Object proxy, Method method, Object[] args) throws Throwable{} 参数: proxy - 代理对象 method - 代理对象调用的(接口)方法的 Method 实例。 args - 代理对象调用的(接口)方法的参数值的对象数组 返回:方法的返回值,方法没有返回值(void)时返回null jdk动态代理总结: 1)jdk动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口这两个来完成的。 2)要使用JDK动态代理,必须要定义接口。 3)JDK动态代理将会拦截所有pubic的方法(因为只能调用接口中定义的方法),这样即使在接口中增加了新的方法,其他地方不用修改,新的方法也会被拦截。 4)如果只想拦截一部分方法,可以在invoke方法中对要执行的方法名进行判断。 eg: // 实现了UserDao接口的目标类 public class UserDaoImpl implements UserDao{ public void deleteUser() { System.out.println("delete User"); } public void saveUser() { System.out.println("save User"); } } // 调用处理程序:实现了InvocationHandler(调用处理器)的拦截器 public class TransactionInterceptor implements InvocationHandler{ private UserDao target;//代理的目标对象 private Transaction transaction;//添加事务 public TransactionInterceptor(UserDao target, Transaction transaction){ this.target = target; this.transaction = transaction; } [@Override](https://my.oschina.net/u/1162528) public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { this.transaction.beginTransaction(); Object result = method.invoke(this.target, args);//调用目标类的方法 this.transaction.commit(); return result; } } // 提供事务操作的类 public class Transaction { public void beginTransaction(){ System.out.println("开启事务 "); } public void commit(){ System.out.println("事务提交"); } } // 测试类 public class JdkProxyTest { [@Test](https://my.oschina.net/azibug) public void test(){ // 创建目标对象 UserDao target = new UserDaoImpl(); // 创建调用处理器 TransactionInterceptor transactionInterceptor = new TransactionInterceptor(target, new Transaction()); // 用Proxy创建jdk动态代理对象 UserDao userDao = (UserDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), transactionInterceptor); // 执行方法(使用的是代理对象) userDao.deleteUser(); } } Console: 开启事务 delete user 事务提交2,代理对象是目标对象的子类 hibernate:Person person = session.load(Person.class,1L); javassisit cglib cglib动态代理: 1)JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。 2)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。 3)CGLIB动态代理是使用net.sf.cglib.proxy.MethodInterceptor接口完成的。 MethodInterceptor接口中的intercept方法 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {} 参数: proxy - 代理对象 method - 代理对象调用的(父类)方法的 Method 实例。 args - 代理对象调用的(父类)方法的参数值的对象数组 methodProxy - 使用它调用(父类)的方法 返回:方法的返回值,方法没有返回值(void)时返回null CGLIB代理总结: 1)CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。 2)要求类不能是final的,要拦截的方法要是非final、非static、非private的。 // 目标类:此类不能是final的,否则不能有子类,CGLIB也就不能工作了 public class UserDaoImpl implements UserDao{ // 注:这个方法不能被final、static、private任何一个修饰,否则CGLIB不会拦截这个方法 public void deleteUser() { System.out.println("delete User"); } public void saveUser() { System.out.println("save User"); } } public class TransactionInterceptor implements MethodInterceptor{ private UserDao target;//代理的目标对象 private Transaction transaction;//添加事务 public TransactionInterceptor(UserDao target, Transaction transaction){ this.target = target; this.transaction = transaction; } // 创建代理对象 public UserDaoImpl createProxyInstance() { Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象 enhancer.setSuperclass(this.target.getClass()); // 设置父类 enhancer.setCallback(this); // 设置回调对象(注:实现了MethodInterceptor接口的对象可以作为回调对象) return (UserDaoImpl) enhancer.create(); // 创建代理对象并返回 } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { this.transaction.beginTransaction(); Object result = methodProxy.invoke(this.target, args); this.transaction.commit(); return result; } } // 测试类 public class CglibProxyTest { @Test public void test(){ // 创建目标对象 UserDao target = new UserDaoImpl(); // 创建方法拦截器 TransactionInterceptor transactionInterceptor = new TransactionInterceptor(target, new Transaction()); // cglib动态代理对象 UserDaoImpl userDaoImpl = (UserDaoImpl)transactionInterceptor.createProxyInstance(); // 执行方法(使用的是代理对象) UserDaoImpl.deleteUser(); } } Console: 开启事务 delete user 事务提交3,jdk动态代理和cglib动态代理的比较: jdk动态代理的优点: 因为有接口,所有使系统更加低耦合,缺点是必须要有接口存在 cglib动态代理的特点:不需要接口的存在,故耦合性方面没有jdk动态代理好
Spring的动态代理:
spring在运行期创建代理,不需要特殊的编译器。spring有两种代理方式: 1,若目标对象实现了若干接口,spring就会使用jdk动态代理(默认) 2,若目标对象没有实现任何接口,spring就使用cglib库生成目标对象的子类。