spring aop

一、何爲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/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章