In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-21 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "how to understand the principle of JDK proxy implementation in AOP". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let the editor take you to learn "how to understand the principle of JDK proxy implementation in AOP"!
? Dynamic agent
There are two kinds of dynamic proxy technology in Spring AOP:
? Based on JDK native dynamic agent.
Provides a new class that creates a set of interfaces at run time. Because Java does not support instantiating interfaces, JDK generates a proxy class to implement a given interface at run time, and forwards the implementation logic to the call handler (Invocation handler) when the proxy class interface is called.
Classes that are dynamically proxied by JDK must implement interfaces
The proxy class is a java.lang.reflect.Proxy subclass, and the class name starts with $Proxy.
? Dynamic agent based on CGLIB.
CGLIB (Code Generation Library) is a class library based on ASM, a framework for manipulating Java bytecode.
In Spring AOP, if the proxied class (targetObject) does not implement the interface, that is, the proxy class cannot be generated through the dynamic proxy of JDK, then CGLIB will be selected to proxy.
The principle of CGLIB dynamic proxy: create a subclass of targetObject, override the methods that need the parent class, and enhance the functionality in the overridden methods.
Note that because of inheritance overrides, classes decorated by the final method cannot be proxied using CGLIB.
? Summary of premise
The JDK dynamic proxy requires the proxied class to implement the interface, and the aspect class needs to implement InvocationHandler.
CGLIB overrides the implementation aspect with the inheritance + method, and the rewriting method delegates the logic to MethodInterceptor#intercept.
CGLIB basically has no restrictions on proxy classes, but it is important to note that proxied classes cannot be modified by final modifiers and private, because Java cannot override the methods of the final class / private.
? The principle of AOP implementation of Spring? @ EnableAspectJAutoProxy
The @ EnableAspectJAutoProxy annotation is the flag that Spring AOP is enabled. By marking this annotation in the startup class, the corresponding aspect class logic can be loaded. The ElementType of this annotation is TYPE, which means that the tag is on the class.
It also declares that annotations are retained at run time with @ Retention (RetentionPolicy.RUNTIME). In addition, the most important thing is to use @ Import (AspectJAutoProxyRegistrar.class) to register AOP.
? Realization scheme
The @ EnableAspectJAutoProxy annotation registers the AspectJAutoProxyRegistrar class as the Bean of Spring through the @ Import method, so that it can be useful when the container parses the facet class. So what is the purpose of the AspectJAutoProxyRegistrar class?
@ Import (AspectJAutoProxyRegistrar.class)? ProxyTargetClass
@ EnableAspectJAutoProxy supports handling components marked with @ Aspect annotations of AspectJ, and users can proactively declare proxyTargetClass to specify which dynamic proxy method Spring AOP uses to create proxy classes (the default is JDK dynamic proxy based on the implementation interface).
Use CGLIB dynamic proxies to create proxy classes
Configuration @ EnableAspectJAutoProxy (proxyTargetClass=true) @ ComponentScan ("com.libo") public class AppConfig {/ /...}? ExposeProxy
In order to solve the problem of facet failure caused by proxy, Spring AOP introduced the AopContext class after Spring 4.3.1 to store the reference of the proxy class in ThreadLocal, and the proxy class of the current class can be quickly obtained through AopContext.
It is not supported by default, and if declared as true, you can use AopContext to get the proxy class, and in order to use AspectJ, you need to ensure that aspectjweaver exists in the current jar repository.
? AspectJAutoProxyRegistrar
Register AspectJAutoProxyRegistrar with @ Import, and in general, our startup class itself is a Bean,Spring that supports the use of @ Import to import a class without any Spring annotations to register the Java class as the Bean of Spring.
Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @ EnableAspectJAutoProxy annotation.
Register the AnnotationAwareAspectJAutoProxyCreator in the appropriate location based on the current BeanDefinitionRegistry.
? ImportBeanDefinitionRegistrar
Used to import some special BeanDefinition,Spring when dealing with @ Configuration, it will scan for classes imported through the @ Import tag, and execute the registerBeanDefinitions method for interfaces such as ImportBeanDefinitionRegistrar.
AspectJAutoProxyRegistrar: implements the ImportBeanDefinitionRegistrar interface for registering AspectJAnnotationAutoProxyCreator, an AspectJ automatic proxy creator that supports annotation-driven (and compatible with XML) parsing.
? AspectJAutoProxyRegistrar source code analysis class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {@ Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {/ / register AspectJAnnotationAutoProxyCreator AopConfigUtils with the container. RegisterAspectJAnnotationAutoProxyCreatorIfNecessary (registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor (importingClassMetadata, EnableAspectJAutoProxy.class) / / if the tag content if (enableAspectJAutoProxy! = null) {/ / proxyTargetClass is true on @ EnableAspectJAutoProxy, force AutoProxyCreator to use CGLIB as proxy if (enableAspectJAutoProxy.getBoolean ("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying (registry) } / / whether to enable the exposeProxy feature if (enableAspectJAutoProxy.getBoolean ("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy (registry);}
Register AspectJAnnotationAutoProxyCreator with the container
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary (registry)
Then parse the metadata on the @ EnableAspectJAutoProxy annotation to decide whether to turn on the proxyTargetClass and exposeProxy features we mentioned above.
To understand the execution link and timing of the registerBeanDefinitions method, we use IDE's debug to view the call stack analysis execution flow.
Org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass (configClass.getImportBeanDefinitionRegistrars ())
This line of code, we can roughly guess from the semantics of the code, this is to parse whether there is a class on the current configuration class that implements ImportBeanDefinitionRegistrar through @ Import import.
Eventually, the AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary method is called to register an AnnotationAwareAspectJAutoProxyCreator, which belongs to the core class of AOP.
? RegisterBeanDefinitions
Execute the AspectJAutoProxyRegistrar#registerBeanDefinitions method.
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary (registry)
Nullablepublic static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary (BeanDefinitionRegistry registry, @ Nullable Object source) {return registerOrEscalateApcAsRequired (AnnotationAwareAspectJAutoProxyCreator.class, registry, source);}? Find a section
As you can see from the above code, the AnnotationAwareAspectJAutoProxyCreator class, as the actual operator, looks at the inheritance diagram of the class. Omit some unnecessary classes.
First of all, look at the first interface, BeanPostProcessor, as the top-level interface, this interface will certainly not be called directly from the outside, so the high probability is that the following specific implementation classes are called, and then by determining whether it is the type of InstantiationAwareBeanPostProcessor interface, and then executing the corresponding logic, with this doubt, look at the source code.
? AbstractAutoProxyCreator
AbstractAutoProxyCreator implements the AOP function through postProcessAfterInitialization.
/ / manipulate the container object @ Override public Object postProcessAfterInitialization (@ Nullable Object bean, String beanName) throws BeansException {if (bean! = null) {Object cacheKey = getCacheKey (bean.getClass (), beanName); if (! this.earlyProxyReferences.contains (cacheKey)) {return wrapIfNecessary (bean, beanName, cacheKey) after instantiation }} return bean;} protected Object wrapIfNecessary (Object bean, String beanName, Object cacheKey) {/ / beanName is not empty and exists in targetSourcedBeans, that is, the custom / / TargetSource has been parsed by if (StringUtils.hasLength (beanName) & & this.targetSourcedBeans.contains (beanName)) {return bean } / / if Bean is advisedBeans, there is no need to be represented by if (Boolean.FALSE.equals (this.advisedBeans.get (cacheKey) {return bean } / / the function of isInfrastructureClass and shouldSkip: / / identify the aspect class, load the aspect class into advisors / / Why is it executed again because advisor if (isInfrastructureClass (bean.getClass ()) cannot be loaded in the case of circular dependency | | shouldSkip (bean.getClass (), beanName)) {this.advisedBeans.put (cacheKey, Boolean.FALSE); return bean } / / Create proxy if we have advice. / / return all Advice, Advisor, Interceptor Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean.getClass (), beanName, null) of the current Bean; if (specificInterceptors! = DO_NOT_PROXY) {this.advisedBeans.put (cacheKey, Boolean.TRUE) / / create a proxy corresponding to Bean. SingletonTargetSource is used to encapsulate the information of the implementation class Object proxy = createProxy (bean.getClass (), beanName, specificInterceptors, new SingletonTargetSource (bean)); this.proxyTypes.put (cacheKey, proxy.getClass ()); return proxy } / / next time the agent does not need to generate this.advisedBeans.put (cacheKey, Boolean.FALSE) repeatedly; return bean;}
Determine whether the current Bean exists in the cache or whether the current Bean has been proxied, then directly return bean.
Try loading advisor again to avoid incomplete advisor loading due to circular dependencies.
Gets the advisor array that the current bean conforms to.
Create a proxy class.
This article analyzes how the getAdvicesAndAdvisorsForBean method finds a matching advisor in all advisors.
? AbstractAdvisorAutoProxyCreator @ Override @ Nullable protected Object [] getAdvicesAndAdvisorsForBean (Class beanClass, String beanName, @ Nullable TargetSource targetSource) {List advisors = findEligibleAdvisors (beanClass, beanName); if (advisors.isEmpty ()) {return DO_NOT_PROXY;} return advisors.toArray () } / / findEligibleAdvisors is called here to find the appropriate advisors, and if the returned collection is empty, then / / finally returns null. / / if advisors is returned, its number is returned by histochemistry. Implementation of all Advisor in protected List findEligibleAdvisors (Class beanClass, String beanName) {/ / BeanFactory List candidateAdvisors = findCandidateAdvisors (); / / qualified Advisor List eligibleAdvisors = findAdvisorsThatCanApply (candidateAdvisors, beanClass, beanName); extendAdvisors (eligibleAdvisors); if (! eligibleAdvisors.isEmpty ()) {eligibleAdvisors = sortAdvisors (eligibleAdvisors) } return eligibleAdvisors;} / / first get the previously parsed advisors list-candidateAdvisors, where all the facet classes parsed into advisors.// find the current Bean matching advisor-findAdvisorsThatCanApply.// in candidateAdvisors and sort the obtained eligibleAdvisors. Protected List findCandidateAdvisors () {Assert.state (this.advisorRetrievalHelper! = null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans ();} protected List findAdvisorsThatCanApply (List candidateAdvisors, Class beanClass, String beanName) {ProxyCreationContext.setCurrentProxiedBeanName (beanName) Try {return AopUtils.findAdvisorsThatCanApply (candidateAdvisors, beanClass);} finally {ProxyCreationContext.setCurrentProxiedBeanName (null) }} public static List findAdvisorsThatCanApply (List candidateAdvisors, Class clazz) {if (candidateAdvisors.isEmpty ()) {return candidateAdvisors;} / / Store the final matching Advisor collection List eligibleAdvisors = new ArrayList () For (Advisor candidate: candidateAdvisors) {/ / whether the current advisor object implements the IntroductionAdvisor interface if (candidate instanceof IntroductionAdvisor & & canApply (candidate, clazz)) {eligibleAdvisors.add (candidate);}} boolean hasIntroductions =! eligibleAdvisors.isEmpty (); for (Advisor candidate: candidateAdvisors) {if (candidate instanceof IntroductionAdvisor) {/ / already processed continue } / / canApply- > determines whether the pointcut expression of the current advisor matches the current class if (canApply (candidate, clazz, hasIntroductions)) {eligibleAdvisors.add (candidate);}} return eligibleAdvisors;} / / finally AopUtils.findAdvisorsThatCanApply is called to filter the Advisors.???? that matches Bean About createProxy
There are bean to proxy and advisor on this Bean in the ProxyFactory object
What kind of agent does Bean use
When Bean implements the interface, Spring uses JDK's dynamic proxy. When Bean does not implement the interface, Spring will automatically use CGlib implementation, but only if the relevant dependencies of CGlib are imported into the project, otherwise Spring can only use JDK to proxy those classes that do not implement the interface, so that the generated proxy class will report an error. AopProxy has two implementation classes, JdkDynamicAopProxy and CglibAopProxy. It's all construction ReflectiveMethodInvocation.proceed ().
JdkDynamicAopProxy
Invocation = new ReflectiveMethodInvocation (proxy, target, method, args, targetClass, chain); retVal = invocation.proceed ()
CglibAopProxy
/ / CglibMethodInvocation inherits from ReflectiveMethodInvocationretVal = new CglibMethodInvocation (proxy, target, method, args, targetClass, chain, methodProxy) .proceed ()
ReflectiveMethodInvocation.proceed ()
Public Object proceed () throws Throwable {/ / when all interceptors are executed, call the target class's target method if (this.currentInterceptorIndex = = this.interceptorsAndDynamicMethodMatchers.size ()-1) {return invokeJoinpoint ();} Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get (+ + this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {/ / dynamic interceptor InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice If (dm.methodMatcher.matches (this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke (this);} else {/ / Dynamic matching failed. / / Skip this interceptor and invoke the next in the chain. Return proceed ();}} the implementation class of else {/ / MethodInterceptor still calls procee () after processing its own logic, and the this is passed in to achieve this purpose: return ((MethodInterceptor) interceptorOrInterceptionAdvice) .invoke (this);}}
Advisor
Advice
Joinpoint
? AnnotationAwareAspectJAutoProxyCreator
Spring is used to handle classes marked by @ AspectJ annotations in the application context. Continue to look at the registration process in the registerOrEscalateApcAsRequired method.
Org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequiredprivate static BeanDefinition registerOrEscalateApcAsRequired (Class cls, BeanDefinitionRegistry registry, @ Nullable Object source) {Assert.notNull (registry, "BeanDefinitionRegistry must not be null"); / / whether the current container contains org.springframework.aop.config.internalAutoProxyCreator if (registry.containsBeanDefinition (AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition (AUTO_PROXY_CREATOR_BEAN_NAME) If (! cls.getName (). Equals (apcDefinition.getBeanClassName () {int currentPriority = findPriorityForClass (apcDefinition.getBeanClassName ()); int requiredPriority = findPriorityForClass (cls); if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName (cls.getName ());} return null } / / package the incoming class as BeanDefinition, register it in the container, and / / optimize the execution order of its order. / / in aop, the class AnnotationAwareAspectJAutoProxyCreator.class will be registered here: RootBeanDefinition beanDefinition = new RootBeanDefinition (cls); beanDefinition.setSource (source); beanDefinition.getPropertyValues () .add ("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole (BeanDefinition.ROLE_INFRASTRUCTURE) Registry.registerBeanDefinition (AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition;}
First check to see if the current container contains
BeanDefiniton of org.springframework.aop.config.internalAutoProxyCreator.
If not, wrap the incoming class (where AnnotationAwareAspectJAutoProxyCreator.class is passed in) as RootBeanDefinition and register with the container.
Set up proxyTargetClass and exposeProxy
Let's take a look at how to set proxyTargetClass. In general, the logic of setting proxyTargetClass is the same as that of exposeProxy.
/ / if the tag content if (enableAspectJAutoProxy! = null) {/ / if proxyTargetClass is true, force AutoProxyCreator to use CGLIB to proxy if (enableAspectJAutoProxy.getBoolean ("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying (registry);} / / whether to enable the exposeProxy feature if (enableAspectJAutoProxy.getBoolean ("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy (registry);}} enter AopConfigUtils.forceAutoProxyCreatorToUseClassProxying (registry) AopConfigUtils#forceAutoProxyCreatorToUseClassProxyingpublic static void forceAutoProxyCreatorToUseClassProxying (BeanDefinitionRegistry registry) {/ / if the container contains org.springframework.aop.config.internalAutoProxyCreator if (registry.containsBeanDefinition (AUTO_PROXY_CREATOR_BEAN_NAME)) {/ / remove the BeanDefinition BeanDefinition definition of the org.springframework.aop.config.internalAutoProxyCreator = registry.getBeanDefinition (AUTO_PROXY_CREATOR_BEAN_NAME) / / set proxyTargetClass to true definition.getPropertyValues () .add ("proxyTargetClass", Boolean.TRUE);}}
If the container contains the name org.springframework.aop.config.internalAutoProxyCreator, take out the BeanDefinition and set the proxyTargetClass to true.
In order to be compatible with different BeanDefinition holding different attribute values, Spring abstracts them into MutablePropertyValues,definition.getPropertyValues (). Add ("proxyTargetClass", Boolean.TRUE), just like our usual set method in JavaBean.
A simple understanding of @ Import and ImportBeanDefinitionRegistrar, let's understand @ Import and ImportBeanDefinitionRegistrar through two use cases
Import classes through @ Import for Spring to manage
Public class NeedImportBean {public void doSomething () {Logger.getGlobal () .info ("Through @ Import registry to a bean");}}
Import NeedImportBean in the startup class
/ * * @ author jaymin * 20:13 on 2020-11-30 * / @ Configuration@ComponentScan (value = "com.xjm") @ Import (NeedImportBean.class) @ EnableAspectJAutoProxypublic class ApplicationConfig {public static AnnotationConfigApplicationContext getApplicationContext () {return new AnnotationConfigApplicationContext (ApplicationConfig.class);}} / a pair of NeedImportBean do getBeanpublic class BeanFactoryDemo {public static void main (String [] args) throws Exception {AnnotationConfigApplicationContext applicationContext = ApplicationConfig.getApplicationContext () NeedImportBean needImportBean = applicationContext.getBean (NeedImportBean.class);}}? Implement ImportBeanDefinitionRegistrar
NeedImportBean implements the ImportBeanDefinitionRegistrar interface, and then verifies two points:
Whether the registerBeanDefinitions method will be called back.
Whether NeedImportBean can be obtained through getBean
Public class NeedImportBean implements ImportBeanDefinitionRegistrar {@ Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Logger.getGlobal () .info ("Through implements ImportBeanDefinitionRegistrar and @ Import to callback me.");} public void doSomething () {Logger.getGlobal () .info ("Through @ Import registry to a bean");}}
Activate post processor ConfigurationClassPostProcessor in refresh to load metadata on @ Configuration
? Refresh
First, the container loads the refresh method.
Execute invokeBeanFactoryPostProcessors (beanFactory); activate the factory-level post processor.
Since the startup classes are marked with @ Configuration, Spring uses ConfigurationClassPostProcessor to parse the @ Configuration classes.
Use ConfigurationClassBeanDefinitionReader to load the configuration class and resolve it to BeanDefinition.
At this point, I believe you have a deeper understanding of "how to understand the principle of JDK proxy implementation in AOP". 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.