• Spring技术内幕:Spring AOP的实现原理(五)


    7、Advice通知的实现
    AopProxy代理对象生成时,其拦截器也一并生成。以下我们来分析下Aop是怎样对目标对象进行增强的。在为AopProxy配置拦截器的实现中,有一个取得拦截器配置过程,这个过程由DefaultAvisorChainFactory实现的,而这个工厂类负责生成拦截器链,在它的getInterceptorsAndDynamicInterceptionA-
    dvice方法中,有一个适配器的注冊过程。通过配置Spring预先设计好的拦截器,Spring增加了它对Aop实现的处理。

    为具体了解这个过程,先从Defau-
    ltAdvisorChainFactory的实现開始。通过以下代码能够看到,在DefaultAdvisorChainFactory实现中。首先构造了一个GlobalAdvisorAdapterRegistry单件,然后对配置的Advisor通知器进行逐个遍历,这些通知链都是配置在interceptorNames中的,从getInterceptorsAndDynamicInterceptionAdvice传递进来的advised參数对象中,能够方便的取得配置的通知器,有了这些通知器,接着就是一个由
    GlobalAdvisorAdapterRegistry来完毕的拦截器的适配和注冊。

    /**
     * A simple but definitive way of working out an advice chain for a Method,
     * given an {@link Advised} object. Always rebuilds each advice chain;
     * caching can be provided by subclasses.
     * @author Juergen Hoeller
     * @author Rod Johnson
     * @author Adrian Colyer
     * @since 2.0.3
     */
    @SuppressWarnings("serial")
    public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
        public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
                Advised config, Method method, Class targetClass) {
            // This is somewhat tricky... we have to process introductions first,
            // but we need to preserve order in the ultimate list.
            List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
            boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
            // 得到注冊器GlobalAdvisorAdapterRegistry,这是一个单件模式的实现
            AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
            for (Advisor advisor : config.getAdvisors()) {
                if (advisor instanceof PointcutAdvisor) {
                    // Add it conditionally.
                    PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                    if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
                        MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                        MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                        if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
                            if (mm.isRuntime()) {
                                // Creating a new object instance in the getInterceptors() method
                                // isn't a problem as we normally cache created chains.
                                for (MethodInterceptor interceptor : interceptors) {
                                    interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, 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;
        }

    GlobalAdvisorAdapterRegistry的getInterceptors方法为AOP的实现做出了非常大的贡献,这种方法封装着advice织入实现的入口。我们先从GlobalAdvisorAdapterRegistry的实现入手,他基本起一个适配器的作用,但同一时候也是单件模式,代码例如以下:

         /**
         * Keep track of a single instance so we can return it to classes that request it.
         */
        // 使用静态变量来保持一个唯一实例
        private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();
        /**
         * Return the singleton {@link DefaultAdvisorAdapterRegistry} instance.
         */
        public static AdvisorAdapterRegistry getInstance() {
            return instance;
        }

    到这里,我们知道在DefaultAdvisorAdapterRegistry中,设置了一系列的adapter适配器,这是这些适配器的实现。为Spring的advice提供了编织能力。以下我们看看DefaultAdvisorAdapterRegistry到底发生了什么?adapter的作用具体分为两个:
    1、调用adapter的support方法,通过这种方法来推断取得的advice属于什么类型的advice通知,从而依据不同的advice类型来注冊不同的AdviceInterceptor,也就是前面我们看到的拦截器
    2、这些AdviceInterceptor都是Spring AOP框架设计好的,是为实现不同的advice功能提供服务的。

    有了这些AdviceInterceptor,能够方便的使用由Spring提供的各种不同的advice来设计AOP应用。也就是说,正是这些AdviceInterceptor终于实现了advice通知在AopProxy对象中的织入功能。

    /**
     * Default implementation of the {@link AdvisorAdapterRegistry} interface.
     * Supports {@link org.aopalliance.intercept.MethodInterceptor},
     * {@link org.springframework.aop.MethodBeforeAdvice},
     * {@link org.springframework.aop.AfterReturningAdvice},
     * {@link org.springframework.aop.ThrowsAdvice}.
     *
     * @author Rod Johnson
     * @author Rob Harrop
     * @author Juergen Hoeller
     */
    @SuppressWarnings("serial")
    public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
        private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
        /**
         * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
         */
        public DefaultAdvisorAdapterRegistry() {
            registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
            registerAdvisorAdapter(new AfterReturningAdviceAdapter());
            registerAdvisorAdapter(new ThrowsAdviceAdapter());
        }
        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) {
                // So well-known it doesn't even need an adapter.
                return new DefaultPointcutAdvisor(advice);
            }
            for (AdvisorAdapter adapter : this.adapters) {
                // Check that it is supported.
                if (adapter.supportsAdvice(advice)) {
                    return new DefaultPointcutAdvisor(advice);
                }
            }
            throw new UnknownAdviceTypeException(advice);
        }
        public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
            List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
            Advice advice = advisor.getAdvice();
            if (advice instanceof MethodInterceptor) {
                interceptors.add((MethodInterceptor) advice);
            }
            for (AdvisorAdapter adapter : this.adapters) {
                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);
        }
    }

    剥茧抽丝,继续看adapter,在DefaultAdvisorRegistry的getInterceptors调用中。从MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdaper这几个通知适配器的名字上能够看出和Advice一一相应,在这里,他们作为适配器被增加到adapter的List中,他们都是实现AdvisorAdapter接口的同一层次的类。仅仅是各自承担着不同的适配的任务,一对一的服务于不同的advice实现。
    以MethodBeforeAdviceAdapter为例。代码例如以下:

    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    
        public boolean supportsAdvice(Advice advice) {
            return (advice instanceof MethodBeforeAdvice);
        }
        // 把advice从通知器中取出
        public MethodInterceptor getInterceptor(Advisor advisor) {
            MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
            return new MethodBeforeAdviceInterceptor(advice);
        }
    }

    到这里就非常清楚了。Spring AOP为了实现advice的织入,设计了特定拦截器对这些功能进行了封装。

    尽管应用不会直接用到这些拦截器。但却是advice发挥作用必不可少的准备。还是以MethodBeforeAdviceInterceptor为例,我们看看advice是怎样封装的。

    在invoke回调方法中,看到首先触发了advice的before的回调,然后才是MethodInvocation的proceed方法的调用。看到这里。就已经和前面的在ReflectionMethodInvocation的Proceed分析中联系起来。回顾了一下,在AopProxy代理对象触发的ReflectionMethodInvocation的proceed方法中,在取得拦截器以后,启动了对拦截器invoke方法的调用。

    依照AOP的规则,ReflectiveMethodInvocation触发的拦截器invoke方法,终于会依据不同的advice类型,触发Spring对不同的advice的拦截器封装,比方对MethodBeforeAdvice,终于会依据不同的advice类型触发Spring对不同的advice的拦截器封装。比方对MethodBeforeAdvice。终于会触发MethodBeforeAdviceInterceptor的invoke方法。在MethodBeforeAdviceInterceptor方法中。会调用advice的before方法。这就是MethodBeforeAdvice所须要的对目标对象的增强效果:在方法调用之前通知增强。

    public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
        private MethodBeforeAdvice advice;
        /**
         * Create a new MethodBeforeAdviceInterceptor for the given advice.
         * @param advice the MethodBeforeAdvice to wrap
         */
        // 为指定的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 {
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
            return mi.proceed();
        }
    }

    完毕MethodBeforeAdviceInterceptor的调用。然后启动advice通知的afterReturning回调,代码例如以下:

    /**
         * Create a new AfterReturningAdviceInterceptor for the given advice.
         * @param advice the AfterReturningAdvice to wrap
         */
        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;
        }

    ThrowsAdvice的实现和上面相似,也是封装在相应的AdviceInterceptor中,ThrowsAdvice的回调方法要复杂一些,他维护了一个exceptionHandlerMap来相应不同的方法调用场景,这个exceptionHandlerMap中的handler的取得时与触发ThrowsAdvice增强的异常相关的。

    /**
     * Interceptor to wrap an after-throwing advice.
     *
     * <p>The signatures on handler methods on the {@code ThrowsAdvice}
     * implementation method argument must be of the form:<br>
     *
     * {@code void afterThrowing([Method, args, target], ThrowableSubclass);}
     *
     * <p>Only the last argument is required.
     *
     * <p>Some examples of valid methods would be:
     *
     * <pre class="code">public void afterThrowing(Exception ex)</pre>
     * <pre class="code">public void afterThrowing(RemoteException)</pre>
     * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
     * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
     *
     * <p>This is a framework class that need not be used directly by Spring users.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     */
    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;
        /** Methods on throws advice, keyed by exception class */
        private final Map<Class, Method> exceptionHandlerMap = new HashMap<Class, Method>();
        /**
         * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
         * @param throwsAdvice the advice object that defines the exception
         * handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
         * implementation)
         */
        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])
                    ) {
                    // Have an exception handler
                    // 配置异常处理
                    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 int getHandlerMethodCount() {
            return this.exceptionHandlerMap.size();
        }
        /**
         * Determine the exception handle method. Can return null if not found.
         * @param exception the exception thrown
         * @return a handler for the given exception type
         */
        private Method getExceptionHandler(Throwable exception) {
            Class exceptionClass = exception.getClass();
            if (logger.isTraceEnabled()) {
                logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");
            }
            Method handler = this.exceptionHandlerMap.get(exceptionClass);
            while (handler == null && !exceptionClass.equals(Throwable.class)) {
                exceptionClass = exceptionClass.getSuperclass();
                handler = this.exceptionHandlerMap.get(exceptionClass);
            }
            if (handler != null && logger.isDebugEnabled()) {
                logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);
            }
            return handler;
        }
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                // 把目标对象方法调用放在try catch中。并在catch中触发。
                // ThrowsAdvice的回调,把异常接着向外抛出,不做过多的处理
                return mi.proceed();
            }
            catch (Throwable ex) {
                Method handlerMethod = getExceptionHandler(ex);
                if (handlerMethod != null) {
                    invokeHandlerMethod(mi, ex, handlerMethod);
                }
                throw ex;
            }
        }
        // 通过反射启动对ThrowsAdvice回调方法的调用
        private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
            Object[] handlerArgs;
            if (method.getParameterTypes().length == 1) {
                handlerArgs = new Object[] { ex };
            }
            else {
                handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
            }
            try {
                method.invoke(this.throwsAdvice, handlerArgs);
            }
            catch (InvocationTargetException targetEx) {
                throw targetEx.getTargetException();
            }
        }
    }

    未完待续……

  • 相关阅读:
    数据仓库_Linux(3)
    2.1(构造序对)
    要修改一万个位置的jdk版本
    8个球7个一样重的,有一个偏重,一个天平,如何两次找出偏重的小球
    玄学
    异常:java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/LoopTag
    提高输入效率
    fan
    idea
    打印整数的补码
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7133719.html
Copyright © 2020-2023  润新知