從Spring及Mybatis框架源碼中學習設計模式(結構型)

設計模式是解決問題的方案,從大神的代碼中學習對設計模式的使用,可以有效提升個人編碼及設計代碼的能力。本系列博文用於總結閱讀過的框架源碼(Spring系列、Mybatis)及JDK源碼中 所使用過的設計模式,並結合個人工作經驗,重新理解設計模式。

本篇博文主要看一下結構型的幾個設計模式,即,適配器模式、代理模式 及 裝飾器模式。

適配器模式

個人理解

從名字就很好理解,主要起到一個連接適配的作用。生活中也有很多這樣的例子,比如我們給筆記本充電,不能直接使用國家標準電源,都需要一個“電源適配器”來適配電源輸入的電流。使用適配器模式最大的好處就是複用現有組件。應用程序需要複用現有的類,但接口不能被該應用程序兼容,則無法直接使用。這種場景下就適合使用適配器模式實現接口的適配,從而完成組件的複用。

很明顯,適配器模式通過提供 Adapter 的方式完成接口適配,實現了程序複用 Adaptee(被適配者) 的需求,避免了修改 Adaptee 實現接口,當有新的 Adaptee 需要被複用時,只要添加新的 Adapter 即可,這是符合“開放封閉”原則的。

本模式的應用也比較廣泛,因爲實際的開發中也有很多適配工作要做,所以 這些都可以考慮使用適配器模式。在spring及mybatis中也使用了本模式,分析如下。

Spring中的應用

Spring 在 AOP 模塊中,設計了一套 AdvisorAdapter 組件,將各種 Advice 對象適配成了相對應的 MethodInterceptor 對象。其中,AfterReturningAdviceAdapter、MethodBeforeAdviceAdapter 及 ThrowsAdviceAdapter 實現類扮演了“適配器”的角色,AfterReturningAdvice、MethodBeforeAdvice 及 ThrowsAdvice 扮演了“被適配者”角色,而AfterReturningAdviceInterceptor、MethodBeforeAdviceInterceptor 及 ThrowsAdviceInterceptor 則扮演了“適配目標”的角色。其源碼實現如下。

/**
 * Advice 適配器的頂級接口
 * @author Rod Johnson
 */
public interface AdvisorAdapter {

	/**
	 * 此適配器是否能適配 給定的 advice 對象
	 */
	boolean supportsAdvice(Advice advice);

	/**
	 * 獲取傳入的 advisor 中的 Advice 對象,將其適配成 MethodInterceptor 對象
	 */
	MethodInterceptor getInterceptor(Advisor advisor);
}


/**
 * 將 AfterReturningAdvice 適配成 AfterReturningAdviceInterceptor 的適配器
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof AfterReturningAdvice);
	}

	public MethodInterceptor getInterceptor(Advisor advisor) {
		AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
		return new AfterReturningAdviceInterceptor(advice);
	}
}


/**
 * 將 MethodBeforeAdvice 適配成 MethodBeforeAdviceInterceptor 的適配器
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
@SuppressWarnings("serial")
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}
}


/**
 * 將 ThrowsAdvice 適配成 ThrowsAdviceInterceptor 的適配器
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
@SuppressWarnings("serial")
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {

	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof ThrowsAdvice);
	}

	public MethodInterceptor getInterceptor(Advisor advisor) {
		return new ThrowsAdviceInterceptor(advisor.getAdvice());
	}
}


/**
 * 下面這三個接口的實現類 均爲 “被適配者”
 */
public interface AfterReturningAdvice extends AfterAdvice {

	/**
	 * 目標方法method執行後,AOP會回調此方法,注意,它還傳入了method的返回值
	 */
	void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

public interface MethodBeforeAdvice extends BeforeAdvice {

	/**
	 * 目標方法method要開始執行時,AOP會回調此方法
	 */
	void before(Method method, Object[] args, Object target) throws Throwable;

}

public interface ThrowsAdvice extends AfterAdvice {

}


/**
 * 下面這三個類均爲“適配目標”
 */
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;

	/**
	 * 爲給定的 advice 創建一個 AfterReturningAdviceInterceptor 對象
	 */
	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
}

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

	private MethodBeforeAdvice advice;

	/**
	 * 爲指定的advice創建對應的MethodBeforeAdviceInterceptor對象
	 */
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	/**
	 * 這個invoke方法是攔截器的回調方法,會在代理對象的方法被調用時觸發回調
	 */
	public Object invoke(MethodInvocation mi) throws Throwable {
		// 首先觸發了advice的before()方法的回調
		// 然後纔是MethodInvocation的process()方法回調
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
		return mi.proceed();
	}
}

public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {

	private static final String AFTER_THROWING = "afterThrowing";

	private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);

	private final Object throwsAdvice;

	private final Map<Class, Method> exceptionHandlerMap = new HashMap<Class, Method>();

	public ThrowsAdviceInterceptor(Object throwsAdvice) {
		Assert.notNull(throwsAdvice, "Advice must not be null");
		this.throwsAdvice = throwsAdvice;

		// 配置 throwsAdvice 的回調
		Method[] methods = throwsAdvice.getClass().getMethods();
		for (Method method : methods) {
			if (method.getName().equals(AFTER_THROWING) &&
					(method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
					Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
				) {
				// 配置異常處理
				this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
				if (logger.isDebugEnabled()) {
					logger.debug("Found exception handler method: " + method);
				}
			}
		}

		if (this.exceptionHandlerMap.isEmpty()) {
			throw new IllegalArgumentException(
					"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
		}
	}

	public Object invoke(MethodInvocation mi) throws Throwable {
		// 把對目標對象的方法調用放入 try/catch 中,並在 catch 中觸發
		// throwsAdvice 的回調,把異常接着向外拋,不做過多處理
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			Method handlerMethod = getExceptionHandler(ex);
			if (handlerMethod != null) {
				invokeHandlerMethod(mi, ex, handlerMethod);
			}
			throw ex;
		}
	}
}


/**
 * 本類的 getInterceptors() 方法使用上述 適配器組件,完成了
 * 從 Advice 到 MethodInterceptor 的適配工作
 */
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

	/**
	 * 持有AdvisorAdapter的list,這個list中的AdvisorAdapter與
	 * 實現 spring AOP 的 Advice 增強功能相對應
	 */
	private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);

	/**
	 * 將已實現的 AdviceAdapter 加入 list
	 */
	public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}

	public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
		
		// 從Advisor通知器中獲取配置的Advice
		Advice advice = advisor.getAdvice();
		
		// 如果advice是MethodInterceptor類型的,直接加進interceptors,不用適配
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
		
		// 如果advice不是MethodInterceptor類型的,就將其適配成MethodInterceptor,
		// 當前的DefaultAdvisorAdapterRegistry對象 在初始化時就已經爲 adapters 添加了
		// 三種 AdvisorAdapter 的實例
		for (AdvisorAdapter adapter : this.adapters) {
			// 依次使用 adapters集合中的 adapter 對 advice 進行適配
			// 將其適配成 MethodInterceptor 對象
			if (adapter.supportsAdvice(advice)) {
				interceptors.add(adapter.getInterceptor(advisor));
			}
		}
		if (interceptors.isEmpty()) {
			throw new UnknownAdviceTypeException(advisor.getAdvice());
		}
		return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
	}

	public void registerAdvisorAdapter(AdvisorAdapter adapter) {
		this.adapters.add(adapter);
	}
	
	/**
	 * 如果adviceObject是Advisor的實例,則將adviceObject轉換成Advisor類型並返回
	 */
	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		if (adviceObject instanceof Advisor) {
			return (Advisor) adviceObject;
		}
		if (!(adviceObject instanceof Advice)) {
			throw new UnknownAdviceTypeException(adviceObject);
		}
		Advice advice = (Advice) adviceObject;
		if (advice instanceof MethodInterceptor) {
			return new DefaultPointcutAdvisor(advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}
}

像這樣整理出來以後,其類結構及層次設計還是比較清晰明瞭的,比起很多書上範例的淺嘗輒止,結合這些實際場景及源碼去理解這些設計模式,要讓人更加印象深刻。

Mybatis中的應用

MyBatis 的日誌模塊中使用了適配器模式,MyBatis 內部調用其日誌模塊時,使用了其內部接口(org.apache.ibatis.logging.Log)。但是 Log4j、Slf4j 等第三方日誌框架對外提供的接口各不相同,MyBatis 爲了集成和複用這些第三方日誌框架,在其日誌模塊中提供了多種 Adapter 實現 如:Log4jImpl、Slf4jImpl 等等,它們將這些 “第三方日誌框架對外的接口方法” 適配成 “Log 接口方法”,這樣 MyBatis 內部就可以統一通過該 Log 接口調用第三方日誌框架的功能了。

其中,Log 接口定義了日誌模塊的功能,日誌適配器 Log4jImpl、Slf4jImpl 等通過實現此接口,將對應框架中的日誌類 (Logger) 裏的方法 適配成Log接口中定義的方法。

/**
 * mybatis的日誌接口,統一了不同日誌框架的 日誌操作,
 * 由各實現類 對各日誌框架進行具體的適配
 */
public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);
}


/**
 * Log4j 日誌框架適配器
 */
public class Log4jImpl implements Log {

  /**
   * 注意!!!!!
   * 下面的 log 對象是 Log4j框架的 org.apache.log4j.Logger
   * 本適配器完成了 “org.apache.log4j.Logger中的方法” 到
   * “org.apache.ibatis.logging.Log中的方法” 的適配
   * 從下面的代碼中可以很輕易地看出來
   */
  private final Logger log;

  private static final String FQCN = Log4jImpl.class.getName();

  public Log4jImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }

  /**
   * !!!!!!!!!!!!!!!
   * 具體適配過程如下:
   * !!!!!!!!!!!!!!!
   */
  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  @Override
  public void error(String s, Throwable e) {
    log.log(FQCN, Level.ERROR, s, e);
  }

  @Override
  public void error(String s) {
    log.log(FQCN, Level.ERROR, s, null);
  }

  @Override
  public void debug(String s) {
    log.log(FQCN, Level.DEBUG, s, null);
  }

  @Override
  public void trace(String s) {
    log.log(FQCN, Level.TRACE, s, null);
  }

  @Override
  public void warn(String s) {
    log.log(FQCN, Level.WARN, s, null);
  }
}


/**
 * JDK 日誌組件適配器
 */
public class Jdk14LoggingImpl implements Log {

  /**
   * 使用了JDK中的日誌類 java.util.logging.Logger
   */
  private final Logger log;

  public Jdk14LoggingImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isLoggable(Level.FINE);
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isLoggable(Level.FINER);
  }

  @Override
  public void error(String s, Throwable e) {
    log.log(Level.SEVERE, s, e);
  }

  @Override
  public void error(String s) {
    log.log(Level.SEVERE, s);
  }

  @Override
  public void debug(String s) {
    log.log(Level.FINE, s);
  }

  @Override
  public void trace(String s) {
    log.log(Level.FINER, s);
  }

  @Override
  public void warn(String s) {
    log.log(Level.WARNING, s);
  }
}

代理模式

個人理解

代理模式的實際應用 主要體現在框架開發中,日常業務上的開發工作中很少有場景需要使用該模式。而代理模式中 動態代理尤爲重要,不管是自己公司的內部框架 還是 一些知名的開源框架,很多重要的實現都用到了該模式。比如,有些 CS架構中,Client端的遠程方法調用 就使用了動態代理,在invoke()方法中 爲被代理對象調用的方法 織入遠程調用處理,然後將遠程處理的結果返回給調用者;Spring的AOP也是優先使用JDK動態代理來完成;Mybatis爲JDBC操作織入日誌處理,等等。下面我們結合源碼來深入理解一下這個模式。

動態代理原理

靜態代理沒什麼好講的,很少見用到,功能也比較薄弱,本篇重點講解動態代理。首先了解一下JDK動態代理的原理,這對理解 Spring AOP 部分的源碼及實現原理也很有幫助。

JDK 動態代理的實現原理是,動態創建代理類井通過指定類加載器加載,然後在創建代理對象時將 InvokerHandler 對象作爲構造參數傳入。當調用代理對象的方法時,會調用 InvokerHandler 的 invoke() 方法,並最終調用真正業務對象的相應方法。 JDK 動態代理不僅在 Spring 及 MyBatis 的多個模塊中都有所涉及, 在其它很多開源框架中也能看到其身影。

/**
 * 一般會使用實現了 InvocationHandler 的類作爲代理對象的生產工廠,
 * 並且通過持有被代理對象target,來在invoke()方法中對被代理對象的目標方法進行調用和增強,
 * 這些我們都能通過下面這段代碼看懂,但代理對象是如何生成的?invoke()方法又是如何被調用的呢?
 */
public class ProxyFactory implements InvocationHandler{
	
	private Object target = null;
	
	public Object getInstanse(Object target){
		
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		Object ret = null;
		System.out.println("前置增強");
		ret = method.invoke(target, args);
		System.out.println("後置增強");
		return ret;
	}
}


/**
 * 實現了接口MyInterface和接口的play()方法,可以作爲被代理類
 */
public class TargetObject implements MyInterface {

	@Override
	public void play() {
		System.out.println("妲己,陪你玩 ~");
		
	}
}


/**
 * 測試類
 */
public class ProxyTest {

	public static void main(String[] args) {
		TargetObject target = new TargetObject();
		// ProxyFactory 實現了 InvocationHandler接口,其中的 getInstanse() 方法利用 Proxy 類
		// 生成了target目標對象的代理對象,並返回;且ProxyFactory持有對target的引用,可以在
		// invoke() 中完成對 target 相應方法的調用,以及目標方法前置後置的增強處理
		ProxyFactory proxyFactory = new ProxyFactory();
		// 這個mi就是JDK的 Proxy 類動態生成的代理類 $Proxy0 的實例,該實例中的方法都持有對
		// invoke() 方法的回調,所以當調用其方法時,就能夠執行 invoke() 中的增強處理
		MyInterface mi = (MyInterface)proxyFactory.getInstanse(target);
		// 這樣可以看到 mi 的 Class 到底是什麼
		System.out.println(mi.getClass());
		// 這裏實際上調用的就是 $Proxy0代理類中對 play() 方法的實現,結合下面的代碼可以看到
		// play() 方法通過 super.h.invoke() 完成了對 InvocationHandler對象(proxyFactory)中
		// invoke()方法的回調,所以我們才能夠通過 invoke() 方法實現對 target 對象方法的
		// 前置後置增強處理
		mi.play();
		// 總的來說,就是在invoke()方法中完成target目標方法的調用,及前置後置增強,
		// JDK動態生成的代理類中對 invoke() 方法進行了回調
	}
	
	/**
	 * 將ProxyGenerator生成的動態代理類的輸出到文件中,利用反編譯工具luyten等就可
	 * 以看到生成的代理類的源碼咯,下面給出了其反編譯好的代碼實現
	 */
	@Test
	public void generatorSrc(){
		byte[] bytesFile = ProxyGenerator.generateProxyClass("$Proxy0", TargetObject.class.getInterfaces());
		FileOutputStream fos = null;
		try{
			String path = System.getProperty("user.dir") + "\\$Proxy0.class";
			File file = new File(path);
			fos = new FileOutputStream(file);
			fos.write(bytesFile);
			fos.flush();
		} catch (Exception e){
			e.printStackTrace();
		} finally{
			try {
				fos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


/**
 * Proxy生成的代理類,可以看到,其繼承了Proxy,並且實現了被代理類的接口
 */
public final class $Proxy0 extends Proxy implements MyInterface
{
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;
    
    static {
        try {
            $Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            $Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
            //實例化MyInterface的play方法
            $Proxy0.m3 = Class.forName("com.shuitu.test.MyInterface").getMethod("play", (Class<?>[])new Class[0]);
            $Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw new NoClassDefFoundError(ex2.getMessage());
        }
    }
    
    public $Proxy0(final InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
    
    public final void play() {
        try {
        	// 這個 h 其實就是我們調用 Proxy.newProxyInstance() 方法時傳進去的ProxyFactory(InvocationHandler對象),
        	// 該對象的 invoke() 方法中實現了對目標對象的目標方法的增強。看到這裏,利用動態代理實現方法增強的
        	// 實現原理就全部理清咯
            super.h.invoke(this, $Proxy0.m3, null);
        }
        catch (Error | RuntimeException error) {
            throw new RuntimeException();
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    public final boolean equals(final Object o) {
        try {
            return (boolean)super.h.invoke(this, $Proxy0.m1, new Object[] { o });
        }
        catch (Error | RuntimeException error) {
            throw new RuntimeException();
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    public final int hashCode() {
        try {
            return (int)super.h.invoke(this, $Proxy0.m0, null);
        }
        catch (Error | RuntimeException error) {
            throw new RuntimeException();
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    public final String toString() {
        try {
            return (String)super.h.invoke(this, $Proxy0.m2, null);
        }
        catch (Error | RuntimeException error) {
            throw new RuntimeException();
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
}

Spring 中的應用

Spring 在生成動態代理類時,會優先選擇使用JDK動態代理,除非被代理類沒有實現接口。

/**
 * 可以看到,其實現了 InvocationHandler 接口,所以肯定也定義了一個 使用 java.lang.reflect.Proxy
 * 動態生成代理對象的方法,並在實現的 invoke() 方法中爲代理對象織入增強方法
 */
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

	/**
	 * 獲取 JVM 動態生成的代理對象
	 */
	public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		
		// 獲取代理類要實現的接口
		Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		
		// 通過 Proxy 生成代理對象
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
	
	/**
	 * 本類所生成的代理對象中,所有方法的調用 都會回調本方法。
	 * 根據用戶的配置,對指定的切面進行相應的增強
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;

		// 通過 targetSource 可以獲取被代理對象
		TargetSource targetSource = this.advised.targetSource;
		Class targetClass = null;
		Object target = null;

		try {
			// 如果目標對象調用的是 Obejct 類中的基本方法,如:equals、hashCode 則進行相應的處理
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// 如果目標對象沒有重寫 Object 類的基本方法:equals(Object other)
				return equals(args[0]);
			}
			if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// 如果目標對象沒有重寫 Object類的基本方法:hashCode()
				return hashCode();
			}
			if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// 使用代理配置對 ProxyConfig 進行服務調用
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			if (this.advised.exposeProxy) {
				// 如果有必要,可以援引
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// 獲取目標對象,爲目標方法的調用做準備
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}

			// 獲取定義好的攔截器鏈
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// 如果沒有配置攔截器,就直接調用目標對象target的method方法,並獲取返回值
			if (chain.isEmpty()) {
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
			}
			else {
				// 如果有攔截器鏈,則需要先調用攔截器鏈中的攔截器,再調用目標的對應方法
				// 這裏通過構造 ReflectiveMethodInvocation 來實現
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// 沿着攔截器鏈繼續向下處理
				retVal = invocation.proceed();
			}

			// 獲取 method 返回值的類型
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// 特殊提醒:它返回“this”,方法的返回類型與類型兼容。
				// 注意,如果 target 在另一個返回的對象中設置了對自身的引用,spring 將無法處理
				retVal = proxy;
			} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// 必須來自 TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// 存儲舊的 proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
}

Mybatis中的應用

Mybatis 的 PooledConnection 類中封裝了數據庫連接的代理對象,對數據庫連接的操作大都會通過該代理對象完成。

/**
 * Mybatis 封裝的數據庫連接類,它實現了 InvocationHandler 接口,封裝了真正的
 * 數據庫連接對象 (java.sql.Connection) 及其代理對象,該代理對象是通過
 * JDK 動態代理類 Proxy 產生的
 * @author Clinton Begin
 */
class PooledConnection implements InvocationHandler {

  private static final String CLOSE = "close";
  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };

  private final int hashCode;

  /**
   * 記錄當前 PooledConnection對象 是從哪個 PooledDataSource(數據庫連接池)對象獲取的。
   * 當調用 close() 方法時會將 PooledConnection 放回該 dataSource 連接池
   */
  private final PooledDataSource dataSource;
  /** 真正的 數據庫連接對象 */
  private final Connection realConnection;
  /** 數據庫連接的 代理對象 */
  private final Connection proxyConnection;
  /** 從連接池中取出該連接的時間戳 */
  private long checkoutTimestamp;
  /** 該連接創建的時間戳 */
  private long createdTimestamp;
  /** 最後一次被使用的時間戳 */
  private long lastUsedTimestamp;
  /** 由數據庫 URL、用戶名 和 密碼 計算出來的 hash值,可用於標識該連接所在的連接池 */
  private int connectionTypeCode;
  /**
   * 檢測當前 PooledConnection 是否有效,主要是爲了防止程序通過 close() 方法
   * 將連接歸還給連接池之後,依然通過該連接操作數據庫
   */
  private boolean valid;

  /**
   * 注意該構造方法中對 proxyConnection 的初始化
   */
  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    // 這裏使用了 JDK 的 Proxy 爲數據庫連接創建了一個代理對象,對該代理對象的所有操作
    // 都會回調 本類中的 invoke() 方法
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }

  /**
   * 實現了 InvocationHandler 接口中的方法
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    // 如果調用的是 close() 方法,則將其放入連接池,而不是真正關閉數據庫連接
    if (CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    }
    try {
      if (!Object.class.equals(method.getDeclaringClass())) {
        // 通過 valid 字段檢測連接是否有效
        checkConnection();
      }
      // 調用真正數據庫連接對象的對應方法
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
}

裝飾器模式

個人理解

在實際生產中,新需求在軟件的整個生命過程中總是不斷出現的。當有新需求出現時,就需要爲某些組件添加新的功能來滿足這些需求。 添加新功能的方式有很多,我們可以直接修改已有組件的代碼井添加相應的新功能,但這樣會破壞己有組件的穩定性,修改完成後,整個組件需要重新進行測試才能上線使用。 這種方式顯然違反了 “開放封閉” 原則。

另一種方式是使用繼承,我們可以創建子類並在子類中添加新功能實現擴展。 這種方法是靜態的,用戶不能控制增加行爲的方式和時機。 而且有些情況下繼承是不可行的,例如 己有組件是被 final 修飾的類。 另外,如果待添加的新功能存在多種組合,使用繼承方式可能會導致大量子類的出現。 例如,有 4 個待添加的新功能,系統需要動態使用任意多個功能的組合, 則需要添加 15 個子類才能滿足全部需求。

裝飾器模式能夠幫助我們解決上述問題,裝飾器可以動態地爲對象添加功能,它是基於組合的方式實現該功能的。在實踐中,我們應該儘量使用組合的方式來擴展系統的功能,而非使用繼承的方式。通過裝飾器模式的介紹,可以幫助讀者更好地理解設計模式中常見的一句話:組合優於繼承。下面先來看一下裝飾器模式的類圖,及其核心角色。
在這裏插入圖片描述

  • Component (組件):組件接口定義了全部 “組件實現類” 以及所有 “裝飾器實現” 的行爲。
  • ConcreteComponent (具體組件實現類):通常情況下,具體組件實現類就是被裝飾器裝飾的原始對象,該類提供了 Component 接口中定義的最基本的功能,其他高級功能或後續添加的新功能,都是通過裝飾器的方式添加到該類的對象之上的。
  • Decorator (裝飾器):所有裝飾器的父類,它是一個實現了 Component 接口的抽象類,並持有一個 Component 被裝飾對象,這就實現了裝飾器的嵌套組合和複用。
  • ConcreteDecorator (具體的裝飾器實現類):該實現類要向被裝飾對象添加某些功能,被裝飾的對象只要是 Component 類型即可。

Mybatis中的應用

在 MyBatis 的緩存模塊中,使用了裝飾器模式的變體,其中將 Decorator 接口和 Component 接口合併爲一個 Component 接口,即,去掉了 Decorator 這個中間層,ConcreteDecorator 直接實現了Component 接口。

MyBatis 中緩存模塊相關的代碼位於 cache 包下, 其中 Cache 接口是緩存模塊的核心接口,它定義了所有緩存的基本行爲,扮演了 Component 的角色。實現類 PerpetualCache 扮演了 ConcreteComponent 的角色,其實現比較簡單,底層使用 HashMap 記錄緩存項,也是通過該 HashMap 對象的方法實現了 Cache 接口中定義的相應方法。而 cache 包下的 decorators 包中,則定義了一系列 ConcreteDecorator 的實現,如 BlockingCache、FifoCache 及 LruCache 等等,它們都持有一個 Cache 類型的對象,通過嵌套組合的方式爲該 Cache對象 裝飾相應的功能。其源碼實現如下。

public interface Cache {

  /** 該緩存對象的 id */
  String getId();

  /** 向緩存中添加數據,一般 key 是 CacheKey,value 是查詢結果 */
  void putObject(Object key, Object value);

  /** 根據指定的 key,在緩存中查找對應的結果對象 */
  Object getObject(Object key);

  /** 刪除 key 對應的緩存項 */
  Object removeObject(Object key);

  /** 清空緩存 */
  void clear();

  /** 緩存項的個數,該方法不會被 MyBatis 核心代碼使用,所以可提供空實現 */
  int getSize();

  /**
   * 獲取讀寫鎖,該方法不會被 MyBatis 核心代碼使用,所以可提供空實現。
   * 這裏在接口中爲此方法提供了默認實現,也是 JDK8 的新特性
   */
  default ReadWriteLock getReadWriteLock() {
    return null;
  }
}


public class PerpetualCache implements Cache {

  /** Cache 對象的唯一標識 */
  private final String id;
  /** 用於記錄緩存項的 Map 對象 */
  private final Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  /**
   * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   * 下面所有的方法都是通過 cache 這個 HashMap對象 的相應方法實現的
   * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   */
  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  /**
   * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   * 重寫了 equals() 和 hashCode() 方法,兩者都只關心 id 字段,並不關心 cache 字段
   * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   */
  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }
}


/**
 * 阻塞版本的緩存裝飾器,它會保證只有一個線程到數據庫中查找指定 key 對應的數據。
 * 假設線程 A 在 BlockingCache 中未查找到 keyA 對應的緩存項時,線程 A 會獲取 keyA 對應的鎖,
 * 這樣後續線程在查找 keyA 時會被阻塞
 */
public class BlockingCache implements Cache {

  /** 阻塞超時時長 */
  private long timeout;
  /** 被裝飾的底層 Cache 對象 */
  private final Cache delegate;
  /** 每個 key 都有對應的 ReentrantLock 對象 */
  private final ConcurrentHashMap<Object, ReentrantLock> locks;

  public BlockingCache(Cache delegate) {
    this.delegate = delegate;
    this.locks = new ConcurrentHashMap<>();
  }

  @Override
  public Object getObject(Object key) {
    // 獲取該 key 對應的鎖
    acquireLock(key);
    // 查詢 key
    Object value = delegate.getObject(key);
    // 緩存中有 key 對應的緩存項,則釋放鎖,否則繼續持有鎖
    if (value != null) {
      releaseLock(key);
    }
    return value;
  }

  private void acquireLock(Object key) {
    // 獲取 ReentrantLock 對象
    Lock lock = getLockForKey(key);
    // 獲取鎖,帶超時時長
    if (timeout > 0) {
      try {
        boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
        // 超時,則拋出異常
        if (!acquired) {
          throw new CacheException("Couldn't get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    } else {
      // 獲取鎖,不帶起時時長
      lock.lock();
    }
  }

  private ReentrantLock getLockForKey(Object key) {
    // 創建 ReentrantLock 對象,嘗試添加到 locks 集合中,若 locks 集合中已經有了
    // 相應的 ReentrantLock 對象,則使用 locks 集合中的 ReentrantLock 對象
    return locks.computeIfAbsent(key, k -> new ReentrantLock());
  }

  @Override
  public void putObject(Object key, Object value) {
    try {
      // 向緩存中添加緩存項
      delegate.putObject(key, value);
    } finally {
      // 釋放鎖
      releaseLock(key);
    }
  }

  private void releaseLock(Object key) {
    // 獲取鎖
    ReentrantLock lock = locks.get(key);
    // 鎖是否被當前線程持有
    if (lock.isHeldByCurrentThread()) {
      // 釋放鎖
      lock.unlock();
    }
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  @Override
  public Object removeObject(Object key) {
    // despite of its name, this method is called only to release locks
    releaseLock(key);
    return null;
  }

  @Override
  public void clear() {
    delegate.clear();
  }

  public long getTimeout() {
    return timeout;
  }

  public void setTimeout(long timeout) {
    this.timeout = timeout;
  }
}


/**
 * 在很多場景中,爲了控制緩存的大小,系統需要按照一定的規則清理緩存。
 * FifoCache 是先入先出版本的裝飾器,當向緩存添加數據時,如果緩存項的個數已經達到上限,
 * 則會將緩存中最老(即最早進入緩存)的緩存項刪除
 */
public class FifoCache implements Cache {

  /** 底層被裝飾的底層 Cache 對象 */
  private final Cache delegate;
  /** 用於記錄 key 進入緩存的先後順序,使用的是 LinkedList<Object> 類型的集合對象 */
  private final Deque<Object> keyList;
  /** 記錄了緩存項的上限,超過該值,則需要清理最老的緩存項 */
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<>();
    this.size = 1024;
  }

  @Override
  public void putObject(Object key, Object value) {
    // 檢測並清理緩存
    cycleKeyList(key);
    // 添加緩存項
    delegate.putObject(key, value);
  }

  private void cycleKeyList(Object key) {
    // 記錄 key
    keyList.addLast(key);
    // 如果達到緩存上限,則清理最老的緩存項
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.size = size;
  }

  @Override
  public Object getObject(Object key) {
    return delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    delegate.clear();
    keyList.clear();
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章