In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "Spring source code analysis of how to AOP from parsing to calling", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Next let the editor to take you to learn "Spring source code analysis of how to AOP from parsing to calling" it!
First of all, in order for you to understand AOP more effectively, let's take a look at the terms in AOP:
Aspect: refers to modularization of a concern, which may crosscut multiple objects. Transaction management is an example of crosscutting concerns in enterprise Java applications. In Spring AOP, aspects can be implemented as @ Aspect annotations in normal classes. Join point: in Spring AOP, a join point always represents the execution of a method, which actually represents an enhanced method. Advice: an action performed at a specific join point of a section. There are many types of notifications, including around, before, after, and so on. Many AOP frameworks, including Spring, use interceptors as notification models and maintain a chain of interceptors centered on join points. Target object (Target): the target object refers to the object to be enhanced. That is, the object of the class that contains the main business logic. Pointcut: the assertion that matches the join point. The notification is associated with a pointcut expression and runs on a join point that satisfies that pointcut (for example, when a method with a particular name is executed). How pointcut expressions match join points is the core of AOP: Spring uses AspectJ pointcut semantics by default. Consultant (Advisor): consultant is a packaging embodiment of Advice, and Advisor is a combination of Pointcut and Advice to manage Advice and Pointcut. Weaving: the process of cutting notifications into join points is called Introductions: other interfaces and implementations can be dynamically introduced into a chestnut in targetClass
After reading the terminology, let's review it on Demo first.
First, open our AOP using the EnableAspectJAutoProxy annotation
@ ComponentScan (basePackages = {"com.my.spring.test.aop"})
@ Configuration
@ EnableAspectJAutoProxy
Public class Main {
Public static void main (String [] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (Main.class)
IService service = context.getBean ("service", IService.class)
Service.doService ()
}
}
Write an interface
Public interface IService {
Void doService ()
}
Write an implementation class
@ Service ("service")
Public class ServiceImpl implements IService {
@ Override
Public void doService () {
System.out.println ("do service...")
}
}
Write a section
@ Aspect
@ Component
Public class ServiceAspect {
@ Pointcut (value = "execution (* com.my.spring.test.aop.*.* (..)")
Public void pointCut () {
}
@ Before (value = "pointCut ()")
Public void methodBefore (JoinPoint joinPoint) {
String methodName = joinPoint.getSignature () .getName ()
System.out.println ([pre-notification] of "execute target method [" + methodName + "]. Input parameter:" + Arrays.toString (joinPoint.getArgs ().
}
@ After (value = "pointCut ()")
Public void methodAfter (JoinPoint joinPoint) {
String methodName = joinPoint.getSignature () .getName ()
System.out.println ([Post Notification] of "execute target method [" + methodName + "]. Input parameter:" + Arrays.toString (joinPoint.getArgs ()
}
@ AfterReturning (value = "pointCut ()")
Public void methodReturn (JoinPoint joinPoint) {
String methodName = joinPoint.getSignature () .getName ()
System.out.println ([return notification] of "execute target method [" + methodName + "]. Input parameter:" + Arrays.toString (joinPoint.getArgs ()
}
@ AfterThrowing (value = "pointCut ()")
Public void methodThrow (JoinPoint joinPoint) {
String methodName = joinPoint.getSignature () .getName ()
System.out.println ([exception notification] of "execute target method [" + methodName + "]. Input parameter:" + Arrays.toString (joinPoint.getArgs ().
}
}
Test run
Execute the [pre-notification] of the target method [doService]. Input parameter: []
Do service...
Execute the [return Notification] of the target method [doService]. Input parameter: []
Execute the [post notification] of the target method [doService]. Input parameter: []
Above
The Demo has been read, the running effect has come out, and the AOP has come into effect, but how does it work? Compared to the Demo that we normally use Bean, here we just add a @ EnableAspectJAutoProxy annotation and a class marked @ Aspectj, so let's see what the @ EnableAspectJAutoProxy annotation does.
Turn on AOP
The following is the general flow chart drawn by the author.
AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar, so the registerBeanDefinitions method will be called when dealing with BeanFactoryPostProcessor logic, and AnnotationAwareAspectJAutoProxyCreator will be registered in the container. The logic of BeanFactoryPostProcessor will no longer be mentioned, which has been analyzed in detail in previous articles. The class diagram of AnnotationAwareAspectJAutoProxyCreator is as follows:
We found that AnnotationAwareAspectJAutoProxyCreator is a class that implements the BeanPostProcessor interface, so it is actually a post-processor, so do you remember when BeanPostProcessor was called nine times during the creation of Bean? It doesn't matter if you don't remember. The place where AnnotationAwareAspectJAutoProxyCreator works is that before instantiation and after initialization of bean, it corresponds to the process of parsing facets and creating dynamic proxies. Now, let's take a look at the process of parsing facets.
Analytical tangent plane
The process of parsing the aspect is shown in the following figure:
We have learned that the process of aspect parsing is done by AnnotationAwareAspectJAutoProxyCreator, and AnnotationAwareAspectJAutoProxyCreator inherits AbstractAutoProxyCreator, so first, we will come to AbstractAutoProxyCreator#postProcessBeforeInstantiation
Public Object postProcessBeforeInstantiation (Class beanClass, String beanName) {
/ / whether the class type is (Advice, Pointcut, Advisor, AopInfrastructureBean)
/ / the section will be parsed in shouldSkip
If (isInfrastructureClass (beanClass) | | shouldSkip (beanClass, beanName)) {
This.advisedBeans.put (cacheKey, Boolean.FALSE)
Return null
}
}
Call the AspectJAwareAdvisorAutoProxyCreator#shouldSkip to the subclass
@ Override
Protected boolean shouldSkip (Class beanClass, String beanName) {
/ / find advisor
List candidateAdvisors = findCandidateAdvisors ()
For (Advisor advisor: candidateAdvisors) {
If (advisor instanceof AspectJPointcutAdvisor & &
((AspectJPointcutAdvisor) advisor) .getAspectName () .equals (beanName)) {
Return true
}
}
Return super.shouldSkip (beanClass, beanName)
}
FindCandidateAdvisors
Protected List findCandidateAdvisors () {
/ / find the class that implements the Advisor interface. Since we don't usually implement the aspect as an interface, we return null here.
List advisors = super.findCandidateAdvisors ()
If (this.aspectJAdvisorsBuilder! = null) {
/ / all the sections will be parsed here
Advisors.addAll (this.aspectJAdvisorsBuilder.buildAspectJAdvisors ())
}
Return advisors
}
BuildAspectJAdvisors
Public List buildAspectJAdvisors () {
/ / if there is a value of aspectBeanNames, it means that the section has been resolved.
List aspectNames = this.aspectBeanNames
/ / Double Check
If (aspectNames = = null) {
Synchronized (this) {
AspectNames = this.aspectBeanNames
If (aspectNames = = null) {
List advisors = new ArrayList ()
AspectNames = new ArrayList ()
/ / take out the bean of the Object subclass, which is actually all the bean
String [] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors (
This.beanFactory, Object.class, true, false)
For (String beanName: beanNames) {
/ / obtain the class of the bean
Class beanType = this.beanFactory.getType (beanName)
/ / determine whether there is a @ AspectJ comment
If (this.advisorFactory.isAspect (beanType)) {
/ / put beanName into the collection
AspectNames.add (beanName)
/ / encapsulate beanType and beanName into AspectMetadata
AspectMetadata amd = new AspectMetadata (beanType, beanName)
/ / Kind defaults to SINGLETON
If (amd.getAjType (). GetPerClause (). GetKind () = = PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
New BeanFactoryAspectInstanceFactory (this.beanFactory, beanName)
/ / all advisor will be obtained through @ Before @ After and other identification methods.
List classAdvisors = this.advisorFactory.getAdvisors (factory)
If (this.beanFactory.isSingleton (beanName)) {
/ / put all acquired advisor into cache
This.advisorsCache.put (beanName, classAdvisors)
}
Advisors.addAll (classAdvisors)
}
}
}
/ / assign all parsed beanName values
This.aspectBeanNames = aspectNames
Return advisors
}
}
}
/ / aspectNames is not empty, which means advisor. Take out all the previously parsed advisor
List advisors = new ArrayList ()
/ / get all the parsed advisor
For (String aspectName: aspectNames) {
List cachedAdvisors = this.advisorsCache.get (aspectName)
If (cachedAdvisors! = null) {
Advisors.addAll (cachedAdvisors)
}
Return advisors
}
AdvisorFactory.getAdvisors
Public List getAdvisors (MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
/ / get the class marked @ AspectJ, which is actually the class just encapsulated
Class aspectClass = aspectInstanceFactory.getAspectMetadata () .getAspectClass ()
/ / obtain className
String aspectName = aspectInstanceFactory.getAspectMetadata () .getAspectName ()
List advisors = new ArrayList ()
/ / the method will be sorted once when traversing the getAdvisorMethods with all the methods of this class except identifying @ PointCut
/ / sort order Around, Before, After, AfterReturning, AfterThrowing
For (Method method: getAdvisorMethods (aspectClass)) {
/ / get the advisor
Advisor advisor = getAdvisor (method, lazySingletonAspectInstanceFactory, 0, aspectName)
If (advisor! = null) {
/ / add to the collection
Advisors.add (advisor)
}
}
}
Let's take a look at the getAdvisorMethods method.
Private List getAdvisorMethods (Class aspectClass) {
Final List methods = new ArrayList ()
/ / Loop through all methods of this class and parent class
ReflectionUtils.doWithMethods (aspectClass, method-> {
/ / the method of excluding @ PointCut identity
If (AnnotationUtils.getAnnotation (method, Pointcut.class) = = null) {
Methods.add (method)
}
}, ReflectionUtils.USER_DECLARED_METHODS)
If (methods.size () > 1) {
/ / Custom sorting in the order of Around, Before, After, AfterReturning, AfterThrowing
Methods.sort (METHOD_COMPARATOR)
}
Return methods
}
I don't know if my friends are familiar with the tool class ReflectionUtils.doWithMethods. This tool class has appeared many times before when analyzing the Bean creation process, and we can also use it.
Now that you've got all the methods in the section, it's time to parse these methods and encapsulate them into advisor ~
GetAdvisor
Public Advisor getAdvisor (Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory
Int declarationOrderInAspect, String aspectName) {
/ / get the tangent expression on the method
AspectJExpressionPointcut expressionPointcut = getPointcut (
CandidateAdviceMethod, aspectInstanceFactory.getAspectMetadata (). GetAspectClass ()
/ / Encapsulation is returned as an object. When creating an object, the method will be parsed to create advice.
Return new InstantiationModelAwarePointcutAdvisorImpl (expressionPointcut, candidateAdviceMethod
This, aspectInstanceFactory, declarationOrderInAspect, aspectName)
}
The process of getting the tangent expression is actually very simple, that is, you can parse the annotation on the method and take out the value on the annotation.
GetPointcut
Private AspectJExpressionPointcut getPointcut (Method candidateAdviceMethod, Class candidateAspectClass) {
/ / find comments related to AspectJ in the method
AspectJAnnotation aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod (candidateAdviceMethod)
/ / set the tangent expression
AspectJExpressionPointcut ajexp =
New AspectJExpressionPointcut (candidateAspectClass, new String [0], new Class [0])
/ / PointcutExpression is the value of the value attribute on the note
Ajexp.set_Expression (aspectJAnnotation.getPointcut_Expression ())
If (this.beanFactory! = null) {
Ajexp.setBeanFactory (this.beanFactory)
}
Return ajexp
}
New InstantiationModelAwarePointcutAdvisorImpl, here, will really create the advice.
Public InstantiationModelAwarePointcutAdvisorImpl () {
/ /... Omit the assignment process.
/ / instantiate an advice
This.instantiatedAdvice = instantiateAdvice (this.declaredPointcut)
}
Private Advice instantiateAdvice (AspectJExpressionPointcut pointcut) {
/ / get advice,aspectJAdviceMethod as the method and aspectName as the aspect class
Advice advice = this.aspectJAdvisorFactory.getAdvice (this.aspectJAdviceMethod, pointcut
This.aspectInstanceFactory, this.declarationOrder, this.aspectName)
Return (advice! = null? Advice: EMPTY_ADVICE)
}
Public Advice getAdvice () {
/ / get the annotation information according to the method
AspectJAnnotation aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod (candidateAdviceMethod)
AbstractAspectJAdvice springAdvice
/ / the object is returned according to the annotation type, and the process of creating the object is the same. The constructor of the parent class is called.
/ / candidateAdviceMethod is the tangent method, and expressionPointcut is the tangent point
Switch (aspectJAnnotation.getAnnotationType ()) {
Case AtPointcut
Return null
Case AtAround:
SpringAdvice = new AspectJAroundAdvice (
CandidateAdviceMethod, expressionPointcut, aspectInstanceFactory)
Break
Case AtBefore:
SpringAdvice = new AspectJMethodBeforeAdvice (
CandidateAdviceMethod, expressionPointcut, aspectInstanceFactory)
Break
Case AtAfter:
SpringAdvice = new AspectJAfterAdvice (
CandidateAdviceMethod, expressionPointcut, aspectInstanceFactory)
Break
/ /... Omit other advice
Default:
Throw new UnsupportedOperationException (
"Unsupported advice type on method:" + candidateAdviceMethod)
}
Return springAdvice
}
The springAdvice has been created, which means that one of the methods in the section has been parsed, and the parsing process of other methods is roughly similar.
Summary
In fact, parsing the aspect itself is not complicated, but it is easy to cause confusion by encapsulating the aspect class in Spring. For example, in the buildAspectJAdvisors method, an AspectMetadata amd = new AspectMetadata (beanType, beanName) is encapsulated. And immediately initiated to determine that amd.getAjType (). GetPerClause (). GetKind () = = PerClauseKind.SINGLETON, in fact, here can be completely changed into AjTypeSystem.getAjType (currClass). GetPerClause (). GetKind () = = PerClauseKind.SINGLETON,AjTypeSystem.getAjType (currClass) is a part of the logic of new AspectMetadata, the author here to give you a summary.
First, loop through all the beanName, find the class with @ Aspectj annotation, get all the methods in the class for traversal and parse, take out the value on the method note (pointcut: pointcut), and then encapsulate the factory of the method, the pointcut expression, into the corresponding SpringAdvice, and combine SpringAdvice and pointcut into an advisor.
Create a proxy object
Now that the section has been parsed, let's look at how to weave the parsed section into the target method.
However, before that, it is necessary to add a little bit of pre-knowledge to our friends.
We know that whether a bean can be proxied by aop depends on whether it meets the proxy condition, that is, whether it can be hit by a pointcut expression. In Spring AOP, bean is matched with a pointcut expression by AspectJ, not by Spring, so let's take a look at how AspectJ matches the appropriate bean.
Chestnut
First, you need to introduce org.aspectj:aspectjweaver dependencies.
A Service with a package named com.my.spring.test.aop
Package com.my.spring.test.aop
/ * *
* classes that can be matched by pointcut expressions
*
, /
Public class ServiceImpl {
/ * *
* methods in which tangent expressions can be matched
, /
Public void doService () {
System.out.println ("do service...")
}
Public void matchMethod () {
System.out.println ("ServiceImpl.notMatchMethod")
}
}
Then, we encapsulate a tool class for matching. See the notes for the specific functions. Haha.
Package com.my.spring.test.aspectj
Import org.aspectj.weaver.tools.PointcutExpression
Import org.aspectj.weaver.tools.PointcutParser
Import org.aspectj.weaver.tools.ShadowMatch
Import java.lang.reflect.Method
/ * *
* aop tool
, /
Public class AOPUtils {
/ / fixed writing of AspectJ to get a pointcut parser
Static PointcutParser parser = PointcutParser
.getPointcutParserSupportingSpecifiedPrivvesAndUsingSpecifiedClassLoaderForResolution (
PointcutParser.getAllSupportedPointcutPrimitives (), ClassLoader.getSystemClassLoader ()
/ / Tangent point expression
Private static PointcutExpression pointcutExpression
/ * *
* to initialize the utility class, we need to get a pointcut expression first
*
* @ param expression expression
, /
Public static void init (String expression) {
/ / parse a tangent expression
PointcutExpression = parser.parsePointcut_Expression (expression)
}
/ * *
* the first screening, also known as coarse screening, is based on class.
*
* @ param targetClass target class
Does * @ return match
, /
Public static boolean firstMatch (Class targetClass) {
/ / filter by class
Return pointcutExpression.couldMatchJoinPointsInType (targetClass)
}
/ * *
* the second screening, which is also called fine screening according to the method, indicates a perfect match if the fine screening is passed.
* ps: you can also use this method for fine screening. The purpose of coarse screening is to improve performance. For the first time, you can directly filter out inappropriate classes and then slowly screen them.
*
* @ param method method
Does * @ return match
, /
Public static boolean lastMatch (Method method) {
/ / filter according to the method
ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution (method)
Return shadowMatch.alwaysMatches ()
}
}
test
Public class AOPUtilsTest {
Public static void main (String [] args) throws NoSuchMethodException {
/ / define expression
String expression = "execution (* com.my.spring.test.aop.*.* (..))"
/ / initialize the tool class
AOPUtils.init (expression)
/ / coarse sieve
Boolean firstMatch = AOPUtils.firstMatch (ServiceImpl.class)
If (firstMatch) {
System.out.println ("pass the first filter")
/ / the normal situation should be to get all the methods for traversal, I am lazy here ~
Method doService = ServiceImpl.class.getDeclaredMethod ("doService")
/ / Fine screen
Boolean lastMatch = AOPUtils.lastMatch (doService)
If (lastMatch) {
System.out.println ("second filter passed")
}
Else {
System.out.println ("failed the second filter")
}
}
Else {
System.out.println ("failed the first filter")
}
}
}
The result (no screenshot, skeptical friends can try it for themselves)
Pass the first screening
Pass the second screening
When we create a new class Test, replace the pointcut expression with
Execution (* com.my.spring.test.aop.Test.* (..))
The test results are
The first screening failed.
Then replace the tangent expression with the specified method
Execution (* com.my.spring.test.aop.*.matchMethod (..))
Result
Pass the first screening
The second screening failed.
At this point, my friends should understand how to use AspectJ.
Proxy object creation process
Next, let's take a look at how Spring uses AspectJ to match the corresponding advisor and create a proxy object. Here is a general roadmap for creating a proxy object.
The creation of the proxy object is completed after bean initialization, so the corresponding beanPostProcessor call time is postProcessAfterInitialization.
AbstractAutoProxyCreator#postProcessAfterInitialization
Public Object postProcessAfterInitialization (@ Nullable Object bean, String beanName) {
If (bean! = null) {
/ / get the cache key value, which is actually beanName
Object cacheKey = getCacheKey (bean.getClass (), beanName)
/ / determine whether the object is in the cache. If so, the object has been dynamically proxied and skipped.
If (this.earlyProxyReferences.remove (cacheKey)! = bean) {
Return wrapIfNecessary (bean, beanName, cacheKey)
}
}
Return bean
}
WrapIfNecessary
Protected Object wrapIfNecessary (Object bean, String beanName, Object cacheKey) {
/ / get the matching advisor based on bean
Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean.getClass (), beanName, null)
If (specificInterceptors! = DO_NOT_PROXY) {
/ / create a proxy object
Object proxy = createProxy (
Bean.getClass (), beanName, specificInterceptors, new SingletonTargetSource (bean))
Return proxy
}
Return bean
}
GetAdvicesAndAdvisorsForBean
Protected Object [] getAdvicesAndAdvisorsForBean (
Class beanClass, String beanName, @ Nullable TargetSource targetSource) {
/ / get the appropriate advisor
List advisors = findEligibleAdvisors (beanClass, beanName)
Return advisors.toArray ()
}
FindEligibleAdvisors
Protected List findEligibleAdvisors (Class beanClass, String beanName) {
/ / get all the advisor first. This is the same as the parsing process. Since it has been parsed, it will be directly removed from the cache.
List candidateAdvisors = findCandidateAdvisors ()
/ / filter out matching advisor
List eligibleAdvisors = findAdvisorsThatCanApply (candidateAdvisors, beanClass, beanName)
/ / add a default advisor
ExtendAdvisors (eligibleAdvisors)
If (! eligibleAdvisors.isEmpty ()) {
/ / sort
EligibleAdvisors = sortAdvisors (eligibleAdvisors)
}
Return eligibleAdvisors
}
FindAdvisorsThatCanApply
Protected List findAdvisorsThatCanApply (
List candidateAdvisors, Class beanClass, String beanName) {
/ / find matching advisor
Return AopUtils.findAdvisorsThatCanApply (candidateAdvisors, beanClass)
}
FindAdvisorsThatCanApply
Public static List findAdvisorsThatCanApply (List candidateAdvisors, Class clazz) {
List eligibleAdvisors = new ArrayList ()
For (Advisor candidate: candidateAdvisors) {
/ / determine whether there is a match
If (canApply (candidate, clazz, hasIntroductions)) {
/ / add to the appropriate advisors collection
EligibleAdvisors.add (candidate)
}
}
Return eligibleAdvisors
}
CanApply
Public static boolean canApply (Advisor advisor, Class targetClass, boolean hasIntroductions) {
If (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor
/ / determine whether there is a match
Return canApply (pca.getPointcut (), targetClass, hasIntroductions)
}
Else {
/ / It doesn't have a pointcut so we assume it applies.
Return true
}
}
CanApply
Public static boolean canApply (Pointcut pc, Class targetClass, boolean hasIntroductions) {
/ / filter for the first time to determine whether the matching condition is met by class screening.
/ / the pointcut expression will be initialized here
If (! pc.getClassFilter (). Matches (targetClass)) {
Return false
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null
If (methodMatcher instanceof IntroductionAwareMethodMatcher) {
IntroductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher
}
For (Class clazz: classes) {
Method [] methods = ReflectionUtils.getAllDeclaredMethods (clazz)
/ / cycle all methods for a second screening to determine whether there is a method to meet the matching conditions
For (Method method: methods) {
If (introductionAwareMethodMatcher! = null?
IntroductionAwareMethodMatcher.matches (method, targetClass, hasIntroductions):
MethodMatcher.matches (method, targetClass)) {
Return true
}
}
}
Return false
}
Pc.getClassFilter ()
Public ClassFilter getClassFilter () {
ObtainPointcut_Expression ()
Return this
}
ObtainPointcutExpression
Private PointcutExpression obtainPointcut_Expression () {
If (this.pointcutExpression = = null) {
/ / confirm the class loader
This.pointcutClassLoader = determinePointcutClassLoader ()
/ / create a tangent expression
This.pointcutExpression = buildPointcut_Expression (this.pointcutClassLoader)
}
Return this.pointcutExpression
}
BuildPointcutExpression
Private PointcutExpression buildPointcut_Expression (@ Nullable ClassLoader classLoader) {
/ / initialize the pointcut parser
PointcutParser parser = initializePointcutParser (classLoader)
PointcutParameter [] pointcutParameters = new PointcutParameter [this.pointcutParameterNames.length]
For (int I = 0; I
< pointcutParameters.length; i++) { pointcutParameters[i] = parser.createPointcutParameter( this.pointcutParameterNames[i], this.pointcutParameterTypes[i]); } // 使用切点解析器进行解析表达式获取切点表达式 return parser.parsePointcut_Expression(replaceBooleanOperators(resolve_Expression()), this.pointcutDeclarationScope, pointcutParameters); } initializePointcutParser private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) { // 获得切点解析器 PointcutParser parser = PointcutParser .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( SUPPORTED_PRIMITIVES, classLoader); parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler()); return parser; } pc.getClassFilter便是完成了以上事情,此时再进行调用matchs方法 public boolean matches(Class targetClass) { PointcutExpression pointcutExpression = obtainPointcut_Expression(); // 使用切点表达式进行粗筛 return pointcutExpression.couldMatchJoinPointsInType(targetClass); } introductionAwareMethodMatcher.matches 同样如此 以上便是寻找合适的advisor的过程,下面,就是通过这些advisor进行创建动态代理了 createProxy protected Object createProxy(Class beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // 将specificInterceptors(现在是Object)转化为Advisor返回 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // 赋值到proxyFactory的advisors属性中 proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); // 创建动态代理 return proxyFactory.getProxy(getProxyClassLoader()); } proxyFactory.getProxy public Object getProxy(@Nullable ClassLoader classLoader) { // 创建代理对象 return createAopProxy().getProxy(classLoader); } createAopProxy protected final synchronized AopProxy createAopProxy() { // 创建AOP代理对象 return getAopProxyFactory().createAopProxy(this); } public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // @EnableAspectJAutoProxy的proxyTargetClass是否配置为true 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."); } // 如何是接口则创建jdk动态代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // cglib动态代理 return new ObjenesisCglibAopProxy(config); } // 默认是jdk动态代理 else { return new JdkDynamicAopProxy(config); } } public Object getProxy(@Nullable ClassLoader classLoader) { // 获取到代理的接口 Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 创建jdk代理,传入的为JdkDynamicAopProxy对象,里面包含了被代理的bean以及匹配的advisor return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } 动态代理创建完成~ 代理对象调用过程 对象都给你创建好了,接下当然是开..发起调用咯 以下是调用的大致流程图 代理对象被调用的是invoke方法,我们所创建的代理对象为JdkDynamicAopProxy,所以 JdkDynamicAopProxy#invoke public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; // 取出包装了被代理bean的对象->SingletonTargetSource when the proxy object is created, and advised is ProxyFactory
TargetSource targetSource = this.advised.targetSource
Object target = null
/ / get the bean
Target = targetSource.getTarget ()
Class targetClass = (target! = null? Target.getClass (): null)
/ / remove the advice from all advisor and convert it to the corresponding interceptor
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice (method, targetClass)
/ / create an outermost MethodInvocation to initiate the call
MethodInvocation invocation =
New ReflectiveMethodInvocation (proxy, target, method, args, targetClass, chain)
/ / initiate a chain call
Object retVal = invocation.proceed ()
Return retVal
}
Let's first look at the process of obtaining interceptor.
GetInterceptorsAndDynamicInterceptionAdvice
Public List getInterceptorsAndDynamicInterceptionAdvice (Method method, @ Nullable Class targetClass) {
/ / remove the advice from all advisor and encapsulate it into intercept
Return this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice (this, method, targetClass)
}
Public List getInterceptorsAndDynamicInterceptionAdvice (
Advised config, Method method, @ Nullable Class targetClass) {
/ / create a registry of advisor adapters to convert advice. Three adapters will be registered by default when created.
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance ()
Advisor [] advisors = config.getAdvisors ()
/ / Loop through all advisor
For (Advisor advisor: advisors) {
/ / convert advice in advisor to interceptor
MethodInterceptor [] interceptors = registry.getInterceptors (advisor)
InterceptorList.addAll (Arrays.asList (interceptors))
Return interceptorList
}
}
Call the static method when the GlobalAdvisorAdapterRegistry.getInstance () class is initialized
Private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry ()
Public static AdvisorAdapterRegistry getInstance () {
Return instance
}
Public DefaultAdvisorAdapterRegistry () {
/ / register three adapters
RegisterAdvisorAdapter (new MethodBeforeAdviceAdapter ())
RegisterAdvisorAdapter (new AfterReturningAdviceAdapter ())
RegisterAdvisorAdapter (new ThrowsAdviceAdapter ())
}
Public void registerAdvisorAdapter (AdvisorAdapter adapter) {
/ / add adapters to the collection
This.adapters.add (adapter)
}
Registry.getInterceptors includes the process of converting advice into interceptor.
Public MethodInterceptor [] getInterceptors (Advisor advisor) throws UnknownAdviceTypeException {
List interceptors = new ArrayList (3)
Advice advice = advisor.getAdvice ()
/ / is advice itself a MethodInterceptor?
If (advice instanceof MethodInterceptor) {
Interceptors.add ((MethodInterceptor) advice)
}
For (AdvisorAdapter adapter: this.adapters) {
/ / determine which advice advice is, such as: (advice instanceof MethodBeforeAdvice)
If (adapter.supportsAdvice (advice)) {
/ / encapsulate the advice to the corresponding interceptor
Interceptors.add (adapter.getInterceptor (advisor))
}
}
Return interceptors.toArray (new MethodInterceptor [0])
}
If adapter is MethodBeforeAdviceAdapter, then
Public MethodInterceptor getInterceptor (Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice ()
Return new MethodBeforeAdviceInterceptor (advice)
}
The conversion process of other advice is the same
Above, all the advice is converted into interceptor, followed by the classic chain recursive call procedure.
The following process can be compared with the flow chart to read. After all, recursion is still a little complicated and requires some skills.
ReflectiveMethodInvocation#proceed
Public Object proceed () throws Throwable {
/ / the initial value of currentInterceptorIndex is-1
/ / when the number of currentInterceptorIndex equals advice minus one, the target method is called
/ / since the advice has been sorted, the calling order is before, after, afterReturn, afterThrowing
/ / Note that the advice method is not executed when the corresponding advice is called. Here, it is similar to a recursive call, and there will be a recursive procedure.
/ / some invocations are initiated on delivery, such as beforeAdvice, while others are initiated on return, such as afterAdvice
/ / the recursive termination condition is the following return invokeJoinpoint ()
If (this.currentInterceptorIndex = = this.interceptorsAndDynamicMethodMatchers.size ()-1) {
Return invokeJoinpoint ()
}
/ / currentInterceptorIndex is added and interceptor is obtained.
Object interceptorOrInterceptionAdvice =
This.interceptorsAndDynamicMethodMatchers.get (+ + this.currentInterceptorIndex)
/ / convert interceptro to MethodInterceptor to initiate a call
Return ((MethodInterceptor) interceptorOrInterceptionAdvice) .invoke (this)
}
At this point, the currentInterceptorIndex value is 0, and our advice is 4 (excluding the default), so when currentInterceptorIndex is 3, our actual method will be called.
The first call is MethodBeforeAdviceInterceptor
Public Object invoke (MethodInvocation mi) throws Throwable {
/ / call pre-notification
This.advice.before (mi.getMethod (), mi.getArguments (), mi.getThis ())
Return mi.proceed ()
}
Mi is the incoming this, and all mi.proceed () will go back to the original method.
Loop again, where the currentInterceptorIndex value is 1
The call is AspectJAfterAdvice
Public Object invoke (MethodInvocation mi) throws Throwable {
Try {
Return mi.proceed ()
}
Finally {
/ / finally means that it will be called anyway
InvokeAdviceMethod (getJoinPointMatch (), null, null)
}
}
Go ahead, the currentInterceptorIndex value is 2
The call is AfterReturningAdviceInterceptor
Public Object invoke (MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed ()
This.advice.afterReturning (retVal, mi.getMethod (), mi.getArguments (), mi.getThis ())
Return retVal
}
Go ahead, the currentInterceptorIndex value is 3
The call is AspectJAfterThrowingAdvice
Public Object invoke (MethodInvocation mi) throws Throwable {
Try {
Return mi.proceed ()
}
Catch (Throwable ex) {
If (shouldInvokeOnThrowing (ex)) {
/ / call exception notification
InvokeAdviceMethod (getJoinPointMatch (), null, ex)
}
/ / throw an exception
Throw ex
}
}
At this point, I believe you have a deeper understanding of "Spring source code analysis of how to AOP from parsing to calling". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.