從JVM看aop
1. Java程序運行在JVM中的特征
當我們在某個類Foo中寫好了一個main()方法,然后執(zhí)行java Foo,你的Java程序之旅就開啟了,如下:
public class Foo {
public static void main(String[] args) {
// your codes begins here
}
}
那么在這個執(zhí)行的過程中,JVM都為你干了什么呢?
當你執(zhí)行java Foo 的時候,JVM會創(chuàng)建一個主線程main,這個主線程以上述的main()方法作為入口,開始執(zhí)行你的代碼。每一個線程在內(nèi)存中都會維護一個屬于自己的棧(Stack),記錄著整個程序執(zhí)行的過程。棧里的每一個元素稱為棧幀(Stack Frame),棧幀表示著某個方法調(diào)用,會記錄方法調(diào)用的信息;實際上我們在代碼中調(diào)用一個方法的時候,在內(nèi)存中就對應(yīng)著一個棧幀的入棧和出棧。
在某個特定的時間點,一個Main線程內(nèi)的棧會呈現(xiàn)如下圖所示的情況:

2. Java程序執(zhí)行流 【了解AOP、連接點(Join Point)、切入點(point cut) 的概念 】
如果從虛擬機線程棧的角度考慮Java程序執(zhí)行的話,那么,你會發(fā)現(xiàn),真?zhèn)€程序運行的過程就是方法調(diào)用的過程。我們按照方法執(zhí)行的順序,將方法調(diào)用排成一串,這樣就構(gòu)成了Java程序流。
我們將上述的線程棧里的方法調(diào)用按照執(zhí)行流排列,會有如下類似的圖:

基于時間序列,我們可以將方法調(diào)用排成一條線。而每個方法調(diào)用則可以看成Java執(zhí)行流中的一個節(jié)點。這個節(jié)點在AOP的術(shù)語中,被稱為Join Point,即連接點。 一個Java程序的運行的過程,就是若干個連接點連接起來依次執(zhí)行的過程。
在我們正常的面向?qū)ο蟮乃季S中, 我們考慮的是如何按照時間序列通過方法調(diào)用來實現(xiàn)我們的業(yè)務(wù)邏輯。那么,什么是AOP(即面向切面的編程)呢?
通常面向?qū)ο蟮某绦?,代碼都是按照時間序列縱向展開的,而他們都有一個共性:即都是已方法調(diào)用作為基本執(zhí)行單位展開的。 將方法調(diào)用當做一個連接點,那么由連接點串起來的程序執(zhí)行流就是整個程序的執(zhí)行過程。
AOP(Aspect Oriented Programming)則是從另外一個角度來考慮整個程序的,AOP將每一個方法調(diào)用,即連接點作為編程的入口,針對方法調(diào)用進行編程。從執(zhí)行的邏輯上來看,相當于在之前縱向的按照時間軸執(zhí)行的程序橫向切入。相當于將之前的程序橫向切割成若干的面,即Aspect.每個面被稱為切面。
所以,根據(jù)我的理解,AOP本質(zhì)上是針對方法調(diào)用的編程思路。

既然AOP是針對切面進行的編程的,那么,你需要選擇哪些切面(即 連接點Joint Point)作為你的編程對象呢?
因為切面本質(zhì)上是每一個方法調(diào)用,選擇切面的過程實際上就是選擇方法的過程。那么,被選擇的切面(Aspect)在AOP術(shù)語里被稱為切入點(Point Cut). 切入點實際上也是從所有的連接點(Join point)挑選自己感興趣的連接點的過程。

既然AOP是針對方法調(diào)用(連接點)的編程, 現(xiàn)在又選取了你感興趣的自己感興趣的鏈接點---切入點(Point Cut)了,那么,AOP能對它做什么類型的編程呢?AOP能做什么呢?
了解這個之前,我們先要知道一個非常重要的問題: 既然AOP是對方法調(diào)用進行的編程,那么,AOP如何捕獲方法調(diào)用的呢? 弄清楚這個問題,你不得不了解設(shè)計模式中的代理模式了。下面我們先來了解一下引入了代理模式的Java程序執(zhí)行流是什么樣子的。
3. 引入了代理模式的Java程序執(zhí)行流(AOP實現(xiàn)的機制)
我們假設(shè)在我們的Java代碼里,都為實例對象通過代理模式創(chuàng)建了代理對象,訪問這些實例對象必須要通過代理,那么,加入了proxy對象的Java程序執(zhí)行流會變得稍微復(fù)雜起來。
我們來看下加入了proxy對象后,Java程序執(zhí)行流的示意圖:

加入了代理模式的Java程序執(zhí)行流,使得所有的方法調(diào)用都經(jīng)過了代理對象。對于Spring AOP框架而言,它負責(zé)控制著真?zhèn)€容器內(nèi)部的代理對象。當我們調(diào)用了某一個實例對象的任何一個非final的public方法時,整個Spring框架都會知曉。
此時的SpringAOP框架在某種程度上扮演著一個上帝的角色:它知道你在這個框架內(nèi)所做的任何操作,你對每一個實例對象的非final的public方法調(diào)用都可以被框架察覺到!

4. Spring AOP的工作原理
前面已經(jīng)介紹了AOP編程首先要選擇它感興趣的連接點----即切入點(Point cut),那么,AOP能對切入點做什么樣的編程呢? 我們先將代理模式下的某個連接點細化,你會看到如下這個示意圖所表示的過程:



5、Spring AOP的核心---ProxyFactoryBean
ProxyFactoryBean提供了如下信息:
1). Proxy應(yīng)該感興趣的Adivce列表;
2). 真正的實例對象引用ticketService;
3).告訴ProxyFactoryBean使用基于接口實現(xiàn)的JDK動態(tài)代理機制實現(xiàn)proxy:
4). Proxy應(yīng)該具備的Interface接口:TicketService;
根據(jù)這些信息,ProxyFactoryBean就能給我們提供我們想要的Proxy對象了!那么,ProxyFactoryBean幫我們做了什么?

Spring 使用工廠Bean模式創(chuàng)建每一個Proxy,對應(yīng)每一個不同的Class類型,在Spring中都會有一個相對應(yīng)的ProxyFactoryBean. 以下是ProxyFactoryBean的類圖。

如上所示,對于生成Proxy的工廠Bean而言,它要知道對其感興趣的Advice信息,而這類的信息,被維護到Advised中。Advised可以根據(jù)特定的類名和方法名返回對應(yīng)的AdviceChain,用以表示需要執(zhí)行的Advice串。
6、基于JDK面向接口的動態(tài)代理JdkDynamicAopProxy生成代理對象
JdkDynamicAopProxy類實現(xiàn)了AopProxy,能夠返回Proxy,并且,其自身也實現(xiàn)了InvocationHandler角色。也就是說,當我們使用proxy時,我們對proxy對象調(diào)用的方法,都最終被轉(zhuǎn)到這個類的invoke()方法中。
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設(shè)置成了當前實例對象,即對這個proxy調(diào)用的任何方法,都會調(diào)用這個類的invoke()方法
//這里的invoke方法被調(diào)用,動態(tài)查找Advice列表,組成ReflectMethodInvocation
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
/**
* 對當前proxy調(diào)用其上的任何方法,都將轉(zhuǎn)到這個方法上
* 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.獲取當前調(diào)用方法的攔截鏈
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.
//如果沒有攔截鏈,則直接調(diào)用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...
//根據(jù)給定的攔截鏈和方法調(diào)用信息,創(chuàng)建新的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);
}
}
}
}
7、基于Cglib子類繼承方式的動態(tài)代理CglibAopProxy生成代理對象
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();
}
}
}
}
8、各種Advice是的執(zhí)行順序是如何和方法調(diào)用進行結(jié)合的?
JdkDynamicAopProxy 和CglibAopProxy只是創(chuàng)建代理方式的兩種方式而已,實際上我們?yōu)榉椒ㄕ{(diào)用添加的各種Advice的執(zhí)行邏輯都是統(tǒng)一的。在Spring的底層,會把我們定義的各個Adivce分別 包裹成一個 MethodInterceptor,這些Advice按照加入Advised順序,構(gòu)成一個AdivseChain.
比如我們下面的代碼:
//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();
當我們調(diào)用 ticketService.sellTicket()時,Spring會把這個方法調(diào)用轉(zhuǎn)換成一個MethodInvocation對象,然后結(jié)合上述的我們添加的各種Advice,組成一個ReflectiveMethodInvocation:

各種Advice本質(zhì)而言是一個方法調(diào)用攔截器,現(xiàn)在讓我們看看各個Advice攔截器都干了什么?

-
攔截原理
