Since springAOP will use springIOC to manage beans, those who are not familiar with springIOC can refer to my previous springIOC source code in-depth analysis.
The source code version used in this article is 5.2.x. To better understand springAOP, we use XML, and most of our actual development is in annotations. My experience has taught me that XML configuration is best from a source code understanding perspective.
Reading advice: Pull down the source code of spring’s official website and compare it to my analysis, so that you can learn the most efficiently, rather than skimming.
This article is divided into two parts. The first part introduces some of the pre-knowledge of Spring AOP. The purpose of this part is to let readers who are not familiar with Spring AOP understand some of the basic knowledge of Spring AOP. The second part is the focus of this article, which will provide an in-depth analysis of springAOP source code.
An introduction to spring AOP terminology
-
Joinpoint
The so-called join points are those points that are intercepted. In Spring, these points refer to methods, because Spring only supports join points of method types
Corresponding class:
public interface Joinpoint { // Executes the next interceptor logic in the interceptor chain Object proceed(a) throws Throwable; Object getThis(a); AccessibleObject getStaticPart(a); } Copy the code
-
Pointcut
A pointcut is the definition of which JoinPoints we want to intercept
Corresponding class:
public interface Pointcut { // Return a type filter ClassFilter getClassFilter(a); // Return a method matcher MethodMatcher getMethodMatcher(a); Pointcut TRUE = TruePointcut.INSTANCE; } Copy the code
The corresponding ClassFilter and MethodMatcher interfaces:
public interface ClassFilter { boolean matches(Class clazz); ClassFilter TRUE = TrueClassFilter.INSTANCE; } public interface MethodMatcher { boolean matches(Method method, Class targetClass); boolean matches(Method method, Class targetClass, Object... args); boolean isRuntime(a); MethodMatcher TRUE = TrueMethodMatcher.INSTANCE; } Copy the code
When we write AOP code, usually with breakthrough point expression to select join points, point expression processing categories: AspectJExpressionPointcut, inheritance of this class diagram is as follows:
Can see AspectJExpressionPointcut inherited ClassFilter and MethodMatcher interface, have matches method, so it can choose to join.
-
Advice (notification/enhancement)
After notification means intercepted Jointpoint have to do is to inform, notice is divided into pre notice (AspectJMethodBeforeAdvice), rear notification (AspectJAfterReturningAdvice), Abnormal notice (AspectJAfterThrowingAdvice), final notice (AspectAfterAdvice), surrounding the notification (AspectJAroundAdvice)
-
The Introduction (Introduction)
Introduction is a special kind of notification. Without modifying the class code, Introduction can dynamically add methods or fields to a class at run time.
-
Target = Target object
The target object of the agent, such as the UserServiceImpl class
-
Weaving (Weaving)
The process of applying an enhancement to a target object to create a new proxy object. Spring implements weaving by implementing the back-end processor BeanPostProcessor interface. That is, after the bean is initialized, the springIOC container takes over by generating a proxy object for the target object, so that the target object retrieved from the container is the enhanced proxy object.
-
Proxy
When a class is woven into AOP for enhancement, a result proxy class is produced
-
Advisor (notifier, Advisor)
It’s very similar to Aspect
-
Aspect (Aspect)
Is a combination of pointcut and advice, corresponding to the PointcutAdvisor in Spring, that allows you to get both pointcuts and advice. As shown in the following class:
public interface Advisor { Advice getAdvice(a); boolean isPerInstance(a); } public interface PointcutAdvisor extends Advisor { Pointcut getPointcut(a); } Copy the code
For the above knowledge points, through the following figure to sum up, I hope to help readers remember.
Understand the concepts of AOP, Spring AOP, AspectJ
What is the AOP
AOP is the abbreviation of Aspect Oriented Programming, meaning: Aspect Oriented Programming. Please refer to relevant definitions for details
Spring AOP
- Based on dynamic proxy implementation, if the interface is used, then use JDK dynamic proxy implementation; If no interface is implemented, CGLIB is used.
- Dynamic proxies are designed based on reflection.
- Spring provides AspectJ support and implements a pure set of Spring AOP
This article analyzes the source code based on springAOP parsing.
The principle is shown in the figure below:
Maybe it’s a little abstract, but here’s an example to help you understand:
Create a Person interface:
public interface Person {
public void eat(a);
}
Copy the code
Create the interface implementation class: PersonImpl
public class PersonImpl implements Person {
@Override
public void eat(a) {
System.out.println("I want to eat."); }}Copy the code
When our mothers told us to wash our hands before eating, how do we use code to do that?
Define an EatHandler that implements InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class EatHandler implements InvocationHandler {
// Target object to be proxied
private Object target;
public EatHandler(Object target) {
this.target = target;
}
public Object proxyInstance(a) {
// The first argument is the class loader to load the proxy object,
// The second argument is the interface to be implemented by the proxy class
// The third parameter is EatHandler
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Wash your hands before eating.");
Object result = method.invoke(this.target, args);
System.out.println("Wash the dishes after eating.");
returnresult; }}Copy the code
Here’s a test class:
public class ProxyMain {
public static void main(String[] args) {
EatHandler eatHandler = new EatHandler(newPersonImpl()); Person person = (Person) eatHandler.proxyInstance(); person.eat(); }}Copy the code
Output:
Wash your hands before eating I wash the dishes after eatingCopy the code
As you can see, before and after “I want to eat” we have inserted “wash your hands before eating” and “wash your dishes after eating” functions
How do you insert it?
The idea is that the JDK creates a proxy class for the target object (EatHandler). This proxy class inserts the desired functionality when we call Person.eat (); We operate on the proxy class instead of the original object, thus fulfilling the need to insert the functionality we want. That should make sense. The generated proxy object is posted below:
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//
// Decomcompiled by Procyon v0.5.36
//
public final class PersonImpl$Proxy1 extends Proxy implements Person
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public PersonImpl$Proxy1(final InvocationHandler h) {
super(h);
}
public final boolean equals(final Object o) {
try {
return (boolean)super.h.invoke(this, PersonImpl$Proxy1.m1, new Object[] { o });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable undeclaredThrowable) {
throw newUndeclaredThrowableException(undeclaredThrowable); }}public final void eat(a) {
try {
super.h.invoke(this, PersonImpl$Proxy1.m3, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable undeclaredThrowable) {
throw newUndeclaredThrowableException(undeclaredThrowable); }}public final String toString(a) {
try {
return (String)super.h.invoke(this, PersonImpl$Proxy1.m2, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable undeclaredThrowable) {
throw newUndeclaredThrowableException(undeclaredThrowable); }}public final int hashCode(a) {
try {
return (int)super.h.invoke(this, PersonImpl$Proxy1.m0, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable undeclaredThrowable) {
throw newUndeclaredThrowableException(undeclaredThrowable); }}static {
try {
PersonImpl$Proxy1.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
// Use reflection to get the target object Method
PersonImpl$Proxy1.m3 = Class.forName("Person").getMethod("eat", (Class<? > [])new Class[0]);
PersonImpl$Proxy1.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<? > [])new Class[0]);
PersonImpl$Proxy1.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<? > [])new Class[0]);
}
catch (NoSuchMethodException ex) {
throw new NoSuchMethodError(ex.getMessage());
}
catch (ClassNotFoundException ex2) {
throw newNoClassDefFoundError(ex2.getMessage()); }}}Copy the code
We see that the JDK does generate a Proxy object for us that inherits the Proxy object and implements the Person interface. We see equals(), toString(), hashCode(), and our eat() methods generated in the proxy object. Super.h.i nvoke(this, PersonImpl$proxy1.m3, null); Where h is EatHandle as defined. We see that when we call Person.eat (), we are actually calling the eat() method of the proxy object, starting with the EatHandle invoke method, which has the enhanced code we added, and this implements the proxy. So you get the idea.
AspectJ
- AspectJ is an AOP framework implemented in Java that enables AOP compilation of Java code (typically at compile time) to give it AspectJ’s AOP capabilities (requiring a special compiler, of course)
- AspectJ is arguably the most mature and feature-rich language currently implementing AOP frameworks. More fortunately, AspectJ is fully compatible with Java programs and is almost seamless, making it easy for engineers with a Java programming background to get started and use.
- ApectJ uses static weaving. ApectJ mainly uses compile-time weaving, in which AspectJ’s ACJ compiler (similar to JavAC) is used to compile aspect classes into class bytecode and then at Java target class compilation time, that is, compile the aspect classes first and then the target classes.
As shown below:
The source code parsing
With the foundation of the previous knowledge, now can finally enter the source code analysis stage. Let’s test our ideas. Please open the spring source.
This stage is divided into three steps:
- AOP configuration file parsing process analysis
- Proxy object creation analysis
- Proxy objects perform process analysis
AOP configuration file parsing process analysis
We need to finish parsing the following configuration files:
<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<! -- Configure the target object -->
<bean class="com.sjc.spring.aop.target.UserServiceImpl"></bean>
<! -- Configure notification classes -->
<bean id="myAdvice" class="com.sjc.spring.aop.advice.MyAdvice"></bean>
<! -- AOP configuration -->
<aop:config>
<! -- <aop:advisor advice-ref="" /> -->
<aop:pointcut expression="" id="" />
<aop:aspect ref="myAdvice">
<aop:before method="before"
pointcut="execution(* *.. *.*ServiceImpl.*(..) )" />
<aop:after method="after"
pointcut="execution(* *.. *.*ServiceImpl.*(..) )" />
</aop:aspect>
</aop:config>
</beans>
Copy the code
Entry: DefaultBeanDefinitionDocumentReader# parseBeanDefinitions
We also analyzed this step in the last parsing of SpringIOC source code, so let’s move into
delegate.parseCustomElement(ele);
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// Whether the loaded Document object uses Spring's default XML namespace (beans namespace)
if (delegate.isDefaultNamespace(root)) {
// Get all child nodes of the root element of the Document object (bean tags, import tags, alias tags, and other custom tags context, AOP, etc.)
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// The default resolution rules are used for bean tags, import tags, and alias tags
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else { // Elements like context tags, AOP tags, tx tags are resolved using user-defined parsing rulesdelegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code
We entered the: BeanDefinitionParserDelegate# parseCustomElement
This method does two main things:
- Get the namespace URI (that is, get the value of the XMLNS: AOP or XMLNS: Context property of the beans tag)
- Get the corresponding handler class according to namespaceUri
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// Get the namespace URI (that is, get the value of the XMLNS: AOP or XMLNS: Context attribute of the beans tag)
// http://www.springframework.org/schema/aop
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// Match different NamespaceHandler according to the namespace URI (one namespace corresponds to one NamespaceHandler)
/ / here will call DefaultNamespaceHandlerResolver resolve method of a class
// Two steps: Find NamespaceHandler, call NamespaceHandler init (register BeanDefinitionParser for different custom tags)
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
Copy the code
We enter NamespaceHandlerSupport#parse
The main thing we care about with this method is the parser.parse() method, and findParserForElement can be seen as a branch if you’re interested.
public BeanDefinition parse(Element element, ParserContext parserContext) {
// NamespaceHandler initializes a number of BeanDefinitionParser tags to handle custom tags
// Match BeanDefinitionParser from the specified NamespaceHandler
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// Call the parser that specifies the custom tag to complete the specific parsing
return(parser ! =null ? parser.parse(element, parserContext) : null);
}
Copy the code
BeanDefinitionParser many implementation class, here we is implemented by ConfigBeanDefinitionParser configuration file.
We entered the ConfigBeanDefinitionParser# parse ();
This method produces three main branches of interest:
-
configureAutoProxyCreator(parserContext, element);
Is mainly generated AspectJAwareAdvisorAutoProxyCreator class BeanDefinition and registered with the IOC container, this class is used to create the AOP proxy objects
-
parsePointcut(elt, parserContext);
Produce a AspectJExpressionPointcut BeanDefinition object, and registration. The AspectJExpressionPointcut expression used to resolve our starting point, the reader can look at the beginning we introduce AspectJExpressionPointcut class diagram.
-
parseAspect(elt, parserContext);
This step mainly wraps the parsed AOP :aspect tag. Let’s focus on this branch.
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
/ / registered BeanDefinition AspectJAwareAdvisorAutoProxyCreator class: (with the IoC container used to create the AOP proxy objects)
// BeanPostProcessor can perform some operations on the instantiated bean
/ / AspectJAwareAdvisorAutoProxyCreator implements the BeanPostProcessor interface, can be instantiated on the target object, create the corresponding proxy objects
configureAutoProxyCreator(parserContext, element);
< AOP :aspect>, < AOP: Advisor >, < AOP :pointcut>
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
// Get the node name or element name of the child tag
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
// parse the < AOP :pointcut> tag
/ / create a AspectJExpressionPointcut BeanDefinition objects, and register
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
// parse the < AOP: Advisor > tag
/ / create a DefaultBeanFactoryPointcutAdvisor BeanDefinition objects, and register
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
// parse the < AOP :aspect> tag
// Many BeanDefinition objects are generated
// AOP: tags such as after correspond to 5 BeanDefinition objects
// AOP: The method attribute of the after tag corresponds to a BeanDefinition object
// Final AspectJPointcutAdvisor BeanDefinition class
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
Copy the code
We entered the ConfigBeanDefinitionParser# parseAspect
This method starts parsing AOP: the aspect tag,
Our primary concern is the parseAdvice method
private void parseAspect(Element aspectElement, ParserContext parserContext) {
// Get the id attribute value of the
tag
String aspectId = aspectElement.getAttribute(ID);
// Get the ref attribute value of the < AOP :aspect> tag, which is the reference name of the enhanced class
String aspectName = aspectElement.getAttribute(REF);
try {
this.parseState.push(new AspectEntry(aspectId, aspectName));
List<BeanDefinition> beanDefinitions = new ArrayList<>();
List<BeanReference> beanReferences = new ArrayList<>();
// Handle the < AOP :declare-parents> child of the < AOP :aspect> tag
List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
Element declareParentsElement = declareParents.get(i);
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
}
// We have to parse "advice" and all the advice kinds in one loop, to get the
// ordering semantics right.
// Get all the children of the < AOP :aspect> tag
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
// Check whether < AOP :before>, < AOP :after>, < AOP :after-returning>, < AOP :after-throwing method="">, < AOP :around method=""
if (isAdviceNode(node, parserContext)) {
if(! adviceFoundAlready) { adviceFoundAlready =true;
if(! StringUtils.hasText(aspectName)) { parserContext.getReaderContext().error("<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
aspectElement, this.parseState.snapshot());
return;
}
beanReferences.add(new RuntimeBeanReference(aspectName));
}
// The method does three things:
// create a RootBeanDefinition based on the weaving methods (before, after, etc.)
// Write the RootBeanDefinition created in the previous step to a new RootBeanDefinition and construct a new object named advisorDefinition (advisor definition)
/ / 3, registered to DefaultListableBeanFactory advisorDefinition
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}
parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop(); }}Copy the code
We entered the ConfigBeanDefinitionParser# parseAdvice
This method mainly does is: parsing AOP :before and other five child tags
RootBeanDefinition (adviceDef, adviceDef, adviceDef); RootBeanDefinition (adviceDef, adviceDef, adviceDef); To construct a new object, named advisorDefinition, namely advisor definition 3, registered to DefaultListableBeanFactory advisorDefinition
private AbstractBeanDefinition parseAdvice(
String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
try {
this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
// create the method factory bean
create BeanDefinition object for Method factory Bean: Method object for Advice enhancement class < AOP :brefore Method ="before"
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
/ / set MethodLocatingFactoryBean targetBeanName advice for reference name, namely < aop: aspect ref = "myAdvice" > the myAdvice
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
/ / set MethodLocatingFactoryBean methodName to < aop: after > tag method of attribute value (namely method = "before" before, as the advice method name)
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);
// create instance factory definition
BeanDefinition: Used to create instances of the enhanced class, i.e., myAdvice in < AOP :aspect ref="myAdvice">
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
/ / set SimpleBeanFactoryAwareAspectInstanceFactory aspectBeanName to advice the reference name of the class
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);
// The above two BeanDefinitions are mainly used to call the Advice object's specified methods through reflection
// method.invoke(obj,args)
// register the pointcut
// The BeanDefinition object (core) of the notification enhancement class, i.e., an < AOP :before>, corresponds to an adviceDef
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);
// configure the advisor
// BeanDefinition object of the notifier class, corresponding to < AOP :aspect>
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
// Set the Advice object property value to the notifier class
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
// register the final advisor
// Register advisorDefinition in the IoC container
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
return advisorDefinition;
}
finally {
this.parseState.pop(); }}Copy the code
This is the end of parsing the tag
Proxy object creation analysis
Before we look at AOP proxy object creation, let’s look at the class inheritance structure diagram:
Take a look at the AbstractautoXyCreator class, which contains these methods:
PostProcessBeforeInitialization postProcessAfterInitialization - postProcessBeforeInstantiation AOP functionality entry postProcessAfterInstantiation postProcessPropertyValuesCopy the code
We find analysis entry: AbstractAutoProxyCreator# postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if(bean ! =null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Use dynamic proxy technology to generate proxy objects
// bean: target object
// beanName: the name of the target object
returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code
Enter the AbstractAutoProxyCreator# wrapIfNecessary
This approach does three things:
- Look for the collection of Advisor objects associated with the proxy class from IOC, because in the previous step we parsed the configuration file and encapsulated the information in several objects and saved it in the IOC container.
- Generate proxy objects using JDK dynamic proxies or Cglib dynamic proxies
- Put the proxy type into the cache
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
/ / Advice/Pointcut/Advisor/AopInfrastructureBean interface beanClass is not acting as well as within the beanName for aop on aspects of agent
// See the subclass override shouldSkip() method here
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// Find the collection of Advisor objects associated with the proxy class, where the point-cut expression is concerned
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// Take the proxy only when the corresponding advisor is not empty
if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
Generate proxy objects using JDK dynamic proxy or cglib dynamic proxy
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// Put the proxy type cache
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Copy the code
We’re going to go to Abstracta toProxy Create #createProxy
protected Object createProxy(Class
beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// Create the proxy factory object
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// If the CGLib proxy is not used
if(! proxyFactory.isProxyTargetClass()) {// Whether it is possible to use the CGLib proxy
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
/ / see if beanClass corresponding class contains InitializingBean. The class/DisposableBean class/Aware. The class interface
// If no, use JDK dynamic proxy. If yes, use CGLib dynamic proxyevaluateProxyInterfaces(beanClass, proxyFactory); }}// Get the set of all associated Advisors (this branch needs to be added)
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
// The targetSource is SingletonTargetSource
proxyFactory.setTargetSource(targetSource);
// Empty implementation
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
// Whether to set prefiltering mode. True for this article
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Get objects generated using JDK dynamic proxies or cglib dynamic proxies
return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code
Let’s go to ProxyFactor #getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
// create a JDK or CGLib AOP proxy
Call AopProxy to create a Proxy object
return createAopProxy().getProxy(classLoader);
}
Copy the code
We chose to create the proxy in the JDK because we implemented the proxy through the interface.
Let’s go to JdkDynamicAopProxy#getProxy
This method does two main things:
- Get all proxy interfaces
- Invoke the JDK dynamic proxy
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
// Get the full proxy interfaceClass<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// Call the JDK dynamic proxy method
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code
At this point, the analysis of the generated proxy class object is completed
Proxy objects perform process analysis
Let’s look at JdkDynamicAopProxy, which implements InvocationHandler, so we’ve already found the entry point for the proxy object to perform the process analysis
Entry: JdkDynamicAopProxy# invoke
This method mainly does the following:
-
Get all enhancers (Advisors) for the target object
These advisors are sequential, and they make chain calls in order
-
If the call chain is empty, the target object’s method is called directly through reflection, that is, without any enhancement to the method
-
Create instances to call invocation chain ReflectiveMethodInvocation start executing process of AOP interception
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
/ /... Omit some code
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
// Get the target object
target = targetSource.getTarget();
// Get the type of the target objectClass<? > targetClass = (target ! =null ? target.getClass() : null);
// Get the interception chain for this method.
// Get all of the enhancers (Advisors) for the target object, which are sequentially called in chain order
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.
// Check if we have any notifications. If we don't, we can simply make the reflection invocation to the target class instead of creating the MethodInvocation class
// Call the method of the target class
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);
// Call the target object's method through reflection
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
// We need to create a method call
// proxy: generates dynamic proxy objects
// target: target method
// args: target method parameter
// targetClass: targetClass object
// CHAIN: AOP interceptor execution chain, which is a collection of methodInterceptors
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// Enter the join point through the interceptor chain
// Start the AOP interception process
retVal = invocation.proceed();
}
// Massage return value if necessary.Class<? > returnType = method.getReturnType();if(retVal ! =null&& retVal == target && returnType ! = Object.class && 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); }}Copy the code
Let’s look at the process of getting the call chain:
To: AdvisedSupport# getInterceptorsAndDynamicInterceptionAdvice
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class
targetClass) {
// Create a cache key in unit of method
MethodCacheKey cacheKey = new MethodCacheKey(method);
// Get the collection of advisors for the specified method from the cache
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
// Gets the MethodInterceptor collection of the specified methods in the target class, converted from the Advisor
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
Copy the code
Let’s get the MethodInterceptor collection:
DefaultAdviceChainFactory#getInterceptorsAndDynamicInterceptionAdvice
Here we mainly do:
-
Create DefaultAdvisorAdapterRegistry instance, and create MethodBeforeAdviceAdapter, AfterReturningAdviceAdapter, ThrowsAdviceAdapter adapter
Why do you need an adapter here? Let’s look at the following class diagram:
Some Advice is not related to the interceptor MethodInterceptor at all, and you need the adapter to generate the relationship between them. Here is the adapter mode, for example, we bought a lot of models of computers, the use of voltage is not the same, our home voltage is 220v, that how to make our computer can charge on it, nano will need a power adapter. So that makes sense
Matches () with Pointcut ClassFilter().matches() and Pointcut().getmatcher (). If it matches, convert the Advisor to a MethodInterceptor,
3. If you need according to the parameters of the dynamic matching (such as overloading) interceptor in the chain of new InterceptorAndDynamicMethodMatcher
public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class
targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
// Advice adapter registry
/ / MethodBeforeAdviceAdapter: Advisor adaptation into MethodBeforeAdvice
/ / AfterReturningAdviceAdapter: Advisor adaptation into AfterReturningAdvice
// ThrowsAdviceAdapter: ADAPTS Advisor to ThrowsAdvice
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
// Returns a collection of values that are either Interceptor or its subclass interface MethodInterceptor
List<Object> interceptorList = new ArrayList<>(advisors.length);
// Get the target class typeClass<? > actualClass = (targetClass ! =null ? targetClass : method.getDeclaringClass());
// Whether there is an introduction
Boolean hasIntroductions = null;
// The collection of all appropriate advisors obtained for the target method during the generation of the proxy object
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// If the Advisor can enhance the target class, proceed
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// Gets a method adapter that matches methods based on the specified pointcut expression
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
match = mm.matches(method, actualClass);
}
if (match) {
// Convert the Advisor to MethodInterceptor
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
The MethodMatcher interface defines two matches() methods via overloading
Matches () is called static matching. It can be used in most scenarios if the match condition is not too strict
The matches() method, called static, matches the types of the three arguments dynamically at run time
// The dividing line between the two methods is the Boolean isRuntime() method
Matches () matches the Boolean isRuntime() function with a matches() function
Matches () : matches(); // true; matches();
// Need to dynamically match parameters (such as overloading)
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(newInterceptorAndDynamicMethodMatcher(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;
}
Copy the code
We return to JDkDynamicaOpXy# invoke
Enter the ReflectiveMethodInvocation# proceed (in) :
This is the execution of the call chain
-
If it goes to the end of the chain, the join point is called directly, that is, the target method is called directly
-
Will call MethodInterceptor invoke method, in this paper, the configuration file will be called AspectJAfterAdvice, MethodBeforeAdviceInterceptor
Let’s look at AspectJAfterAdvice: the method call goes like this, and the call chain continues
public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null.null); }}Copy the code
We see: MethodBeforeAdviceInterceptor (pre inform interceptor)
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
Copy the code
The target method is executed and then the next call chain is called
This ensures that the order of calls is followed by the pre-notification before the target method and the final notification after the target method executes. It also explains that < AOP :before /> in the configuration file
The order of < AOP :after method=”after”/> does not affect the order of final execution, which is guaranteed by the call chain.
public Object proceed(a) throws Throwable {
// We start with an index of -1 and increment early.
// If the execution goes to the end of the chain, the join point method is called directly, i.e. the target method is called directly
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// Get the MethodInterceptor from the collection
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
/ / if it is InterceptorAndDynamicMethodMatcher type (dynamic)
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<? > targetClass = (this.targetClass ! =null ? this.targetClass : this.method.getDeclaringClass());
// The target method is matched each time
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
MethodInterceptor's invoke method is called directly if it matches
/ / note that there is this incoming parameters, we see ReflectiveMethodInvocation types below
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
// If this target method does not apply, proceed to the next chain
// recursive call
returnproceed(); }}else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// The specification applies to the target method and calls the invoke method of the MethodInterceptor directly
/ / in this namely ReflectiveMethodInvocation instance
// Pass this in to form a chain of calls
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code
After a long time of writing, finally written, due to the limited level, can not be detailed in all aspects of the reader forgive me, can communicate with me.