Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Spring source code analysis how to AOP from parsing to calling

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report