成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

重拾-Spring-AOP

468122151 / 1680人閱讀

摘要:簡(jiǎn)單點(diǎn)說(shuō)也就是當(dāng)前切面將會(huì)攔截哪些類(lèi)下的哪些方法,攔截過(guò)程中會(huì)采用哪些增強(qiáng)處理前置通知,返回通知,異常通知。切面鏈,是一系列的切面的集合。

AOP 術(shù)語(yǔ)

關(guān)于 AOP 的概念描述及相關(guān)術(shù)語(yǔ)可以參考 徹底征服 Spring AOP 之 理論篇 總結(jié)的很好; 本文將著重分析下 AOP 的實(shí)現(xiàn)過(guò)程。

使用示例 定義接口
public interface UserService {
    void say ();
}

接口實(shí)現(xiàn)類(lèi)如下:

public class UserServiceImpl implements UserService {
    public void say() {
        System.out.println("do say method");
    }
}
定義通知
public class UserAdvice implements MethodBeforeAdvice {

    public void before(Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do before advice ....");
    }
}
配置 AOP

    
    

    
    

    
    
        
        
        
            org.springframework.aop.UserService
        
        
        
            
                userAdvice
            
        
        
        
            
        
    
測(cè)試
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/org/springframework/aop/aop.xml");

UserService userService = (UserService) ctx.getBean("userProxy");

userService.say();

執(zhí)行結(jié)果如下:

do before advice ....
do say method

從執(zhí)行結(jié)果來(lái)看,前置通知對(duì)接口方法已經(jīng)起增強(qiáng)作用。 下面我們將看下 Spring AOP 的具體實(shí)現(xiàn)。

實(shí)現(xiàn)分析
從上面的示例可以看出 Spring AOP 的配置主要基于類(lèi) ProxyFactoryBean ,那么我們就以此為入口去剖析其實(shí)現(xiàn)。
ProxyFactoryBean 類(lèi)結(jié)構(gòu)

創(chuàng)建切面鏈

ProxyFactoryBean 的類(lèi)結(jié)構(gòu),我們發(fā)現(xiàn)其實(shí)現(xiàn)了接口 BeanFactoryAware,也就說(shuō)明在其實(shí)例化過(guò)程中會(huì)調(diào)用方法 setBeanFactory; 源碼如下:

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    // 設(shè)置 beanFactory
    this.beanFactory = beanFactory;
    logger.debug("Set BeanFactory. Will configure interceptor beans...");
    // 創(chuàng)建 advisor chain
    createAdvisorChain();
    logger.info("ProxyFactoryBean config: " + this);
    if (singleton) {
        // Eagerly initialize the shared singleton instance
        getSingletonInstance();
        // We must listen to superclass advice change events to recache singleton
        // instance if necessary
        addListener(this);
    }
}

setBeanFactory 方法中除了設(shè)置 beanFactory , 還有一個(gè)重要的動(dòng)作就是 createAdvisorChain 創(chuàng)建 advisor chain (也可以理解為就是切面鏈)。 那么下面我們將看下具體是怎樣創(chuàng)建 advisor chain 的。

private void createAdvisorChain() throws AopConfigException, BeansException {
    // 檢測(cè)是否配置了 interceptorNames, 也就是是否配置相關(guān) advice 通知; 若沒(méi)有配置直接返回
    if (this.interceptorNames == null || this.interceptorNames.length == 0) {
        //throw new AopConfigException("Interceptor names are required");
        return;
    }
    
    // Globals can"t be last
    if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX)) {
        throw new AopConfigException("Target required after globals");
    }

    // Materialize interceptor chain from bean names
    for (int i = 0; i < this.interceptorNames.length; i++) {
        String name = this.interceptorNames[i];
        logger.debug("Configuring interceptor "" + name + """);
        // 判斷 interceptor name 是否以 * 結(jié)尾
        if (name.endsWith(GLOBAL_SUFFIX)) {
            if (!(this.beanFactory instanceof ListableBeanFactory)) {
                throw new AopConfigException("Can only use global advisors or interceptors with a ListableBeanFactory");
            }
            else {
                addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                                 name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
            }
        }
        else {
            // add a named interceptor
            // 獲取 advice bean
            Object advice = this.beanFactory.getBean(this.interceptorNames[i]);
            // 將 advisor 加入到鏈表中
            addAdvisor(advice, this.interceptorNames[i]);
        }
    }
}
private void addAdvisor(Object next, String name) {
    logger.debug("Adding advisor or TargetSource [" + next + "] with name [" + name + "]");
    // We need to add a method pointcut so that our source reference matches
    // what we find from superclass interceptors.
    // 查找 advice 通知匹配的 pointcut, 并創(chuàng)建一個(gè) advisor
    Object advisor = namedBeanToAdvisorOrTargetSource(next);
    if (advisor instanceof Advisor) {
        // if it wasn"t just updating the TargetSource
        logger.debug("Adding advisor with name [" + name + "]");
        addAdvisor((Advisor) advisor);
        this.sourceMap.put(advisor, name);
    }
    else {
        logger.debug("Adding TargetSource [" + advisor + "] with name [" + name + "]");
        setTargetSource((TargetSource) advisor);
        // save target name
        this.targetName = name;
    }
}

addAdvisor 方法可以看到,在添加 advisor 前,需要先創(chuàng)建 advisor , 會(huì)調(diào)用方法 namedBeanToAdvisorOrTargetSource

private Object namedBeanToAdvisorOrTargetSource(Object next) {
    try {
        // 將 advice 包裝成一個(gè) advisor
        Advisor adv = GlobalAdvisorAdapterRegistry.getInstance().wrap(next);
        return adv;
    }
    catch (UnknownAdviceTypeException ex) {
        
    }
}

namedBeanToAdvisorOrTargetSource 方法會(huì)調(diào)用單例模式的 GlobalAdvisorAdapterRegistry 的方法 wrap 將 advice 包裝成一個(gè) advisor;
在查看 wrap 的實(shí)現(xiàn)之前,我們可以先看下 GlobalAdvisorAdapterRegistry 是做什么的。

public class GlobalAdvisorAdapterRegistry extends DefaultAdvisorAdapterRegistry {
    
    private static GlobalAdvisorAdapterRegistry instance = new GlobalAdvisorAdapterRegistry();
    
    public static GlobalAdvisorAdapterRegistry getInstance() {
        return instance;
    }

    private GlobalAdvisorAdapterRegistry() {
    }    
}

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry {
    
    private List adapters = new LinkedList();
    
    public DefaultAdvisorAdapterRegistry() {
        // register well-known adapters
        registerAdvisorAdapter(new BeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }
}

從上面 GlobalAdvisorAdapterRegistry 的實(shí)現(xiàn)可以看出其采用了單例模式并繼承了類(lèi) DefaultAdvisorAdapterRegistry 在構(gòu)造的過(guò)程中內(nèi)置了 3 種 advice adapter 用于匹配 advice 。 下面我們?cè)诳聪滤侨绾?wrap 包裝 advice 的。

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 Interceptor) {
        // So well-known it doesn"t even need an adapter
        return new DefaultPointcutAdvisor(advice);
    }

    // 遍歷內(nèi)置的 advice adapters
    for (int i = 0; i < this.adapters.size(); i++) {
        // Check that it is supported
        AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
        // 判斷當(dāng)前 adapter 是否支付當(dāng)前 advice
        if (adapter.supportsAdvice(advice)) {
            // 如果支持的話,返回一個(gè) DefaultPointcutAdvisor
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

wrap 的實(shí)現(xiàn)可以發(fā)現(xiàn),若 advice 匹配了某個(gè) adapter 將會(huì)創(chuàng)建一個(gè) DefaultPointcutAdvisor 實(shí)例并返回;

public class DefaultPointcutAdvisor implements PointcutAdvisor, Ordered {

    private int order = Integer.MAX_VALUE;

    private Pointcut pointcut;
    
    private Advice advice;
    
    public DefaultPointcutAdvisor() {
    }
    
    public DefaultPointcutAdvisor(Advice advice) {
        this(Pointcut.TRUE, advice);
    }
    
    public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
        this.pointcut = pointcut;
        this.advice = advice;
    }
}

/**
 * Canonical instance that matches everything.
 * 默認(rèn)匹配所有的類(lèi)及類(lèi)下的所有方法
 */
Pointcut TRUE = new Pointcut() {

    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    public MethodMatcher getMethodMatcher() {
        return MethodMatcher.TRUE;
    }

    public String toString() {
        return "Pointcut.TRUE";
    }
};

DefaultPointcutAdvisor 的實(shí)例可以看出創(chuàng)建 advisor (切面) 的過(guò)程實(shí)際就是將 advice (通知) 和 pointcut (切入點(diǎn)) 綁定的過(guò)程;同時(shí)在 Spring AOP 默認(rèn)的 pointcut 是攔截所有類(lèi)下的所有方法。

簡(jiǎn)單點(diǎn)說(shuō)也就是當(dāng)前切面將會(huì)攔截哪些類(lèi)下的哪些方法,攔截過(guò)程中會(huì)采用哪些增強(qiáng)處理(前置通知,返回通知,異常通知)。

至此 advisor chain 的創(chuàng)建流程結(jié)束,其過(guò)程大概如下:

遍歷 interceptor names (也就是 advice 通知)

獲取 advice bean

判斷 advice 是否匹配內(nèi)置的 advisorAdapter, 匹配的話則創(chuàng)建 DefaultPointcutAdvisor (默認(rèn)攔截所有類(lèi)所有方法) 加入到鏈表中

創(chuàng)建目標(biāo)代理對(duì)象

ProxyFactoryBean 類(lèi)的名字及類(lèi)結(jié)構(gòu),發(fā)現(xiàn)其實(shí)現(xiàn)接口 FactoryBean, 也就是說(shuō)當(dāng)其 getBean 的時(shí)候會(huì)調(diào)用方法 getObject, 源碼如下:

public Object getObject() throws BeansException {
    // 默認(rèn)單例
    return (this.singleton) ? getSingletonInstance() : newPrototypeInstance();
}

private Object getSingletonInstance() {
    if (this.singletonInstance == null) {
        // This object can configure the proxy directly if it"s
        // being used as a singleton.
        this.singletonInstance = createAopProxy().getProxy();
    }
    return this.singletonInstance;
}

protected synchronized AopProxy createAopProxy() {
    if (!isActive) {
        activate();
    }
    
    return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
    // 是否采用 cglib 代理
    boolean useCglib = advisedSupport.getOptimize() || advisedSupport.getProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0;
    if (useCglib) {
        return CglibProxyFactory.createCglibProxy(advisedSupport);
    }
    else {
        // Depends on whether we have expose proxy or frozen or static ts
        return new JdkDynamicAopProxy(advisedSupport);
    }
}
public Object getProxy(ClassLoader cl) {
    logger.debug("Creating JDK dynamic proxy");
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}
ProxyFactoryBean 通過(guò)判斷 proxyTargetClass , interfaceNames 的配置去選擇采用 cglib 或者 jdk 來(lái)創(chuàng)建目標(biāo)代理對(duì)象。
目標(biāo)代理對(duì)象執(zhí)行

上面簡(jiǎn)單介紹了代理對(duì)象的創(chuàng)建,那么在看下當(dāng)我們調(diào)用目標(biāo)方法的時(shí)候,代理是如何執(zhí)行的,以 jdk 動(dòng)態(tài)代理為例:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    MethodInvocation invocation = null;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = advised.targetSource;
    Class targetClass = null;
    Object target = null;        
    
    try {
        // Try special rules for equals() method and implementation of the
        // Advised AOP configuration interface
        
        // Short-circuit expensive Method.equals() call, as Object.equals() isn"t overloaded
        if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
            // What if equals throws exception!?

            // This class implements the equals() method itself
            return new Boolean(equals(args[0]));
        }
        else if (Advised.class == method.getDeclaringClass()) {
            // Service invocations on ProxyConfig with the proxy config
            return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        
        Object retVal = null;
        
        // May be null. Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        // 目標(biāo)實(shí)現(xiàn)類(lèi)
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
    
        // Get the interception chain for this method
        // 獲取目標(biāo)類(lèi),執(zhí)行方法的 interception chain
        List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this.advised, proxy, method, targetClass);
        
        // Check whether we have any advice. If we don"t, we can fallback on
        // direct reflective invocation of the target, and avoid creating a MethodInvocation
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying
            retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
            
            invocation = new ReflectiveMethodInvocation(proxy, target,
                                method, args, targetClass, chain);
                                    
            // Proceed to the joinpoint through the interceptor chain
            // 方法調(diào)用
            retVal = invocation.proceed();
        }
        
        // Massage return value if necessary
        if (retVal != null && retVal == target) {
            retVal = proxy;
        }
        return retVal;
    }
    finally {
    }
}

首先我們看下如何獲取匹配當(dāng)前 method 的攔截器, 參考 calculateInterceptorsAndDynamicInterceptionAdvice 的實(shí)現(xiàn)如下:

public static List calculateInterceptorsAndDynamicInterceptionAdvice(Advised config, Object proxy, Method method, Class targetClass) {
    // 用于存儲(chǔ)攔截器
    List interceptors = new ArrayList(config.getAdvisors().length);
    // 遍歷 advisor (切面)
    for (int i = 0; i < config.getAdvisors().length; i++) {
        Advisor advisor = config.getAdvisors()[i];
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // 判斷當(dāng)前 target class 是否當(dāng)前 pointcut
            if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
                // 獲取 advisor 對(duì)應(yīng)的 method interceptor 
                MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                // 判斷當(dāng)前 method 是否匹配 pointcut
                if (mm.matches(method, targetClass)) {
                    if (mm.isRuntime()) {
                        // Creating a new object instance in the getInterceptor() method
                        // isn"t a problem as we normally cache created chains
                        interceptors.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm) );
                    }
                    else {                            
                        // 將攔截器加入鏈表中
                        interceptors.add(interceptor);
                    }
                }
            }
        }
        else if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            if (ia.getClassFilter().matches(targetClass)) {
                MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
                interceptors.add(interceptor);
            }
        }
    }    // for
    return interceptors;
}    // calculateInterceptorsAndDynamicInterceptionAdvice

我們?cè)谠敿?xì)看下如何查找 advisor 匹配的攔截器呢,同樣與上文中 wrap 類(lèi)似,如下:

public Interceptor getInterceptor(Advisor advisor) throws UnknownAdviceTypeException {
    Advice advice = advisor.getAdvice();
    if (advice instanceof Interceptor) {
        return (Interceptor) advice;
    }

    // 遍歷內(nèi)置的 advisor adapter
    for (int i = 0; i < this.adapters.size(); i++) {
        AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
        // 是否匹配當(dāng)前 advice
        if (adapter.supportsAdvice(advice)) {
            // 匹配的話返回 interceptor
            return adapter.getInterceptor(advisor);
        }
    }
    throw new UnknownAdviceTypeException(advisor.getAdvice());
}

到目前為止,我們多次發(fā)現(xiàn) AdvisorAdapter 的身影,下面我們看下其具體的實(shí)現(xiàn), 以 BeforeAdviceAdapter 為例:

class BeforeAdviceAdapter implements AdvisorAdapter {

    /**
     * @see org.springframework.aop.framework.adapter.AdvisorAdapter#supportsAdvice(java.lang.Object)
     */
    public boolean supportsAdvice(Advice advice) {
        // 匹配 MethodBeforeAdvice
        return advice instanceof MethodBeforeAdvice;
    }

    /**
     * @see org.springframework.aop.framework.adapter.AdvisorAdapter#getInterceptor(org.springframework.aop.Advisor)
     */
    public Interceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        // 返回 MethodBeforeAdviceInterceptor
        return new MethodBeforeAdviceInterceptor(advice) ;
    }

}
通過(guò) AdvisorAdapter 很巧妙的將 Advice 和 Interceptor 結(jié)合起來(lái),同時(shí)也會(huì)發(fā)現(xiàn)二者關(guān)系是一一對(duì)應(yīng)的

下面在看下方法的真正調(diào)用過(guò)程, 由 ReflectiveMethodInvocation 的方法 proceed 實(shí)現(xiàn):

public Object proceed() throws Throwable {
    //    We start with an index of -1 and increment early
    // 當(dāng)執(zhí)行到最后一個(gè)攔截器的時(shí)候?qū)?huì)調(diào)用目標(biāo)方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // 獲取下一個(gè)攔截器
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    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)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed
            // Skip this interceptor and invoke the next in the chain
            return proceed();
        }
    }
    else {
        // It"s an interceptor so we just invoke it: the pointcut will have
        // been evaluated statically before this object was constructed
        // 執(zhí)行攔截器
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

下面具體看下 MethodInterceptor 的實(shí)現(xiàn),分別是前置通知,返回通知,異常通知

public Object invoke(MethodInvocation mi) throws Throwable {
    // 目標(biāo)方法前執(zhí)行
    advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
    return mi.proceed();
}

public Object invoke(MethodInvocation mi) throws Throwable {
    // 先執(zhí)行目標(biāo)方法
    Object retval = mi.proceed();
    // 后置處理
    advice.afterReturning(retval, mi.getMethod(), mi.getArguments(), mi.getThis() );
    return retval;
}

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 執(zhí)行目標(biāo)方法
        return mi.proceed();
    }
    catch (Throwable t) {
        // 異常處理
        Method handlerMethod = getExceptionHandler(t);
        if (handlerMethod != null) {
            invokeHandlerMethod(mi, t, handlerMethod);
        }
        throw t;
    }
}

至此 Spring AOP 代理對(duì)象的執(zhí)行過(guò)程處理結(jié)束,其流程可大概總結(jié)如下:

獲取當(dāng)前目標(biāo)方法的 interceptor chain

遍歷 advisor ,判斷當(dāng)前目標(biāo)類(lèi)和目標(biāo)方法是否匹配 advisor 對(duì)應(yīng)的 ponitcut

通過(guò)匹配的 advisor 對(duì)應(yīng)的 advice 匹配對(duì)應(yīng)的 advisorAdapter , 進(jìn)而獲取對(duì)應(yīng)的 methodInterceptor

執(zhí)行攔截器

執(zhí)行目標(biāo)方法

小結(jié)

Spring AOP 中的對(duì)象關(guān)系小結(jié)下:

Advisor : 翻譯是顧問(wèn),簡(jiǎn)單理解其就是一個(gè) Aspect (切面); 其內(nèi)部綁定了對(duì)應(yīng)的 Pointcut(切入點(diǎn)) 和 Advice(通知)。

Advisor Chain : 切面鏈,是一系列的切面的集合。

Advice : 通知,是對(duì)攔截方法的增強(qiáng)處理;在 1.0 版本中包含 BeforeAdivce, AfterReturningAdvice, ThrowsAdvice; 其面向的是用戶。

MethodInterceptor : 方法攔截器,是 Advice 的執(zhí)行者; 與 Advice 是一一對(duì)應(yīng)的。

AdvisorAdapter : Advice 的適配器,是 Advice 和 MethodInterceptor 匹配的紐帶。

AdvisorAdapterRegistry : 是 AdvisorAdapter 的注冊(cè)中心,內(nèi)置了 BeforeAdviceAdapter, AfterReturnAdviceAdapter, ThrowsAdviceAdapter; 用來(lái)將 Advice wrap 成一個(gè) Advisor 并提供獲取 Advice 對(duì)應(yīng)的 MethodInterceptor。

使用 Spring 1.0 版本時(shí), 當(dāng)我們自定義 Advice 時(shí),可不可以同時(shí)支持多種 Advice 呢 ? 譬如:

public class UserAdvice implements MethodBeforeAdvice, AfterReturningAdvice {

    public void before(Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do before advice ....");
    }

    public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do after returning ....");
    }
}

那么當(dāng)測(cè)試后,您會(huì)發(fā)現(xiàn)只有 before 調(diào)用了,而 afterReturning 未調(diào)用了;這是為什么呢 ? (好好看源碼額)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73531.html

相關(guān)文章

  • 重拾css(1)——寫(xiě)在前邊的話

    摘要:本系列文章重拾主要參考王福朋知多少,結(jié)合自己的理解和學(xué)習(xí)需要,修改或添加了一些內(nèi)容,難免有失偏頗,僅供自我學(xué)習(xí)參考之用。 工作中或多或少的寫(xiě)一些css,但總感覺(jué)掌握的不夠扎實(shí),時(shí)而需要查閱一下知識(shí)點(diǎn)。我想,一方面跟缺少科班出身式的系統(tǒng)學(xué)習(xí)有關(guān),另一方面也苦于一直未尋覓到一套合我胃口教程。直到我讀到了王福朋css知多少系列文章,使我有了重新系統(tǒng)學(xué)習(xí)css的想法。 本系列文章(重拾css)...

    li21 評(píng)論0 收藏0
  • CSS魔法堂:重拾Border之——更廣闊的遐想

    摘要:也就是說(shuō)我們操作的幾何公式中的未知變量,而具體的畫(huà)圖操作則由渲染引擎處理,而不是我們苦苦哀求設(shè)計(jì)師幫忙。 前言 ?當(dāng)CSS3推出border-radius屬性時(shí)我們是那么欣喜若狂啊,一想到終于不用再添加額外元素來(lái)模擬圓角了,但發(fā)現(xiàn)border-radius還分水平半徑和垂直半徑,然后又發(fā)現(xiàn)border-top-left/right-radius的水平半徑之和大于元素寬度時(shí),實(shí)際值會(huì)按比...

    lily_wang 評(píng)論0 收藏0
  • CSS魔法堂:重拾Border之——解構(gòu)Border

    摘要:本系列將稍微深入探討一下那個(gè)貌似沒(méi)什么好玩的魔法堂重拾之解構(gòu)魔法堂重拾之圖片作邊框魔法堂重拾之不僅僅是圓角魔法堂重拾之更廣闊的遐想解構(gòu)說(shuō)起我們自然會(huì)想起,而由條緊緊包裹著的邊組成,所以的最小操作單元是。 前言 ?當(dāng)CSS3推出border-radius屬性時(shí)我們是那么欣喜若狂啊,一想到終于不用再添加額外元素來(lái)模擬圓角了,但發(fā)現(xiàn)border-radius還分水平半徑和垂直半徑,然后又發(fā)現(xiàn)...

    lingdududu 評(píng)論0 收藏0
  • Hello Spring-AOP

    摘要:簡(jiǎn)介什么是面向切面編程,是對(duì)傳統(tǒng)的面向?qū)ο缶幊痰难a(bǔ)充。通知有五種通知,執(zhí)行前,執(zhí)行后,執(zhí)行成功后,執(zhí)行拋出異常后,環(huán)繞通知。連接點(diǎn)連接點(diǎn)是一個(gè)應(yīng)用執(zhí)行過(guò)程中能夠插入一個(gè)切面的點(diǎn)。 OOP(Object Oriented Programming)面向?qū)ο缶幊探鉀Q了縱向上的層次分割,例如MVC模式將展示層、持久化層、邏輯處理層一一分開(kāi)了,使得開(kāi)發(fā)效率得到了較大提高,但是這只是縱向上的分割,...

    Kahn 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<