preface
The ritual sense of the preface still needs to be there. The main reason for coming back to Spring AOP is that I wrote a conceptual article about AOP before, and then ended the article with a “next source code” analysis. But then he went off to write something else. Er…
In fact, the reason why I am so paranoid about writing Spring AOP is that, after all, it is the focus of the framework, and the abstract code and ideas inside cannot be lost; Besides, we simple programmers, if we can’t actually see the code implementation, how can we stop!
The main purpose of this article is to fully explain how AOP In Spring is implemented. So the order of the whole article is:
- The implementation principle of AOP
- AOP specific source code analysis
- Write a simplified version of AOP to deepen your understanding
Let ‘Go!
Realize the principle of
About the principle, the last article Spring AOP detailed explanation and basic usage ② I have said a little. But for now, I’d like to add.
Spring AOP is implemented in two ways: AspectJ and Spring AOP. The difference is
Spring AOP | AspectJ |
---|---|
Only implemented in Java | Extended implementation using the Java programming language |
No separate compilation process is required | Use the AspectJ-specific compiler unless LTW is set |
Only run-time weaving is available | Runtime weaving is not available. Support compile time, compile time and load time weaving |
Method level weaving | More powerful – includes fields, methods, constructors, static initializers final classes/methods, and more |
Rely on the IOC container to manage the implementation | You can apply all objects |
Only method execution pointcuts are supported | All method cuts |
Proxies are created by the target object, and these proxies apply facets | Extend at the code level, that is, generate proxy classes before runtime |
Much slower than AspectJ | Higher performance |
Easy to learn and easy to use | More complicated |
The source code parsing
version
The source code analysis is based on the Spring.5.1.2.release.
parsing
As we all know, the core element of Spring is IOC. So it reserves a lot of extended abstract classes and interfaces. Let’s take a normal Spring flowchart for the bean-managed life cycle:
We can see that by implementing the methods of the abstract class above, we can flexibly configure and change the Bean through the cycle of different phases of the Bean through the methods.
So how does this lifecycle help AOP? Isn’t it reasonable to think that the principle of AOP is dynamic proxying, and that the best time to proxy a Bean is at the appropriate time of its life? Here’s a quick overview of how Spring starts to trigger aOP-related processors:
SpringApplication.run()
Start the application.- Methods are called in the run method
refreshContext()
Context preparation and data refreshes refreshContext
The refresh method that asks up and down the Context is calledrefresh
There’s a lot of work in there, but we just need to focusfinishBeanFactoryInitialization()
Methods.finishBeanFactoryInitialization
Will be calledpreInstantiateSingletons
methodspreInstantiateSingletons
Method will callAbstractBeanFactory
的getBean
methodsgetBean
Continue to callAbstractBeanFactory
的doGetBean
methodsdoGetbean
Will continue to callAbstractBeanFactory
的getSingleton
->getObject
->doGetBean
, and finally enter the lambda cycle- The loop is called during the process
AbstractAutowireCapableBeanFactory#createBean
Methods. createBean
That’s when it gets calledresolveBeforeInstantiation
Methods. This method is here and nowGives BeanPostProcessors an opportunity to generate a proxy class instead of the original target class, the exact method that will be called ispostProcessBeforeInstantiation
和postProcessAfterInitialization
.applyBeanPostProcessorsBeforeInstantiation
Method by getting all of the current contextBeanPostProcessors
Loop through, and then call itpostProcessBeforeInstantiation
和postProcessAfterInitialization
.
Ok! Although we omit the process, we have now come to an important step. At this point, it’s time to actually scan the generated beans. There is a call in the BeanPostProcessors AnnotationAwareAspectJAutoProxyCreator classes. It implements the InstantiationAwareBeanPostProcessor (belong to BeanPostProcessor subclass, the main function is before instantiated attribute is set to automatic assembly). AnnotationAwareAspectJAutoProxyCreator also instantiate AbstractAutoProxyCreator (belong to BeanPostProcessor can expand the AOP proxy abstract class). Let’s take a look at its postProcessBeforeInstantiation method
It is important to note that because of the Spring Boot loading context, the loaded class does not match the source code parsing, so it may be in a different context
public Object postProcessBeforeInstantiation(Class
beanClass, String beanName) {
// Get the bean's already cached name
Object cacheKey = getCacheKey(beanClass, beanName);
// Check whether the load has been loaded according to the name
if(! StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
/ / check whether about AOP related classes | | determine whether need to skip loading
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null; }}/ / get TargetSource
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if(targetSource ! =null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
Copy the code
The steps of the code above are
- through
isInfrastructureClass
Judge whether or notAOP
Related to the class - through
shouldSkip
To determine whether to skip thisBean
The agent of - If all the above pass, then pass
getAdvicesAndAdvisorsForBean
Methods To obtain the corresponding section information - The proxy class is generated from the aspect information and then returned to facilitate the replacement of the original object class
As a sidebar, the TargetSource in the above code is actually a Holder that holds the object. The advantage of this is that the real proxy is not the actual object but the TargetSource, so we can replace the object held by the TargetSource without having to regenerate the proxy.
So let’s first look at how the shouldSkip method determines if we need to generate a proxy class; And then see how getAdvicesAndAdvisorsForBean resolution to find the corresponding information of section; Finally, see how createProxy generates the proxy class in Spring.
shouldSkip
protected boolean shouldSkip(Class
beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor) {
if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
return true; }}}return super.shouldSkip(beanClass, beanName);
}
Copy the code
The above code is mainly through the findCandidateAdvisors method to obtain the names of all aspects, and the loop comparison is whether the same; Return true if the same. And is the method of adjustable BeanFactoryAdvisorRetrievalHelper main findCandidateAdvisors method. Note that the current class is a global singleton. Its purpose is to find all appropriate Advisors in the current beanFactory, but not FactoryBeans.
public List<Advisor> findAdvisorBeans(a) {
// Store the name of the advisors
String[] advisorNames = null;
synchronized (this) {
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// No FactoryBeans are initialized here, just normal, unloaded classes
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true.false);
this.cachedAdvisorBeanNames = advisorNames; }}if (advisorNames.length == 0) {
return new LinkedList<Advisor>();
}
List<Advisor> advisors = new LinkedList<Advisor>();
// Loop the name of adivors
for (String name : advisorNames) {
if (isEligibleBean(name)) {
// If you want to check whether the creation is excluded or is being created
if (this.beanFactory.isCurrentlyInCreation(name)) {
// log info
}
else {
/ / add
try {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
// throw Exception}}}}return advisors;
}
Copy the code
The above is mainly to find the Advisor class from the BeanFactory and make a judgment.
The beans returned by the findCandidateAdvisors() method can be cached, which helps speed up retrieval
getAdvicesAndAdvisorsForBean
In AbstractAutoProxyCreator getAdvicesAndAdvisorsForBean method is a template method, mainly is reserved for the subclass to be realized. Let’s take a look at its subclasses AbstractAdvisorAutoProxyCreator implementation.
protectedObject[] getAdvicesAndAdvisorsForBean(Class<? > beanClass, String beanName, TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
Copy the code
Is to call the AbstractAdvisorAutoProxyCreator findEligibleAdvisors method.
protected List<Advisor> findEligibleAdvisors(Class
beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if(! eligibleAdvisors.isEmpty()) {// Sort the cuts
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
Copy the code
The above code is used to find the Advisor for the appropriate Bean. The main steps are:
- Get all of
Advisors
- through
findAdvisorsThatCanApply
Method will beAdvisors
与beanName
Begin to match - through
extendAdvisors
To matchAdvisor
Make a small extension (that is, add a header Advisor) - Sort last (for sequential execution)
Below I will divide the title to carry on the explanation!
Get all Advisors
Let’s first look at how to get all the Advisors. This method is invoked the AnnotationAwareAspectAutoProxyCreator findCandidateAdvisors method.
protected List<Advisor> findCandidateAdvisors(a) {
// Get it according to the parent rules
/ / in fact, that is, the above advisorRetrievalHelper. FindAdvisorBeans () call
List<Advisor> advisors = super.findCandidateAdvisors();
// Add all section classes annotated with @aspect
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
Copy the code
Match with findAdvisorsThatCanApply
And then let’s see how they match.
protected List<Advisor> findAdvisorsThatCanApply( List
candidateAdvisors, Class
beanClass, String beanName)
{
// Set beanName with ThreadLocal
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null); }}Copy the code
AopUtils. FindAdvisorsThatCanApply match
public static List<Advisor> findAdvisorsThatCanApply(List
candidateAdvisors, Class
clazz)
{
// Omit some judgment code
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceofIntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); }}booleanhasIntroductions = ! eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// Skip it, the above code is already loaded
continue;
}
if(canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); }}return eligibleAdvisors;
}
Copy the code
The core code above is the canApply method, which is primarily used to filter inappropriate advisors. Source code analysis is as follows:
public static boolean canApply(Advisor advisor, Class<? > targetClass,boolean hasIntroductions) {
// In case of IntroductionAdvisor, filter with PointCut
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// Returns true by default if there are no matches
return true; }}Copy the code
Extend with extendAdvisors
Let’s see what extendAdvisors actually do. Mainly is to call the AspectJProxyUtils makeAdvisorChainAspectJCapableIfNecessary extension code. The purpose of this method is to add ExposeBeanNameAdvisors, a special Adviors, to the chain of Advisors just filtered. The role of the exposebeanna ameadvisors is to pass the MethodInvocation when that step is called. Let’s briefly look at the code:
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// If it is empty, no execution is required
if(! advisors.isEmpty()) {boolean foundAspectJAdvice = false;
for (Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as
// this might eagerly instantiate a non-singleton AspectJ aspect
// Check whether it is cut
if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true; }}/ / if there is no ExposeInvocationInterceptor is added
if(foundAspectJAdvice && ! advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true; }}return false;
}
private static boolean isAspectJAdvice(Advisor advisor) {
return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
advisor.getAdvice() instanceof AbstractAspectJAdvice ||
(advisor instanceof PointcutAdvisor &&
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}
Copy the code
Sort advisors
And finally, let’s see what sort is mostly done by elements. In Spring Boot, the main sorting is done with the @Order annotation. Through the comparator AnnotationAwareOrderComparator implementation. Take a look at its findOrder method.
createProxy
The createProxy method is implemented by AbstractAutoProxyCreator.
If you need another extension to AOP, you can further enhance it by inheriting BeanNameAutoProxyCreator or AbstractAutoProxyCreator
I want to talk a little bit about what createProxy basically does. Step is
- Create ProxyFactory
- Judgment determines whether targetClass should be used instead of its interface proxy for a given bean
- Create the Advisor using the buildAdvisors method
- The information for the proxyFactory setting includes advisors, target objects, whether subclasses are allowed to pre-filter the Advisor that needs to be executed, and configuring the proxyFactory by calling the template method customizeProxyFactory
- Finally, call ProxyFactory.getProxy for dynamic proxy
protected Object createProxy( Class
beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
/ / if it is ConfigurableListableBeanFactory TargetSource can be exposed
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// Create a ProxyFactory to copy the configuration information of the slice
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if(! proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else{ evaluateProxyInterfaces(beanClass, proxyFactory); }}// Encapsulate the enhancer
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// Set the class to proxy
proxyFactory.addAdvisors(advisors);
// Set the class to proxy
proxyFactory.setTargetSource(targetSource);
// Provides a custom function customizeProxyFactory for subclasses
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code
ok! The above code Outlines the loading process. You said, “What? Isn’t Spring AOP based on JDK Dynamic Proxy and Cglib? Why don’t I see any code? I had no problem memorizing in the interview.” Now don’t panic, I’m going to talk about this in isolation, because it’s important and it’s long.
ProxyFactory.getProxy()
ProxyFactory/ProxyCreatorSupport/AdvisedSupport ProxyConfig/Advised
class | instructions |
---|---|
ProxyFactory | Decorates utility classes and provides apis externally |
ProxyCreatorSupport | Proxy base factory, which provides configurable AopProxyFactory and proxy listeners |
AdvisedSupport | Base class for the AOP proxy configuration manager, providing interceptor chains, interface caching, whether to filter parameters ahead of time, and so on |
ProxyConfig | Highest parent class to ensure global parameter consistency |
Now that we’ve set the properties of ProxyFactory, let’s call getProxy to get the proxy class.
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
Copy the code
CreateAopProxy is the aopProxyFactory set in ProxyCreatorSupport. If you look at createAopProxy you’ll see that it passes itself in as a parameter, and that’s what AdvisedSupport does.
Moving forward, we call the getProxy() method.
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<? > targetClass = config.getTargetClass();if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return newJdkDynamicAopProxy(config); }}Copy the code
What is actually returned is AopProxy. AopProxy has two subclasses: CglibAopProxy and JdkDynamicAopProxy. This is the JDK proxy and the Cglib proxy.
Let’s see what JdkDynamicAopProxy looks like. In the source code, JdkDynamicAopProxy implements the InvocationHandler interface, which also meets the Java native dynamic proxy implementation standards. Since InvocationHandler is implemented, its invoke method must also be invoked when its target method is called. Let’s take a look
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// Call equals
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// If the method called is hashCode
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// Both the DecoratingProxy method and the Advised interface method end up calling config
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// call by reflection
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// Check whether "expose proxy" is set to true
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
Try to delay fetching the object as long as possible, since the target object may come from the object pool or elsewheretarget = targetSource.getTarget(); Class<? > targetClass = (target ! =null ? target.getClass() : null);
// Get the call chain for the current method
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// If the chain is empty, call directly
if (chain.isEmpty()) {
// reduce the chance to create MethodInvocation, just call the method directly
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
/ / create ReflectiveMethodInvocation, this is for the sake of combining invocation chain as well as the method to carry on the prototype chain calls
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Handle JoinPoint through the method's call chain
retVal = invocation.proceed();
}
// Get the type of the value returned by the method, and process the returned informationClass<? > returnType = method.getReturnType();if(retVal ! =null&& retVal == target && returnType ! = Object.class && returnType.isInstance(proxy) && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// If the return value is equal to the target object, only the Proxy object can be returned directly
retVal = proxy;
}
else if (retVal == null&& returnType ! = Void.TYPE && returnType.isPrimitive()) {//throw Exception
}
return retVal;
}
finally {
if(target ! =null && !targetSource.isStatic()) {
// Set aside template methods to free objects (usually those with object pools need to implement this method)
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Reset to the old proxy objectAopContext.setCurrentProxy(oldProxy); }}}Copy the code
ExposeProxy mainly solves the problem of nested method proxy. For example
class A {
@Transactional(propagation = Propagation.REQUIRED)
public void a(a) {
this.b();
}
@Transactional(propagation = Propagation.REQUIRED_NEW)
public void b(a) {
System.out.println("method b"); }}Copy the code
In the above example, method B is not intercepted by the section. Method B is an internal call to method A. Then if method B also implements AOP then only through the expose-proxy attribute.
This is the JdkDynamicAopProxy proxy. Let’s take a look at the ObjenesisCglibAopProxy that belongs to Cglib. The Cglib approach is created with intercepting methods ready to generate proxy classes.
public Object getProxy(ClassLoader classLoader) {
//throw code
try{ Class<? > rootClass =this.advised.getTargetClass(); Class<? > proxySuperClass = rootClass;if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
// Loop the parent interface, saveClass<? >[] additionalInterfaces = rootClass.getInterfaces();for(Class<? > additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface); }}/ / check
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB enhancement
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));
// Get the interception method according to ClassCallback[] callbacks = getCallbacks(rootClass); Class<? >[] types =newClass<? >[callbacks.length];for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap is called after getCallbacks
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy and instantiate it
return createProxyClassAndInstance(enhancer, callbacks);
}
//throw Exception
}
Copy the code
In the process of acquiring the Proxy, we will get the corresponding interception method according to TargetSource. The method to get this is getCallbacks.
privateCallback[] getCallbacks(Class<? > rootClass)throws Exception {
// Optimized parameters
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Select the AOP interceptor
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Whether to expose the object
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());
}
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 static and the notification chain is frozen, then we can do some optimization by sending an AOP call, using a fixed chain to point the method directly to the target.
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);
// loop method
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);
}
/ / copy callbacks
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;
}
Copy the code
After a series of configurations, Spring generates and instantiates the proxy.
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { Class<? > proxyClass = enhancer.createClass(); Object proxyInstance =null;
if (objenesis.isWorthTrying()) {
try {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
}
catch (Throwable ex) {
//throw Exception}}if (proxyInstance == null) {
// If proxyInstance is null, use the normal constructor to initialize it
try {
proxyInstance = (this.constructorArgs ! =null ?
proxyClass.getConstructor(this.constructorArgTypes).newInstance(this.constructorArgs) :
proxyClass.newInstance());
}
catch (Throwable ex) {
//throw Exception
}
}
((Factory) proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
Copy the code
Finally, Cglib, like JDK Proxy, calls an invoke-like method. There are several main implementations in Cglib:
The name of the class | instructions |
---|---|
StaticUnadvisedInterceptor | A static method interceptor invocation without advise |
StaticUnadvisedExposedInterceptor | There is no static method interceptor for advise, but one that exposes proxy |
DynamicUnadvisedInterceptor | Dynamic method interceptor calls without advise |
DynamicUnadvisedExposedInterceptor | There is no dynamic method interceptor for Advise, but one that exposes proxy |
StaticDispatcher | Make sure the return value is not this |
EqualsInterceptor | Equals method interceptor |
HashCodeInterceptor | HashCode method interceptor |
FixedChainStaticTargetInterceptor | The interceptor is dedicated to advise on frozen static agents |
DynamicAdvisedInterceptor | There is a dynamic method interceptor for advise |
Because we have most commonly is advise dynamic methods interceptors, so see DynamicAdvisedInterceptor# intercept method.
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) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Delay fetching the object, since the target object may come from the object pool
target = getTarget();
if(target ! =null) {
targetClass = target.getClass();
}
// Get the call chain
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
Call the method directly if there is no advice
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// create the CglibMethodInvocation and invoke it
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
/ / release
if(target ! =null) {
releaseTarget(target);
}
// Reset the old Proxy
if(setProxyContext) { AopContext.setCurrentProxy(oldProxy); }}}Copy the code
A simple AOP example
From the above source code parsing, we know that Spring AOP is really just four steps:
- When the application starts, yeah
AOP
The relevant configuration classes are read - Then through
Spring
Extension function that handles the target object that needs to be proxied - The interceptor chain is obtained based on the target object, and then the parameter is assigned
- Finally, the proxy instantiation returns to replace the original target class
We can also implement an AOP example that mimics Spring.
- First we create a section class
Aspects
, which contains all kinds of notifications (pre, post, etc., benchmarkingSpring
的MethodBeforeAdvice
) - Then we wrote the implementation
Aspects
的SimpleAspect
The default interception passes. - And then we write one
LogAspect
inheritanceSimpleAspect
theLogAspect
You can override methods of interest to the parent class itself - Then we write one to use
JDK Dynamic Proxy
The proxy generates classes forJdkDynamicAopProxy
(For SpringJdkDynamicAopProxy
) - And then we write one
TargetSource
Acting as a proxy data source - The foundation above
AOP
With the code finished, we started to write a business interface and implementation classUserService
和UserServceImpl
(Remember becauseJDK Dynamic Proxy
Interface required) - Finally, we write test classes to test
The first step
public interface Aspects {
boolean before(Object target, Method method, Object[] args);
boolean after(Object target, Method method, Object[] args);
boolean afterException(Object target, Method method, Object[] args, Throwable e);
}
Copy the code
‘the second step
public class SimpleAspect implements Aspects{
@Override
public boolean before(Object target, Method method, Object[] args) {
return true;
}
@Override
public boolean after(Object target, Method method, Object[] args) {
return true;
}
@Override
public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
return true; }}Copy the code
The third step
public class LogAspect extends SimpleAspect{
@Override
public boolean before(Object target, Method method, Object[] args) {
System.out.println("log in the time " + System.currentTimeMillis());
return true; }}Copy the code
The fourth step
public class JdkDynamicAopProxy implements InvocationHandler {
private TargetSource target;
private Aspects aspect;
/ / the constructor
public JdkDynamicAopProxy(TargetSource target, Aspects aspect) {
this.target = target;
this.aspect = aspect;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final Object target = this.target.getTarget();
final Aspects aspects = this.aspect;
Object result = null;
if (aspects.before(target, method, args)) { // call the prefix
result = method.invoke(target, args==null ? null : args);
}
if (aspects.after(target, method, args)) { / / rear
return result;
}
return null; }}Copy the code
Step 5
public class TargetSource {
private Object target;
public Object getTarget(a) {
return target;
}
public void setTarget(Object target) {
this.target = target; }}Copy the code
Step 6: UserService interface
public interface UserService {
public void show(a);
}
Copy the code
The implementation class
public class UserServiceImpl implements UserService{
public void show(a){
System.out.println("User create"); }}Copy the code
The last step
public class AopTest {
public static void main(String[] args) {
// Create a slice
Aspects aspects = new LogAspect();
TargetSource targetSource = new TargetSource();
UserServiceImpl userService = new UserServiceImpl();
targetSource.setTarget(userService);
// Create the proxy class and call the method
JdkDynamicAopProxy jdkProxyIntercept = new JdkDynamicAopProxy(targetSource, aspects);
UserService userService1 = (UserService)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{UserService.class}, jdkProxyIntercept);
userService1.show();
}
}
Copy the code
The output is
log in the time 1605582637979
User create
Copy the code
The example above is a very simple complete example, so it doesn’t consider very much, for example
- Resolve nested notifications (via chain of responsibility invocation)
- Consider the interception method if yes
equals
,hashCode
, static methods, etc - Right now it’s hard coded,
AOP
How about adding interceptors anytime and anywhere? - And more…
It is recommended that those who want to dig deeper take a look at every detail of the Spring AOP implementation.
conclusion
At this point, the source code for Spring AOP is complete. To be honest, there are concepts or problems that can only be discovered through the source code. And in the process of looking at the source code, generally speaking, not only need to grasp the general process, but also have to ask for certain details, in order to more profound grasp.