在上篇文章《Spring設計思想》AOP設計基本原理 中闡述了Spring AOP 的基本原理以及基本機制,本文將深刻源碼,詳細闡述整個Spring AOP實現的整個過程。java
讀完本文,你將瞭解到:git
一、Spring內部建立代理對象的過程
二、Spring AOP的核心---ProxyFactoryBean
三、基於JDK面向接口的動態代理JdkDynamicAopProxy生成代理對象
四、基於Cglib子類繼承方式的動態代理CglibAopProxy生成代理對象
五、各類Advice是的執行順序是如何和方法調用進行結合的?
六、PointCut與Advice的結合------Adivce的條件執行
七、總結
在Spring的底層,若是咱們配置了代理模式,Spring會爲每個Bean建立一個對應的ProxyFactoryBean的FactoryBean來建立某個對象的代理對象。github
假定咱們如今有一個接口TicketService及其實現類RailwayStation,咱們打算建立一個代理類,在執行TicketService的方法時的各個階段,插入對應的業務代碼。算法
[java] view plain copy print ?
![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- /**
- * 售piao服務
- * Created by louis on 2016/4/14.
- */
- public interface TicketService {
- //售piao
- public void sellTicket();
- //問詢
- public void inquire();
- //退piao
- public void withdraw();
- }
[java] view plain copy print ?![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- /**
- * RailwayStation 實現 TicketService
- * Created by louis on 2016/4/14.
- */
- public class RailwayStation implements TicketService {
- public void sellTicket(){
- System.out.println("售piao............");
- }
- public void inquire() {
- System.out.println("問詢.............");
- }
- public void withdraw() {
- System.out.println("退piao.............");
- }
- }
[java] view plain copy print ?![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.springframework.aop.MethodBeforeAdvice;
- import java.lang.reflect.Method;
- /**
- * 執行RealSubject對象的方法以前的處理意見
- * Created by louis on 2016/4/14.
- */
- public class TicketServiceBeforeAdvice implements MethodBeforeAdvice {
- public void before(Method method, Object[] args, Object target) throws Throwable {
- System.out.println("BEFORE_ADVICE: 歡迎光臨代售點....");
- }
- }
[java] view plain copy print ?![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.springframework.aop.AfterReturningAdvice;
- import java.lang.reflect.Method;
- /**
- * 返回結果時後的處理意見
- * Created by louis on 2016/4/14.
- */
- public class TicketServiceAfterReturningAdvice implements AfterReturningAdvice {
- @Override
- public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
- System.out.println("AFTER_RETURNING:本次服務已結束....");
- }
- }
[java] view plain copy print ?![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.springframework.aop.ThrowsAdvice;
- import java.lang.reflect.Method;
- /**
- * 拋出異常時的處理意見
- * Created by louis on 2016/4/14.
- */
- public class TicketServiceThrowsAdvice implements ThrowsAdvice {
- public void afterThrowing(Exception ex){
- System.out.println("AFTER_THROWING....");
- }
- public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
- System.out.println("調用過程出錯啦!!!!!");
- }
- }
[java] view plain copy print ?
![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- import org.springframework.aop.aspectj.AspectJAroundAdvice;
- /**
- *
- * AroundAdvice
- * Created by louis on 2016/4/15.
- */
- public class TicketServiceAroundAdvice implements MethodInterceptor {
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- System.out.println("AROUND_ADVICE:BEGIN....");
- Object returnValue = invocation.proceed();
- System.out.println("AROUND_ADVICE:END.....");
- return returnValue;
- }
- }
如今,咱們來手動使用ProxyFactoryBean來建立Proxy對象,並將相應的幾種不一樣的Advice加入這個proxy對應的各個執行階段中:spring
[java] view plain copy print ?
![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.aopalliance.aop.Advice;
- import org.springframework.aop.framework.ProxyFactoryBean;
- /**
- * 經過ProxyFactoryBean 手動建立 代理對象
- * Created by louis on 2016/4/14.
- */
- public class App {
- public static void main(String[] args) throws Exception {
- //1.針對不一樣的時期類型,提供不一樣的Advice
- Advice beforeAdvice = new TicketServiceBeforeAdvice();
- Advice afterReturningAdvice = new TicketServiceAfterReturningAdvice();
- Advice aroundAdvice = new TicketServiceAroundAdvice();
- Advice throwsAdvice = new TicketServiceThrowsAdvice();
- RailwayStation railwayStation = new RailwayStation();
- //2.建立ProxyFactoryBean,用以建立指定對象的Proxy對象
- ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
- //3.設置Proxy的接口
- proxyFactoryBean.setInterfaces(TicketService.class);
- //4. 設置RealSubject
- proxyFactoryBean.setTarget(railwayStation);
- //5.使用JDK基於接口實現機制的動態代理生成Proxy代理對象,若是想使用CGLIB,須要將這個flag設置成true
- proxyFactoryBean.setProxyTargetClass(true);
- //6. 添加不一樣的Advice
- proxyFactoryBean.addAdvice(afterReturningAdvice);
- proxyFactoryBean.addAdvice(aroundAdvice);
- proxyFactoryBean.addAdvice(throwsAdvice);
- proxyFactoryBean.addAdvice(beforeAdvice);
- proxyFactoryBean.setProxyTargetClass(false);
- //7經過ProxyFactoryBean生成Proxy對象
- TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
- ticketService.sellTicket();
- }
- }
不出意外的話,你會獲得以下的輸出結果:app
你會看到,咱們成功地建立了一個經過一個ProxyFactoryBean和 真實的實例對象建立出了對應的代理對象,並將各個Advice加入到proxy代理對象中。less
你會發現,在調用RailwayStation的sellticket()以前,成功插入了BeforeAdivce邏輯,而調用RailwayStation的sellticket()以後,AfterReturning邏輯也成功插入了。ide
AroundAdvice也成功包裹了sellTicket()方法,只不過這個AroundAdvice發生的時機有點讓人感到迷惑。實際上,這個背後的執行邏輯隱藏了Spring AOP關於AOP的關於Advice調度最爲核心的算法機制,這個將在本文後面詳細闡述。性能
另外,本例中ProxyFactoryBean是經過JDK的針對接口的動態代理模式生成代理對象的,具體機制,請看下面關於ProxyFactoryBean的介紹。fetch
上面咱們經過了純手動使用ProxyFactoryBean實現了AOP的功能。如今來分析一下上面的代碼:咱們爲ProxyFactoryBean提供了以下信息:
1). Proxy應該感興趣的Adivce列表;
2). 真正的實例對象引用ticketService;
3).告訴ProxyFactoryBean使用基於接口實現的JDK動態代理機制實現proxy:
4). Proxy應該具有的Interface接口:TicketService;
根據這些信息,ProxyFactoryBean就能給咱們提供咱們想要的Proxy對象了!那麼,ProxyFactoryBean幫咱們作了什麼?
Spring 使用工廠Bean模式建立每個Proxy,對應每個不一樣的Class類型,在Spring中都會有一個相對應的ProxyFactoryBean. 如下是ProxyFactoryBean的類圖。
如上所示,對於生成Proxy的工廠Bean而言,它要知道對其感興趣的Advice信息,而這類的信息,被維護到Advised中。Advised能夠根據特定的類名和方法名返回對應的AdviceChain,用以表示須要執行的Advice串。
JdkDynamicAopProxy類實現了AopProxy,可以返回Proxy,而且,其自身也實現了InvocationHandler角色。也就是說,當咱們使用proxy時,咱們對proxy對象調用的方法,都最終被轉到這個類的invoke()方法中。
[java] view plain copy print ?![]()
![]()
- final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
- //省略若干...
- /** Proxy的配置信息,這裏主要提供Advisor列表,並用於返回AdviceChain */
- private final AdvisedSupport advised;
- /**
- * Construct a new JdkDynamicAopProxy for the given AOP configuration.
- * @param config the AOP configuration as AdvisedSupport object
- * @throws AopConfigException if the config is invalid. We try to throw an informative
- * exception in this case, rather than let a mysterious failure happen later.
- */
- public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
- Assert.notNull(config, "AdvisedSupport must not be null");
- if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("No advisors and no TargetSource specified");
- }
- this.advised = config;
- }
- @Override
- public Object getProxy() {
- return getProxy(ClassUtils.getDefaultClassLoader());
- }
- //返回代理實例對象
- @Override
- 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);
- //這裏的InvocationHandler設置成了當前實例對象,即對這個proxy調用的任何方法,都會調用這個類的invoke()方法
- //這裏的invoke方法被調用,動態查找Advice列表,組成ReflectMethodInvocation
- return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
- }
- /**
- * 對當前proxy調用其上的任何方法,都將轉到這個方法上
- * Implementation of {@code InvocationHandler.invoke}.
- * <p>Callers will see exactly the exception thrown by the target,
- * unless a hook method throws an exception.
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- MethodInvocation invocation;
- Object oldProxy = null;
- boolean setProxyContext = false;
- TargetSource targetSource = this.advised.targetSource;
- Class<?> targetClass = null;
- Object target = null;
- try {
- if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
- // The target does not implement the equals(Object) method itself.
- return equals(args[0]);
- }
- if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
- // The target does not implement the hashCode() method itself.
- return hashCode();
- }
- if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
- method.getDeclaringClass().isAssignableFrom(Advised.class)) {
- // Service invocations on ProxyConfig with the proxy config...
- return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
- }
- Object retVal;
- if (this.advised.exposeProxy) {
- // Make invocation available if necessary.
- oldProxy = AopContext.setCurrentProxy(proxy);
- setProxyContext = true;
- }
- // May be null. Get as late as possible to minimize the time we "own" the target,
- // in case it comes from a pool.
- target = targetSource.getTarget();
- if (target != null) {
- targetClass = target.getClass();
- }
- // Get the interception chain for this method.獲取當前調用方法的攔截鏈
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(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.
- //若是沒有攔截鏈,則直接調用Joinpoint鏈接點的方法。
- 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.
- Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
- retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
- }
- else {
- // We need to create a method invocation...
- //根據給定的攔截鏈和方法調用信息,建立新的MethodInvocation對象,整個攔截鏈的工做邏輯都在這個ReflectiveMethodInvocation裏
- invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
- // Proceed to the joinpoint through the interceptor chain.
- retVal = invocation.proceed();
- }
- // Massage return value if necessary.
- Class<?> returnType = method.getReturnType();
- if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
- !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
- // Special case: it returned "this" and the return type of the method
- // is type-compatible. Note that we can't help if the target sets
- // a reference to itself in another returned object.
- 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()) {
- // Must have come from TargetSource.
- targetSource.releaseTarget(target);
- }
- if (setProxyContext) {
- // Restore old proxy.
- AopContext.setCurrentProxy(oldProxy);
- }
- }
- }
- }
基於Cglib子類繼承方式的動態代理CglibAopProxy生成代理對象:
[java] view plain copy print ?
![]()
![]()
- package org.springframework.aop.framework;
- /**
- * CGLIB-based {@link AopProxy} implementation for the Spring AOP framework.
- *
- * <p>Formerly named {@code Cglib2AopProxy}, as of Spring 3.2, this class depends on
- * Spring's own internally repackaged version of CGLIB 3.</i>.
- */
- @SuppressWarnings("serial")
- class CglibAopProxy implements AopProxy, Serializable {
- // Constants for CGLIB callback array indices
- private static final int AOP_PROXY = 0;
- private static final int INVOKE_TARGET = 1;
- private static final int NO_OVERRIDE = 2;
- private static final int DISPATCH_TARGET = 3;
- private static final int DISPATCH_ADVISED = 4;
- private static final int INVOKE_EQUALS = 5;
- private static final int INVOKE_HASHCODE = 6;
- /** Logger available to subclasses; static to optimize serialization */
- protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);
- /** Keeps track of the Classes that we have validated for final methods */
- private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<Class<?>, Boolean>();
- /** The configuration used to configure this proxy */
- protected final AdvisedSupport advised;
- protected Object[] constructorArgs;
- protected Class<?>[] constructorArgTypes;
- /** Dispatcher used for methods on Advised */
- private final transient AdvisedDispatcher advisedDispatcher;
- private transient Map<String, Integer> fixedInterceptorMap;
- private transient int fixedInterceptorOffset;
- /**
- * Create a new CglibAopProxy for the given AOP configuration.
- * @param config the AOP configuration as AdvisedSupport object
- * @throws AopConfigException if the config is invalid. We try to throw an informative
- * exception in this case, rather than let a mysterious failure happen later.
- */
- public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
- Assert.notNull(config, "AdvisedSupport must not be null");
- if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("No advisors and no TargetSource specified");
- }
- this.advised = config;
- this.advisedDispatcher = new AdvisedDispatcher(this.advised);
- }
- /**
- * Set constructor arguments to use for creating the proxy.
- * @param constructorArgs the constructor argument values
- * @param constructorArgTypes the constructor argument types
- */
- public void setConstructorArguments(Object[] constructorArgs, Class<?>[] constructorArgTypes) {
- if (constructorArgs == null || constructorArgTypes == null) {
- throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
- }
- if (constructorArgs.length != constructorArgTypes.length) {
- throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length +
- ") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")");
- }
- this.constructorArgs = constructorArgs;
- this.constructorArgTypes = constructorArgTypes;
- }
- @Override
- public Object getProxy() {
- return getProxy(null);
- }
- @Override
- public Object getProxy(ClassLoader classLoader) {
- if (logger.isDebugEnabled()) {
- logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
- }
- try {
- Class<?> rootClass = this.advised.getTargetClass();
- Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
- Class<?> proxySuperClass = rootClass;
- if (ClassUtils.isCglibProxyClass(rootClass)) {
- proxySuperClass = rootClass.getSuperclass();
- Class<?>[] additionalInterfaces = rootClass.getInterfaces();
- for (Class<?> additionalInterface : additionalInterfaces) {
- this.advised.addInterface(additionalInterface);
- }
- }
- // Validate the class, writing log messages as necessary.
- validateClassIfNecessary(proxySuperClass, classLoader);
- // Configure CGLIB Enhancer...
- Enhancer enhancer = createEnhancer();
- if (classLoader != null) {
- enhancer.setClassLoader(classLoader);
- if (classLoader instanceof SmartClassLoader &&
- ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
- enhancer.setUseCache(false);
- }
- }
- enhancer.setSuperclass(proxySuperClass);
- enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
- enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
- enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
- Callback[] callbacks = getCallbacks(rootClass);
- Class<?>[] types = new Class<?>[callbacks.length];
- for (int x = 0; x < types.length; x++) {
- types[x] = callbacks[x].getClass();
- }
- // fixedInterceptorMap only populated at this point, after getCallbacks call above
- enhancer.setCallbackFilter(new ProxyCallbackFilter(
- this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
- enhancer.setCallbackTypes(types);
- // Generate the proxy class and create a proxy instance.
- return createProxyClassAndInstance(enhancer, callbacks);
- }
- catch (CodeGenerationException ex) {
- throw new AopConfigException("Could not generate CGLIB subclass of class [" +
- this.advised.getTargetClass() + "]: " +
- "Common causes of this problem include using a final class or a non-visible class",
- ex);
- }
- catch (IllegalArgumentException ex) {
- throw new AopConfigException("Could not generate CGLIB subclass of class [" +
- this.advised.getTargetClass() + "]: " +
- "Common causes of this problem include using a final class or a non-visible class",
- ex);
- }
- catch (Exception ex) {
- // TargetSource.getTarget() failed
- throw new AopConfigException("Unexpected AOP exception", ex);
- }
- }
- protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
- enhancer.setInterceptDuringConstruction(false);
- enhancer.setCallbacks(callbacks);
- return (this.constructorArgs != null ?
- enhancer.create(this.constructorArgTypes, this.constructorArgs) :
- enhancer.create());
- }
- /**
- * Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
- * {@link Enhancer} implementation.
- */
- protected Enhancer createEnhancer() {
- return new Enhancer();
- }
- private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
- // Parameters used for optimisation choices...
- boolean exposeProxy = this.advised.isExposeProxy();
- boolean isFrozen = this.advised.isFrozen();
- boolean isStatic = this.advised.getTargetSource().isStatic();
- // Choose an "aop" interceptor (used for AOP calls).
- Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
- // Choose a "straight to target" interceptor. (used for calls that are
- // unadvised but can return this). May be required to expose the proxy.
- Callback targetInterceptor;
- if (exposeProxy) {
- targetInterceptor = isStatic ?
- new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
- new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
- }
- else {
- targetInterceptor = isStatic ?
- new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
- new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
- }
- // Choose a "direct to target" dispatcher (used for
- // unadvised calls to static targets that cannot return this).
- Callback targetDispatcher = isStatic ?
- new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
- Callback[] mainCallbacks = new Callback[] {
- aopInterceptor, // for normal advice
- targetInterceptor, // invoke target without considering advice, if optimized
- new SerializableNoOp(), // no override for methods mapped to this
- targetDispatcher, this.advisedDispatcher,
- new EqualsInterceptor(this.advised),
- new HashCodeInterceptor(this.advised)
- };
- Callback[] callbacks;
- // If the target is a static one and the advice chain is frozen,
- // then we can make some optimisations by sending the AOP calls
- // direct to the target using the fixed chain for that method.
- if (isStatic && isFrozen) {
- Method[] methods = rootClass.getMethods();
- Callback[] fixedCallbacks = new Callback[methods.length];
- this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);
- // TODO: small memory optimisation here (can skip creation for methods with no advice)
- for (int x = 0; x < methods.length; x++) {
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
- fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
- chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
- this.fixedInterceptorMap.put(methods[x].toString(), x);
- }
- // Now copy both the callbacks from mainCallbacks
- // and fixedCallbacks into the callbacks array.
- callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
- System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
- System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
- this.fixedInterceptorOffset = mainCallbacks.length;
- }
- else {
- callbacks = mainCallbacks;
- }
- return callbacks;
- }
- /**
- * General purpose AOP callback. Used when the target is dynamic or when the
- * proxy is not frozen.
- */
- private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
- private final AdvisedSupport advised;
- public DynamicAdvisedInterceptor(AdvisedSupport advised) {
- this.advised = advised;
- }
- @Override
- public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- Object oldProxy = null;
- boolean setProxyContext = false;
- Class<?> targetClass = null;
- Object target = null;
- try {
- if (this.advised.exposeProxy) {
- // Make invocation available if necessary.
- oldProxy = AopContext.setCurrentProxy(proxy);
- setProxyContext = true;
- }
- // May be null. Get as late as possible to minimize the time we
- // "own" the target, in case it comes from a pool...
- target = getTarget();
- if (target != null) {
- targetClass = target.getClass();
- }
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- Object retVal;
- // Check whether we only have one InvokerInterceptor: that is,
- // no real advice, but just reflective invocation of the target.
- if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
- // 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.
- Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
- retVal = methodProxy.invoke(target, argsToUse);
- }
- else {
- // We need to create a method invocation...
- retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
- }
- retVal = processReturnType(proxy, target, method, retVal);
- return retVal;
- }
- finally {
- if (target != null) {
- releaseTarget(target);
- }
- if (setProxyContext) {
- // Restore old proxy.
- AopContext.setCurrentProxy(oldProxy);
- }
- }
- }
- //省略...
- }
- /**
- * Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
- */
- private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
- private final MethodProxy methodProxy;
- private final boolean publicMethod;
- public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
- Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
- super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
- this.methodProxy = methodProxy;
- this.publicMethod = Modifier.isPublic(method.getModifiers());
- }
- /**
- * Gives a marginal performance improvement versus using reflection to
- * invoke the target when invoking public methods.
- */
- @Override
- protected Object invokeJoinpoint() throws Throwable {
- if (this.publicMethod) {
- return this.methodProxy.invoke(this.target, this.arguments);
- }
- else {
- return super.invokeJoinpoint();
- }
- }
- }
- }
JdkDynamicAopProxy 和CglibAopProxy只是建立代理方式的兩種方式而已,實際上咱們爲方法調用添加的各類Advice的執行邏輯都是統一的。在Spring的底層,會把咱們定義的各個Adivce分別 包裹成一個 MethodInterceptor,這些Advice按照加入Advised順序,構成一個AdivseChain.
好比咱們上述的代碼:
[java] view plain copy print ?
![]()
![]()
- //5. 添加不一樣的Advice
- proxyFactoryBean.addAdvice(afterReturningAdvice);
- proxyFactoryBean.addAdvice(aroundAdvice);
- proxyFactoryBean.addAdvice(throwsAdvice);
- proxyFactoryBean.addAdvice(beforeAdvice);
- proxyFactoryBean.setProxyTargetClass(false);
- //經過ProxyFactoryBean生成
- TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
- ticketService.sellTicket();
當咱們調用 ticketService.sellTicket()時,Spring會把這個方法調用轉換成一個MethodInvocation對象,而後結合上述的咱們添加的各類Advice,組成一個ReflectiveMethodInvocation:
各類Advice本質而言是一個方法調用攔截器,如今讓咱們看看各個Advice攔截器都幹了什麼?
[java] view plain copy print ?
![]()
![]()
- /**
- * 包裹MethodBeforeAdvice的方法攔截器
- * Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}.
- * Used internally by the AOP framework; application developers should not need
- * to use this class directly.
- *
- * @author Rod Johnson
- */
- @SuppressWarnings("serial")
- public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
- private MethodBeforeAdvice advice;
- /**
- * Create a new MethodBeforeAdviceInterceptor for the given advice.
- * @param advice the MethodBeforeAdvice to wrap
- */
- public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
- Assert.notNull(advice, "Advice must not be null");
- this.advice = advice;
- }
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- //在調用方法以前,先執行BeforeAdvice
- this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
- return mi.proceed();
- }
- }
[java] view plain copy print ?![]()
![]()
- /**
- * 包裹AfterReturningAdvice的方法攔截器
- * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
- * Used internally by the AOP framework; application developers should not need
- * to use this class directly.
- *
- * @author Rod Johnson
- */
- @SuppressWarnings("serial")
- public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
- private final AfterReturningAdvice advice;
- /**
- * 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;
- }
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- //先調用invocation
- Object retVal = mi.proceed();
- //調用成功後,調用AfterReturningAdvice
- this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
- return retVal;
- }
- }
[java] view plain copy print ?![]()
![]()
- /**
- * 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;
- Method[] methods = throwsAdvice.getClass().getMethods();
- for (Method method : methods) {
- //ThrowsAdvice定義的afterThrowing方法是Handler方法
- 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 != 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;
- }
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- //使用大的try,先執行代碼,捕獲異常
- try {
- return mi.proceed();
- }
- catch (Throwable ex) {
- //獲取異常處理方法
- Method handlerMethod = getExceptionHandler(ex);
- //調用異常處理方法
- if (handlerMethod != null) {
- invokeHandlerMethod(mi, ex, handlerMethod);
- }
- throw ex;
- }
- }
- 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();
- }
- }
- }
關於AroundAdivce,其自己就是一個MethodInterceptor,因此不須要額外作轉換了。
細心的你會發現,在攔截器串中,每一個攔截器最後都會調用MethodInvocation的proceed()方法。若是按照簡單的攔截器的執行串來執行的話,MethodInvocation的proceed()方法至少要執行N次(N表示攔截器Interceptor的個數),由於每一個攔截器都會調用一次proceed()方法。更直觀地講,好比咱們調用了ticketService.sellTicket()方法,那麼,按照這個邏輯,咱們會打印出四條記錄:
[plain] view plain copy print ?
![]()
![]()
這樣咱們確定不是咱們須要的結果!!!!由於按照咱們的理解,只應該有一條"售piao............"纔對。真實的Spring的方法調用過程可以控制這個邏輯按照咱們的思路執行,Spring將這個整個方法調用過程連同若干個Advice組成的攔截器鏈組合成ReflectiveMethodInvocation對象,讓咱們來看看這一執行邏輯是怎麼控制的:
- 售piao............
- 售piao............
- 售piao............
- 售piao............
[java] view plain copy print ?
![]()
![]()
- public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
- protected final Object proxy;
- protected final Object target;
- protected final Method method;
- protected Object[] arguments;
- private final Class<?> targetClass;
- /**
- * Lazily initialized map of user-specific attributes for this invocation.
- */
- private Map<String, Object> userAttributes;
- /**
- * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
- * that need dynamic checks.
- */
- protected final List<?> interceptorsAndDynamicMethodMatchers;
- /**
- * Index from 0 of the current interceptor we're invoking.
- * -1 until we invoke: then the current interceptor.
- */
- private int currentInterceptorIndex = -1;
- /**
- * Construct a new ReflectiveMethodInvocation with the given arguments.
- * @param proxy the proxy object that the invocation was made on
- * @param target the target object to invoke
- * @param method the method to invoke
- * @param arguments the arguments to invoke the method with
- * @param targetClass the target class, for MethodMatcher invocations
- * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
- * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
- * MethodMatchers included in this struct must already have been found to have matched
- * as far as was possibly statically. Passing an array might be about 10% faster,
- * but would complicate the code. And it would work only for static pointcuts.
- */
- protected ReflectiveMethodInvocation(
- Object proxy, Object target, Method method, Object[] arguments,
- Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
- this.proxy = proxy;//proxy對象
- this.target = target;//真實的realSubject對象
- this.targetClass = targetClass;//被代理的類類型
- this.method = BridgeMethodResolver.findBridgedMethod(method);//方法引用
- this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);//調用參數
- this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;//Advice攔截器鏈
- }
- @Override
- public final Object getProxy() {
- return this.proxy;
- }
- @Override
- public final Object getThis() {
- return this.target;
- }
- @Override
- public final AccessibleObject getStaticPart() {
- return this.method;
- }
- /**
- * Return the method invoked on the proxied interface.
- * May or may not correspond with a method invoked on an underlying
- * implementation of that interface.
- */
- @Override
- public final Method getMethod() {
- return this.method;
- }
- @Override
- public final Object[] getArguments() {
- return (this.arguments != null ? this.arguments : new Object[0]);
- }
- @Override
- public void setArguments(Object... arguments) {
- this.arguments = arguments;
- }
- @Override
- public Object proceed() throws Throwable {
- // 沒有攔截器,則直接調用Joinpoint上的method,即直接調用MethodInvocation We start with an index of -1 and increment early.
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
- return invokeJoinpoint();
- }
- // 取得第攔截器鏈上第N個攔截器
- Object interceptorOrInterceptionAdvice =
- this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
- //PointcutInterceptor會走這個邏輯
- if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
- // Evaluate dynamic method matcher here: static part will already have
- // been evaluated and found to match.
- InterceptorAndDynamicMethodMatcher dm =
- (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
- //當前攔截器是符合攔截規則,每一個攔截器能夠定義是否特定的類和方法名是否符合攔截規則
- //實際上PointCut定義的方法簽名最後會轉換成這個MethodMatcher,並置於攔截器中
- if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
- //符合攔截規則,調用攔截器invoke()
- 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.
- //直接調用攔截器,
- return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
- }
- }
- /**
- * Invoke the joinpoint using reflection.
- * Subclasses can override this to use custom invocation.
- * @return the return value of the joinpoint
- * @throws Throwable if invoking the joinpoint resulted in an exception
- */
- protected Object invokeJoinpoint() throws Throwable {
- return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
- }
上述的代碼比較冗雜,解釋起來比較繁瑣,請看下面一張圖,你就知道這段代碼的思路了:
實例分析
根據上面的執行鏈上的邏輯,咱們將咱們上面舉的例子的輸出結果在整理一下:
Advice攔截器的添加順序:
[java] view plain copy print ?
![]()
![]()
第一個攔截器:AfterReturningAdvice
- proxyFactoryBean.addAdvice(afterReturningAdvice);
- proxyFactoryBean.addAdvice(aroundAdvice);
- proxyFactoryBean.addAdvice(throwsAdvice);
- proxyFactoryBean.addAdvice(beforeAdvice);
第一個添加的是afterReturningAdivce,它所處的位置是第一個攔截器,執行的操做就是:
[java] view plain copy print ?
![]()
![]()
也就是說,先完成MethodInvocation的proceed()方法再執行相應的advice;而調用了mi.proceed()方法,致使了當前的調用鏈後移,進行和後續的操做,也就是說,AfterReturningAdvice只能等到整個攔截器鏈上全部執行完畢後纔會生效,因此:AFTER_RETURNING:本次服務已結束.... 這句話排在了最後:
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- Object retVal = mi.proceed();
- this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
- return retVal;
- }
第二個攔截器:AroundAdvice
[java] view plain copy print ?
![]()
![]()
如今執行到了第二個攔截器,首先輸出了"AROUND_ADVICE:BEGIN......",接着調用Invocation.proceed(),等到剩餘的執行完後,再輸出"AROUND_ADVICE:END.....":
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- System.out.println("AROUND_ADVICE:BEGIN....");
- Object returnValue = invocation.proceed();
- System.out.println("AROUND_ADVICE:END.....");
- return returnValue;
- }
第三個攔截器:ThrowsAdvice:
ThrowsAdvice攔截器的處理模式是:
[java] view plain copy print ?
![]()
![]()
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- try {
- //先執行invocation.proceed();
- return mi.proceed();
- }
- catch (Throwable ex) {
- //捕捉錯誤,調用afterThrowing()方法
- Method handlerMethod = getExceptionHandler(ex);
- if (handlerMethod != null) {
- invokeHandlerMethod(mi, ex, handlerMethod);
- }
- throw ex;
- }
- }
上述的邏輯是,先執行Invocation.proceed();若是這個過程當中拋出異常,則調用ThrowsAdvice。
第四個攔截器:BeforeAdvice:
這個攔截器的工做邏輯以下:
[java] view plain copy print ?
![]()
![]()
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );//先執行Advice
- return mi.proceed();//後執行Invocation
- }
![]()
綜上全部的攔截器過程,咱們就能理解,爲何咱們剛開始的輸出爲何是下面這樣了:
上面咱們提供了幾個Adivce,你會發現,這些Advice是無條件地加入了咱們建立的對象中。不管調用Target的任何方法,這些Advice都會被觸發到。
那麼,咱們能否告訴Advice,只讓它對特定的方法或特定類起做用呢? 這個其實是要求咱們添加一個過濾器,若是知足條件,則Advice生效,不然無效。Spring將這個過濾器抽象成以下的接口:
[java] view plain copy print ?
![]()
![]()
- public interface MethodMatcher {
- /**
- * 提供方法簽名和所屬的Class類型,判斷是否支持
- * Perform static checking whether the given method matches. If this
- * returns {@code false} or if the {@link #isRuntime()} method
- * returns {@code false}, no runtime check (i.e. no.
- * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made.
- * @param method the candidate method
- * @param targetClass the target class (may be {@code null}, in which case
- * the candidate class must be taken to be the method's declaring class)
- * @return whether or not this method matches statically
- */
- boolean matches(Method method, Class<?> targetClass);
- /**
- * Is this MethodMatcher dynamic, that is, must a final call be made on the
- * {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
- * runtime even if the 2-arg matches method returns {@code true}?
- * <p>Can be invoked when an AOP proxy is created, and need not be invoked
- * again before each method invocation,
- * @return whether or not a runtime match via the 3-arg
- * {@link #matches(java.lang.reflect.Method, Class, Object[])} method
- * is required if static matching passed
- */
- boolean isRuntime();
- /**
- * Check whether there a runtime (dynamic) match for this method,
- * which must have matched statically.
- * <p>This method is invoked only if the 2-arg matches method returns
- * {@code true} for the given method and target class, and if the
- * {@link #isRuntime()} method returns {@code true}. Invoked
- * immediately before potential running of the advice, after any
- * advice earlier in the advice chain has run.
- * @param method the candidate method
- * @param targetClass the target class (may be {@code null}, in which case
- * the candidate class must be taken to be the method's declaring class)
- * @param args arguments to the method
- * @return whether there's a runtime match
- * @see MethodMatcher#matches(Method, Class)
- */
- boolean matches(Method method, Class<?> targetClass, Object... args);
- /**
- * Canonical instance that matches all methods.
- */
- MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
- }
將這個匹配器MethodMatcher和攔截器Interceptor 結合到一塊兒,就構成了一個新的類InterceptorAndDynamicMethodMatcher :
[java] view plain copy print ?
![]()
![]()
咱們再將上述的包含整個攔截器執行鏈邏輯的ReflectiveMethodInvocation實現的核心代碼在過一遍:
- /**
- * Internal framework class, combining a MethodInterceptor instance
- * with a MethodMatcher for use as an element in the advisor chain.
- *
- * @author Rod Johnson
- */
- class InterceptorAndDynamicMethodMatcher {
- final MethodInterceptor interceptor;
- final MethodMatcher methodMatcher;
- public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
- this.interceptor = interceptor;
- this.methodMatcher = methodMatcher;
- }
- }
[java] view plain copy print ?
![]()
![]()
- @Override
- public Object proceed() throws Throwable {
- // We start with an index of -1 and increment early.
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
- return invokeJoinpoint();
- }
- Object interceptorOrInterceptionAdvice =
- this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
- //起到必定的過濾做用,若是不匹配,則直接skip
- if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
- // Evaluate dynamic method matcher here: static part will already have
- // been evaluated and found to match.
- InterceptorAndDynamicMethodMatcher dm =
- (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
- //知足匹配規則,則攔截器Advice生效
- 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.
- //攔截器還沒有生效,直接skip
- return proceed();
- }
- }
- else {
- // It's an interceptor, so we just invoke it: The pointcut will have
- // been evaluated statically before this object was constructed.
- return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
- }
- }
實戰:
咱們如今實現一個PointcutAdisor,PointcutAdvisor表示擁有某個Pointcut的Advisor。
[java] view plain copy print ?
![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.aopalliance.aop.Advice;
- import org.springframework.aop.Pointcut;
- import org.springframework.aop.PointcutAdvisor;
- /**
- * 實現一個PointcutAdvisor,經過提供的Pointcut,對Advice的執行進行過濾
- * Created by louis on 2016/4/16.
- */
- public class FilteredAdvisor implements PointcutAdvisor {
- private Pointcut pointcut;
- private Advice advice;
- public FilteredAdvisor(Pointcut pointcut, Advice advice) {
- this.pointcut = pointcut;
- this.advice = advice;
- }
- /**
- * Get the Pointcut that drives this advisor.
- */
- @Override
- public Pointcut getPointcut() {
- return pointcut;
- }
- @Override
- public Advice getAdvice() {
- return advice;
- }
- @Override
- public boolean isPerInstance() {
- return false;
- }
- }
[java] view plain copy print ?![]()
![]()
- package org.luanlouis.meditations.thinkinginspring.aop;
- import org.aopalliance.aop.Advice;
- import org.springframework.aop.aspectj.AspectJExpressionPointcut;
- import org.springframework.aop.framework.ProxyFactoryBean;
- /**
- * 經過ProxyFactoryBean 手動建立 代理對象
- * Created by louis on 2016/4/14.
- */
- public class App {
- public static void main(String[] args) throws Exception {
- //1.針對不一樣的時期類型,提供不一樣的Advice
- Advice beforeAdvice = new TicketServiceBeforeAdvice();
- Advice afterReturningAdvice = new TicketServiceAfterReturningAdvice();
- Advice aroundAdvice = new TicketServiceAroundAdvice();
- Advice throwsAdvice = new TicketServiceThrowsAdvice();
- RailwayStation railwayStation = new RailwayStation();
- //2.建立ProxyFactoryBean,用以建立指定對象的Proxy對象
- ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
- //3.設置Proxy的接口
- proxyFactoryBean.setInterfaces(TicketService.class);
- //4. 設置RealSubject
- proxyFactoryBean.setTarget(railwayStation);
- //5.使用JDK基於接口實現機制的動態代理生成Proxy代理對象,若是想使用CGLIB,須要將這個flag設置成true
- proxyFactoryBean.setProxyTargetClass(true);
- //5. 添加不一樣的Advice
- proxyFactoryBean.addAdvice(afterReturningAdvice);
- proxyFactoryBean.addAdvice(aroundAdvice);
- proxyFactoryBean.addAdvice(throwsAdvice);
- //proxyFactoryBean.addAdvice(beforeAdvice);
- proxyFactoryBean.setProxyTargetClass(false);
- //手動建立一個pointcut,專門攔截sellTicket方法
- AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
- pointcut.setExpression("execution( * sellTicket(..))");
- //傳入建立的beforeAdvice和pointcut
- FilteredAdvisor sellBeforeAdvior = new FilteredAdvisor(pointcut,beforeAdvice);
- //添加到FactoryBean中
- proxyFactoryBean.addAdvisor(sellBeforeAdvior);
- //經過ProxyFactoryBean生成
- TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
- ticketService.sellTicket();
- System.out.println("---------------------------");
- ticketService.inquire();
- }
- }
這個時候,你會看到輸出:
從結果中你能夠清晰地看到,咱們能夠對某一個Advisor(即Advice)添加一個pointcut限制,這樣就能夠針對指定的方法執行Advice了!本例中使用了PointcutAdvisor,實際上,帶底層代碼中,Spring會將PointcutAdvisor轉換成InterceptorAndDynamicMethodMatcher 參與ReflectiveMethodInvocation關於攔截器鏈的執行邏輯:
[java] view plain copy print ?
![]()
![]()
- public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
- @Override
- 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);
- Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
- boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
- AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
- for (Advisor advisor : config.getAdvisors()) {
- //PointcutAdvisor向 InterceptorAndDynamicMethodMatcher 的轉換
- if (advisor instanceof PointcutAdvisor) {
- // Add it conditionally.
- PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
- if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
- MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
- MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
- if (MethodMatchers.matches(mm, method, actualClass, 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(actualClass)) {
- Interceptor[] interceptors = registry.getInterceptors(advisor);
- interceptorList.addAll(Arrays.asList(interceptors));
- }
- }
- else {
- Interceptor[] interceptors = registry.getInterceptors(advisor);
- interceptorList.addAll(Arrays.asList(interceptors));
- }
- }
- return interceptorList;
- }
- /**
- * Determine whether the Advisors contain matching introductions.
- */
- private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
- for (int i = 0; i < config.getAdvisors().length; i++) {
- Advisor advisor = config.getAdvisors()[i];
- if (advisor instanceof IntroductionAdvisor) {
- IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
- if (ia.getClassFilter().matches(actualClass)) {
- return true;
- }
- }
- }
- return false;
- }
- }
至此,你已經瞭解了Spring的AOP的精髓,以及Spring的整個工做機制。我我的認爲,想要理解Spring AOP,你須要從ProxyFactoryBean 開始,逐步地分析整個代理的構建過程:
1. 代理對象是怎麼生成的(JDK or Cglib)
2. Advice鏈(即攔截器鏈)的構造過程以及執行機制
3. 如何在Advice上添加pointcut,而且這個pointcut是如何工做的(實際上起到的過濾做用)
最後再講一下性能問題,如上面描述的,Spring建立Proxy的過程邏輯雖然很清晰,可是你也看到,對於咱們每個方法調用,都會通過很是複雜的層層Advice攔截判斷,是否須要攔截處理,這個開銷是很是大的。記得Spring的documentation介紹,若是使用Spring的AOP,對項目而言會形成10%的性能消耗,So,用AOP以前要仔細考慮一下性能問題~~~~~
做者的話
本文使用的源碼,已經託管到Github上,讀者能夠自行clone查看測驗~
源碼地址:https://github.com/LuanLouis/thinking-in-spring.git ,請fetch branch : aop-principle 分支~
本文關於Spring AOP的設計原理僅是本人我的的看法和見解,若有任何疑問和錯誤,請不吝指出,敬請賜教,共同進步!