一、何爲aop
AOP(Aspect-Oriented Programming)面向方面編程,可以說是OOP(Object-Oriented Programing)面向對象編程的補充和完善。AOP(面向切面編程)與OOP(面向對象編程)的區別是什麼。其實AOP與OOP可以理解爲不在同一層面上的兩個獨立的定義。也就是說其實這兩個東西沒法去做一個對等的比較,OOP專注於對象,我們利用對象的屬性,行爲來解決現實中的問題,而AOP則用來在使用OOP解決問題的過程中增強解決問題的能力,實現更好的模塊化。
AOP編程思想,它利用一種稱爲“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關係,如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行爲;那麼面向方面編程的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息。而剖開的切面,也就是所謂的“方面”了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。
二、編程中的aop
aop並非在spring首先引入的,而是由來已久,他是前人智慧的結晶,仔細尋找就能發現其蹤跡。
第一次接觸servlet 過濾器 filter亂碼的解決
默認的解決亂碼的思路是在每個方法中寫request.setCharacterEncoding("UTF-8");並且有其侷限性,代碼也不夠簡介明瞭,filter的出現打破了這個侷限,在請求之前做響應的操作。
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.mingxungu.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
原理:通過過濾器攔截所有的請求,對中文亂碼的解決通過一個攔截器類統一進行解決。
原理圖:
第二次接觸Java的動態代理
代碼:
//JDK動態代理
public class UserServiceProxyFactory implements InvocationHandler {
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
private UserService us;
public UserService getUserServiceProxy(){
//生成動態代理
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
this);
//返回
return usProxy;
}
@Override
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
//額外的責任
System.out.println("打開事務!");
//執行主體方法
Object invoke = method.invoke(us, arg2)
//額外的責任
System.out.println("提交事務!");return invoke;
}
}
原理:點擊查看動態代理的原理 invoke()主體方法,通過java的反射機制 Object invoke = method.invoke(us, arg2)獲取執行主體方法,在前後加入與業務無關的額外的方法
原理圖:
第三次接觸struts攔截器Interceptor
我們發現在jsp中定義好name屬性值,根據 對象名.屬性名(user.name)的方式在Action中獲取對象的時候其屬性值已經被塞入其中。這不得不歸功於Interceptor。
我們struts的一個重要功能之一就是參數賦值,我們就回顧一下其源碼實現
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();//獲取當前執行的Action對象
if (!(action instanceof NoParameters)) {//判斷Action是否實現了NoParameters接口,實現該接口表示該Action沒有任何請求參數
ActionContext ac = invocation.getInvocationContext();//獲取ActionContext對象
final Map<String, Object> parameters = retrieveParameters(ac);//獲取請求參數Map
//省略...
if (parameters != null) {//如果請求參數不爲null
Map<String, Object> contextMap = ac.getContextMap();//獲取ActionContext內部的context Map,即OgnlContext對象
try {
//省略...
ValueStack stack = ac.getValueStack();//獲取值棧
setParameters(action, stack, parameters);//爲值棧設置參數
} finally {
//省略...
}
}
}
return invocation.invoke();//調用下一個攔截器
}
setParameters方法纔是ParametersInterceptor攔截器的主要邏輯protected void setParameters(Object action, ValueStack stack, final Map<String, Object> parameters) {
ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware)
? (ParameterNameAware) action : null;//判斷Action有無實現ParameterNameAware接口
Map<String, Object> params;
Map<String, Object> acceptableParameters;//合法參數集合
//判斷參數設置是否有序,ordered默認爲false,即無序
if (ordered) {
params = new TreeMap<String, Object>(getOrderedComparator());//如果有序則要獲取比較器
acceptableParameters = new TreeMap<String, Object>(getOrderedComparator());
params.putAll(parameters);
} else {
params = new TreeMap<String, Object>(parameters);
acceptableParameters = new TreeMap<String, Object>();
}
//迭代請求參數
for (Map.Entry<String, Object> entry : params.entrySet()) {
String name = entry.getKey();
//判斷參數是否合法,如果Action實現了ParameterNameAware則acceptableName(name)返回true且parameterNameAware.acceptableParameterName(name)
//也返回true該參數纔是合法的;如果Action沒有實現ParameterNameAware則參數是否合法由acceptableName(name)方法決定
boolean acceptableName = acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
//如果參數合法
if (acceptableName) {
acceptableParameters.put(name, entry.getValue());//把合法參數添加到合法參數集合中
}
}
ValueStack newStack = valueStackFactory.createValueStack(stack);
//省略...
for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {//迭代合法參數
String name = entry.getKey();//參數名
Object value = entry.getValue();//參數值
try {
newStack.setValue(name, value);//將該參數設置到ValueStack中
} catch (RuntimeException e) {
//省略...
}
}
//省略...
//看該方法的名稱是將合法參數添加到ActionContext中,但在該攔截器中,該方法爲空實現,無任何代碼
//該方法被聲明爲protected,即子類可以覆蓋該方法以改變行爲
addParametersToContext(A
原理:查看源碼不難發現使用的是Interceptor攔截器獲取request中的鍵值對和值棧中找有相應setter方法的對象進行賦值。
原理圖:
三、spring aop
首先了解其中幾個名詞
Pointcut (切入點):目標對象,已經增強的飯方法。
Joinpoint (連接點):目標對象中,所有可以增強的方法。
Advice(通知/增強):增強的代碼。
Target (目標對象):被通知的對象。
Weaving(織入):將通知應用到切入點的過程。
Proxy (代理):將通知織入到目標對象之後,形成的代理對象。
Aspect(切面):切入點+通知
spring 的 aop的原理
spring使用動態代理來實現aop的。spring提供了兩種方式來生成代理對象: JDK Proxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據AdvisedSupport對象的配置來決定。默認的策略是如果目標類是接口,則使用JDK動態代理技術,否則使用Cglib來生成代理。下面我們來研究一下Spring如何使用JDK來生成代理對象,具體的生成代碼放在JdkDynamicAopProxy這個類中,相關代碼:
/**
* <ol>
* <li>獲取代理類要實現的接口,除了Advised對象中配置的,還會加上SpringProxy, Advised(opaque=false)
* <li>檢查上面得到的接口中有沒有定義 equals或者hashcode的接口
* <li>調用Proxy.newProxyInstance創建代理對象
* </ol>
*/
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);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
我們知道InvocationHandler是JDK動態代理的核心,生成的代理對象的方法調用都會委託到InvocationHandler.invoke()方法。而通過JdkDynamicAopProxy的簽名我們可以看到這個類其實也實現了InvocationHandler,下面我們就通過分析這個類中實現的invoke()方法來具體看下Spring AOP是如何織入切面的。public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {
MethodInvocation invocation = null;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class targetClass = null;
Object target = null;
try {
//eqauls()方法,具目標對象未實現此方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){
return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);
}
//hashCode()方法,具目標對象未實現此方法
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){
return newInteger(hashCode());
}
//Advised接口或者其父接口中定義的方法,直接反射調用,不應用通知
if (!this.advised.opaque &&method.getDeclaringClass().isInterface()
&&method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations onProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);
}
Object retVal = null;
if (this.advised.exposeProxy) {
// Make invocation available ifnecessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//獲得目標對象的類
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//獲取可以應用到此方法上的Interceptor列表
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
//如果沒有可以應用到此方法的通知(Interceptor),此直接反射調用 method.invoke(target, args)
if (chain.isEmpty()) {
retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
} else {
//創建MethodInvocation
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// Massage return value if necessary.
if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)
&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned"this" and the return type of the method
// is type-compatible. Notethat we can't help if the target sets
// a reference to itself inanother returned object.
retVal = proxy;
}
return retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
// Must have come fromTargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
獲取可以應用到此方法上的通知鏈(Interceptor Chain),如果有,則應用通知,並執行joinpoint; 如果沒有,則直接反射執行joinpoint。而這裏的關鍵是通知鏈是如何獲取的以及它又是如何執行的,下面逐一分析下。首先,從上面的代碼可以看到,通知鏈是通過Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,我們來看下這個方法的實現:
public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
MethodCacheKeycacheKey = new MethodCacheKey(method);
List<Object>cached = this.methodCache.get(cacheKey);
if(cached == null) {
cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this,method, targetClass);
this.methodCache.put(cacheKey,cached);
}
returncached;
}
可以看到實際的獲取工作其實是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()這個方法來完成的,獲取到的結果會被緩存。
下面來分析下這個方法的實現:/**
* 從提供的配置實例config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor,
* 則判斷此Advisor能否應用到目標類targetClass上.如果是PointcutAdvisor,則判斷
* 此Advisor能否應用到目標方法method上.將滿足條件的Advisor通過AdvisorAdaptor轉化成Interceptor列表返回.
*/
publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {
// This is somewhat tricky... we have to process introductions first,
// but we need to preserve order in the ultimate list.
List interceptorList = new ArrayList(config.getAdvisors().length);
//查看是否包含IntroductionAdvisor
boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);
//這裏實際上註冊一系列AdvisorAdapter,用於將Advisor轉化成MethodInterceptor
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
for (int i = 0; i <advisors.length; i++) {
Advisor advisor = advisors[i];
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;
if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
//TODO: 這個地方這兩個方法的位置可以互換下
//將Advisor轉化成Interceptor
MethodInterceptor[]interceptors = registry.getInterceptors(advisor);
//檢查當前advisor的pointcut是否可以匹配當前方法
MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {
if(mm.isRuntime()) {
// Creating a newobject instance in the getInterceptors() method
// isn't a problemas we normally cache created chains.
for (intj = 0; j < interceptors.length; j++) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor){
IntroductionAdvisor ia =(IntroductionAdvisor) advisor;
if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
Interceptor[] interceptors= registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} else {
Interceptor[] interceptors =registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
這個方法執行完成後,Advised中配置能夠應用到連接點或者目標類的Advisor全部被轉化成了MethodInterceptor.接下來我們再看下得到的攔截器鏈是怎麼起作用的。if (chain.isEmpty()) {
retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
} else {
//創建MethodInvocation
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
從這段代碼可以看出,如果得到的攔截器鏈爲空,則直接反射調用目標方法,否則創建MethodInvocation,調用其proceed方法,觸發攔截器鏈的執行,來看下具體代碼 public Object proceed() throws Throwable {
// We start with an index of -1and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {
//如果Interceptor執行完了,則執行joinPoint
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//如果要動態匹配joinPoint
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
//動態匹配:運行時參數是否滿足匹配條件
if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) {
//執行當前Intercetpor
returndm.interceptor.invoke(this);
}
else {
//動態匹配失敗時,略過當前Intercetpor,調用下一個Interceptor
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcutwill have
// been evaluated statically before this object was constructed.
//執行當前Intercetpor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
部分代碼參考:http://blog.csdn.net/moreevan/article/details/11977115/