miracle just wanna be better

代理设计模式和spring aop

2019-08-25
miracle

代理设计模式:

有一个需求:原有的业务不做任何的修改,添加额外的功能 实现需求的方式:

  • 方式一:
    • 在原有的代码的前面添加新的功能
    • 在原有的代码的后面添加新的功能
  • 方式二:用代理设计模式来实现
    • 原有的业务代码能够正常执行,也没有修改源代码
    • 通过代理设计模式,能够实现在原有功能的前面或后面添加新的功能

实现的代理的步骤:

  1. 必须有能够正常运行的业务代码
  2. 必须有要额外添加的新的业务功能代码
  3. 必须有一个类把原有的业务跟新业务耦合(组合),即就是把新的业务功能横切或织入到需要的业务类的前或后 代理的分类:
  4. 静态代理
  5. 动态代理
    • a.jdk动态代理 jdk自带的
    • b.cglib动态代理 第三jar包实现的 spring aop底层就是用的动态代理(jdk,cglib)

静态代理

静态代理部分:

需要准备原有业务
UserDao.java UserDaoImpl.java
UserService.java UserServiceImpl.java

新功能业务

TransactionManager.java

	/**
	 * 此类是一个新的功能类
	 * 目的要把这个新的功能类添加到原有的业务上
	 * @author Administrator
	 *
	 */
	public class TransactionManager {
		public void begin(){
			System.out.println("事务开启");
		}
		public void commit(){
			System.out.println("事务提交");
		}
		public void rollback(){
			System.out.println("事务回滚");
		}
	}
	

静态代理类

StaticProxy.java

   /**
	 * 静态的代理类
	 * 通过此类把原有的业务和新功能的业务,必须耦合在一起
	 * 静态代理类必须实现业务的接口,因为静态代理类功能要跟业务一致
	 * 即静态代理类的功能,不能少于原有业务
	 * @author Administrator
	 *
	 */
	public class StaticProxy implements UserService {
		//组合耦合两个对象,业务对象,新功能对象
		private UserService userServiceImpl;
		private TransactionManager transactionManager;
		
		public void setUserServiceImpl(UserService userServiceImpl) {
			this.userServiceImpl = userServiceImpl;
		}

		public void setTransactionManager(TransactionManager transactionManager) {
			this.transactionManager = transactionManager;
		}

		@Override
		public boolean addUser(User user) {
			boolean flag=false;
			try{
				transactionManager.begin();
				userServiceImpl.addUser(user);
				transactionManager.commit();
			}catch(Exception e){
				transactionManager.rollback();
				e.printStackTrace();
			}
			return flag;
		}

		@Override
		public boolean updateUser(User user) {
			boolean flag=false;
			try{
				transactionManager.begin();
				userServiceImpl.updateUser(user);
				transactionManager.commit();
			}catch(Exception e){
				transactionManager.rollback();
				e.printStackTrace();
			}
			return flag;
		}		
	}

测试类

public class TestClass_StaticProxy {
	@Test
	public void testMethod1(){
		//原有的业务模型
		UserService userService=new UserServiceImpl();
		userService.addUser(new User());
	}
	
	//老的业务和新的业务耦合后的结果
	@Test
	public void testMethod2(){
		//老的业务对象
		UserService userService=new UserServiceImpl();
		//新的业务对象
		TransactionManager transactionmanage=new TransactionManager();
		
		//创建老业务和新业务耦合类的对象
		StaticProxy staticProxy=new StaticProxy();
		staticProxy.setTransactionManager(transactionmanage);
		staticProxy.setUserService(userService);
		staticProxy.addUser(new User());
	}

}

静态代理总结:

  1. 没有修改原有的业务代码,没有破坏开闭原则
  2. 有新功能业务,且新功能业务类和原有业务类都独立的,遵守单一职责
  3. 新建了一个静态代理类,把原有的业务和新功能的业务耦合在一起
    • a.要求静态代理类要实现业务接口,保证业务完整性
    • b.有多少个业务,就有多少个静态代理类
      比如:
      UserService 对应 StaticProxy implements UserService
      ProductService 对应 StaticProxy1 implements ProductService
      典型从一个代码泥潭有调到另一个代码的泥潭(增加了静态代理类的代码量)
      代码的泥潭:
      UserServiceImpl.java –> StaticProxy.java
    • c.静态代理类不满足单一职责,在静态代理类中明显耦合了 原有业务和新功能业务
    • d.StaticProxy是静态代理类,此类的对象是代理对象 有多少个代理类,就会产生多少个代理对象,甚至更多个代理对象
    • e.静态代理类在编译期间就已经确认原有业务和新功能业务的耦合模型

动态代理部分:

jdk动态代理

是由jdk类库提供的一种代理模式,jdk动态代理要求业务必须有接口
在运行期间才能确认如何耦合
需要的新业务同上静态代理

/**
	 * 此类是专门用来生成代理对象的类
	 * 就是一个工厂,用来生成代理对象
	 * 要根据原有业务类和业务对象生成代理对象  
	 * 原有业务类UserServiceImpl  目标类
	 * 原有业务对象就是UserServiceImpl对象   目标对象
	 * 解释名词:原有业务类就叫做目标类
	 *        原有业务对象就叫做目标对象
	 * @author Administrator
	 *
	 */
	public class JDKProxy {
		/**
		 * 此方法专门用来生成代理对象的类
		 * 就是一个工厂方法,专门用来生成目标对象所对应的代理对象
		 * 此方法是一个通用的方法,方法的参数是Object,说明接收任意对象,生成代理对象
		 * targetObject 可以是UserServiceImpl对象
		 *             也可以是ProductorServiceImpl对象
		 *             等...
		 * @param targetObject 目标对象
		 * @return 代理对象  就是针对targetObject的代理对象
		 */
		public static Object getProxyObject(Object targetObject){
			Object proxyObject=null;
			/**
			 * Proxy类是jdk类库中自带的类
			 * 其中newProxyInstance方法是一个静态的方法
			 * 此方法有三个参数
			 * 参数一:类加载器 任意类都可以获取类加载器,只能获取,本质就是为了定位类路径
			 * 参数二:Class[] 数组,获取目标对象所对应的类的所有接口
			 *       要求jdk动态代理,目标类必须有接口,否则无法用jdk生成代理对象
			 *       代理对象和目标对象是兄弟关系,因为两个对象有共同的接口
			 * 参数三:必须是InvocationHandler接口对象
			 * 此方法的本质就是用jdk动态创建出代理类(在运行的时候才动态创建代理类)的
			 * 字节码,这个字节码就是代理类的字节码,通过代理类的字节码生成代理对象
			 * 此代理类的字节码一定实现业务接口,并重写了接口方法,在接口方法中调用invoke方法
			 */
			proxyObject=Proxy.newProxyInstance(
					targetObject.getClass().getClassLoader(),
					targetObject.getClass().getInterfaces(), 
					new TransactionHandler(targetObject));
			
			return proxyObject;
		}
	}

实现新功能要实现InvocationHandler接口

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 returnValue=null;
		TransactionManager tm=new TransactionManager();
		try{
			tm.begin();			
			returnValue=method.invoke(targetObject, args);//执行原有业务
			tm.commit();
		}catch(Exception e){
			tm.rollback();
			e.printStackTrace();
		}
		return returnValue;
	}
}

生成的代理类:(通过反编译得到的代码)

	package com.tarena.core;

	import java.lang.reflect.InvocationHandler;
	import java.lang.reflect.Method;
	import java.lang.reflect.Proxy;
	import java.lang.reflect.UndeclaredThrowableException;

	final class $Proxy0
	  extends Proxy
	  implements IUser
	{
	  private static Method m1;
	  private static Method m3;
	  private static Method m2;
	  private static Method m0;
	  
	  public $Proxy0(InvocationHandler paramInvocationHandler)
	  {
		super(paramInvocationHandler);
	  }
	  
	  public final boolean equals(Object paramObject)
	  {
		try
		{
		  return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
		}
		catch (Error|RuntimeException localError)
		{
		  throw localError;
		}
		catch (Throwable localThrowable)
		{
		  throw new UndeclaredThrowableException(localThrowable);
		}
	  }
	  
	  public final void sayHello(String paramString)
	  {
		try
		{
		  this.h.invoke(this, m3, new Object[] { paramString });
		  return;
		}
		catch (Error|RuntimeException localError)
		{
		  throw localError;
		}
		catch (Throwable localThrowable)
		{
		  throw new UndeclaredThrowableException(localThrowable);
		}
	  }
	  
	  public final String toString()
	  {
		try
		{
		  return (String)this.h.invoke(this, m2, null);
		}
		catch (Error|RuntimeException localError)
		{
		  throw localError;
		}
		catch (Throwable localThrowable)
		{
		  throw new UndeclaredThrowableException(localThrowable);
		}
	  }
	  
	  public final int hashCode()
	  {
		try
		{
		  return ((Integer)this.h.invoke(this, m0, null)).intValue();
		}
		catch (Error|RuntimeException localError)
		{
		  throw localError;
		}
		catch (Throwable localThrowable)
		{
		  throw new UndeclaredThrowableException(localThrowable);
		}
	  }
	  
	  static
	  {
		try
		{
		  m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
		  m3 = Class.forName("com.tarena.core.IUser").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
		  m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
		  m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
		  return;
		}
		catch (NoSuchMethodException localNoSuchMethodException)
		{
		  throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
		}
		catch (ClassNotFoundException localClassNotFoundException)
		{
		  throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
		}
	  }
	}

cglib动态代理

cglib是第三方的工具jar包提供代理模式
cglib要求业务类有无接口皆可,但业务类不能是final类

/**
 * 此类是专门用来生成代理对象的类
 * 其实就是一个工厂,用来生成代理的对象
 * 要根据原来有的业务类和业务对象生成代理对象
 * 原有的业务类UserServiceImpl 目标类
 * 原有的业务对象就是UserServiceImpl的对象 目标对象
 * 即:
 * 	原有的业务类就叫做目标类
 * 	原有的业务对象就叫做目标对象
 * @author PC
 *
 */
public class CGLIBProxy {
	/**
	 * 此方法是用来生成代理对象的
	 * @param targetObject 目标对象
	 * @return 代理对象
	 * 通过目标对象生产出代理对象,利用jdk工具生成
	 */
	public static    Object getProxyObject(Object targetObject){
		Object proxyObject=null;
		/**
		 * 创建出代理对象,生成代理类是一个字节数组存储在内存中
		 * 通过这个字节数组的代理类来创建出代理对象
		 * 且此目标类一定不能是final
		 * cglib生成的代理类是目标类的子类,变相的约束代理类的功能的完整性
		 * 要求必须实现MethodInterceptor接口中的intercept方法
		 * 在intercept方法中耦合老的业务和新的业务
		 */
		//Enhancer:加强/增强器
		Enhancer enhancer=new Enhancer();
		//此语句可以不用写,即用cglib生成代理类,目标类有无接口皆可,但目标一定不能是final
		enhancer.setInterfaces(targetObject.getClass().getInterfaces());
		//设置代理类的父类(此语句必须要写)
		enhancer.setSuperclass(targetObject.getClass());
		//设置回调,用于回调耦合方法(intercept方法,是MethodIntercept接口中的)
		enhancer.setCallback(new TransactionHandler(targetObject));
		//创建出cglib的代理对象,其实用asm创建的代理类
		proxyObject=enhancer.create();
		return proxyObject;
	}

新的功能类需要实现MethodInterceptor接口

public class TransactionHandler implements MethodInterceptor{
	//targetObject是老的业务对象
	private Object targetObject;
	public TransactionHandler(Object targetObject){
		this.targetObject=targetObject;
	}
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		Object returnValue=null;
		TransactionManager transactionManager=new TransactionManager();
		try{
			//创建新业务对象
			
			transactionManager.begin();
			//用老的业务对象调用老的业务方法(二选一)
			//Method是java.reflect包的类
			//MethodProxy是bglib工具包对Method的一个封装
			returnValue=method.invoke(targetObject, args);
			//returnValue=methodProxy.invoke(targetObject, args);
			transactionManager.commit();
		}catch(Exception e){
			transactionManager.rollback();
			e.printStackTrace();
		}finally{
			
		}
		return returnValue;
	}
	

}

动态代理总结:

  1. 原有业务功能没有被修改,新业务功能也添加了,遵守了开闭原则
  2. 原有的业务类,新功能的业务类都是独立类,遵守单一职责
  3. 静态代理类是由程序员创建的,动态代理类是由工具创建的(jdk,cglib)
  4. 每一个业务类都对应至少一个静态代理类,编译期间确定代理类,开发效率低,执行效率高
    每一个业务类对应一个动态代理类,运行期间确定代理类,开发效率高,执行效率低
    动态代理有缓存,存储代理类字节码,效率也不会太低
  5. 动态代理和静态代理的代理对象还是一个都不能少
  6. 用动态代理和静态代理,在没有特殊处理的前提下,只能给业务中的所有方法横切新功能
  7. 静态代理是用静态代理类把原有业务和新业务耦合在一起
    动态代理是用动态代理类回调指定接口的方法,用指定接口的子实现把原有的业务和新业务耦合在一起
  8. 静态代理类和动态代理类都要实现对应的业务接口
  9. 通过jdk或cglib生成代理类,通过代理类实例化代理对象

jdk和cglib的区别:

jdk动态代理:

  1. 业务类必须有接口
  2. 必须实现InvocationHandler接口,在接口的子实现中耦合原有业务和新业务
  3. 代理类和业务类是兄弟关系,因为隶属于同一个接口
  4. 用jdk创建代理,创建代理快,执行代理类慢 cglib动态代理:
  5. 业务类有无接口皆可
  6. 必须实现MethodInterceptor接口,在接口的子实现中耦合原有业务和新业务
  7. 代理类和业务类是父子,业务类是父,代理类是子
  8. 用asm和cglib创建代理类,创建慢,执行快
  9. 业务类不用final修饰

用jdk和cglib动态代理,在没有特别java代码处理的前提下,是给业务类的所有方法横切/织入新功能,spring aop自己创建了一套表达式语言,通过这些表达式,可以轻松控制业务类中的某些方法添加横切,或不添加横切,spring aop底层用的就是动态代理,有业务接口用jdk动态代理,没有业务接口用cglib动态代理


上一篇 spring框架

下一篇 spring mvc

Comments

Content