@EnableAspectJAutoProxy

The @enableAspectJAutoProxy annotation is a sign that Spring AOP is turned on. Mark this annotation in the startup class to load the corresponding aspect class logic. The ElementType of this annotation is TYPE, indicating that the tag is on the class. Annotations are also declared to be retained at RUNTIME with @retention (retentionPolicy.runtime). In addition the most important thing is to use the @ Import (AspectJAutoProxyRegistrar. Class) to register the AOP

proxyTargetClass

@enableAspectJAutoProxy supports handling components marked with AspectJ’s @Aspect notation, and users can proactively declare proxyTargetClass to specify Spring Which dynamic proxy method is used by AOP 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.foo")
   public class AppConfig {
       // ...
   }
Copy the code

exposeProxy

To address some of the aspect failures caused by proxies, Spring AOP introduced the AopContext class after Spring 4.3.1 to store references to proxy classes in ThreadLocal. AopContext can be used to quickly retrieve the proxy class of the current class. The default is not supported. If true, AopContext is used to obtain the proxy class.

Also, in order to use AspectJ, you need to ensure that AspectJWeaver exists in the current JAR repository.

Through @ AspectJAutoProxyRegistrar Import registration

Typically, our startup class is also a Bean itself, Spring supports using @import to Import a class that is not marked with any Spring annotations to register that Java class as a Spring Bean. It is through the @ @ EnableAspectJAutoProxy annotation will Import way AspectJAutoProxyRegistrar class registered into a Spring Bean, analytical aspects so that the container class come in handy. So what is the AspectJAutoProxyRegistrar class role? From JavaDoc we can see the following sentence:

Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @EnableAspectJAutoProxy annotation. Based on the current BeanDefinitionRegistry AnnotationAwareAspectJAutoProxyCreator registrations in place.

UML

  • ImportBeanDefinitionRegistrar:It’s used to import some special BeanDefinitions that Spring is handling@Configuration“, will scan to see if there is a pass@ImportTag import class, rightImportBeanDefinitionRegistrarThis type of interface will also be implementedregisterBeanDefinitionsMethods.
  • AspectJAutoProxyRegistrar:To achieve theImportBeanDefinitionRegistrarInterface for registrationAspectJAnnotationAutoProxyCreator, AspectJ automatic proxy creator that supports annotation-driven (and XML-compliant) parsing.

AspectJAutoProxyRegistrar

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		/ / registered AspectJAnnotationAutoProxyCreator with the container
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		// If @enableAspectJAutoProxy has tag content
		if(enableAspectJAutoProxy ! =null) {
			// If proxyTargetClass is true, AutoProxyCreator is forced to use CGLIB as a proxy
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			// Whether to enable exposeProxy
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}}Copy the code

This key is through AopConfigUtils. RegisterAspectJAnnotationAutoProxyCreatorIfNecessary (registry); Registered AspectJAnnotationAutoProxyCreator with the container. The metadata on the @EnableAspectJAutoProxy annotation is then parsed to determine whether to enable the proxyTargetClass and exposeProxy features mentioned above. To understand the execution link and call timing of the registerBeanDefinitions method, we use IDE debug to look at the call stack analysis execution flow.

  • Refresh the activation rear processor ConfigurationClassPostProcessor loaded @ the Configuration on the metadata

  1. First, the container loads the Refresh method.
  2. performinvokeBeanFactoryPostProcessors(beanFactory);Activate the factory-level back processor.
  3. Since the startup classes are all by@ConfigurationThat Spring will useConfigurationClassPostProcessorTo resolve be@ConfigurationIn the class.
  4. useConfigurationClassBeanDefinitionReaderTo load a configuration class that resolves to BeanDefinition.
  5. inorg.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClassIs executed loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());This line of code, as we can roughly guess from the semantics of the code, is resolving whether there is a pass on the current configuration class@ImportThe import is implementedImportBeanDefinitionRegistrarIn the class,

  1. Correction of the registrarregisterBeanDefinitionsMethods.
  2. performAspectJAutoProxyRegistrar#registerBeanDefinitionsMethods.
  • AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

Use the IDE to jump into a AopConfigUtils. RegisterAspectJAnnotationAutoProxyCreatorIfNecessary (registry); 2 floors, jump down to AopConfigUtils# registerAspectJAnnotationAutoProxyCreatorIfNecessary method.

  • org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(org.springframework.b eans.factory.support.BeanDefinitionRegistry, java.lang.Object)
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
		BeanDefinitionRegistry registry, @Nullable Object source) {

	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
Copy the code

Here we found a new role – AnnotationAwareAspectJAutoProxyCreator, is the Spring used to handle application context is @aspectj annotation marks of a class. Continue to look at the registration process into registerOrEscalateApcAsRequired method.

  • org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
private static BeanDefinition registerOrEscalateApcAsRequired( Class<? > cls, BeanDefinitionRegistry registry,@Nullable Object source) {

	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	/ / 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;
	}
	// Wrap the incoming class as a BeanDefinition, register it in the container, and optimize its order
	/ / in aop, here will be registered AnnotationAwareAspectJAutoProxyCreator. Class - this class
	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;
}
Copy the code

The logic here is clear. First check to see if the current container contains org. Springframework. Aop. Config. InternalAutoProxyCreator BeanDefiniton. If not, will be introduced to the class (introduced into AnnotationAwareAspectJAutoProxyCreator here. The class) packaged into RootBeanDefinition, then register into the container.

  • Set proxyTargetClass and exposeProxy

This is also a little bit more clear, so I’ll talk about it briefly. Let’s look at how to set proxyTargetClass. In general, setting proxyTargetClass is the same logic as exposeProxy.

// If @enableAspectJAutoProxy has tag content
if(enableAspectJAutoProxy ! =null) {
	// If proxyTargetClass is true, AutoProxyCreator is forced to use CGLIB as a proxy
	if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
		AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
	}
	// Whether to enable exposeProxy
	if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}Copy the code

Enter the AopConfigUtils. ForceAutoProxyCreatorToUseClassProxying (registry);

  • AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {  
	/ / if the container contains org. Springframework. Aop. Config. InternalAutoProxyCreator
	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
		/ / remove the org. Springframework. Aop. Config. InternalAutoProxyCreator BeanDefinition
		BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
		// Set proxyTargetClass to true
		definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); }}Copy the code

If the container called org. Springframework. Aop. Config. InternalAutoProxyCreator BeanDefinition, then take out the BeanDefinition, Set proxyTargetClass to true. By the way, Spring holds different property values for compatibility with different BeanDefinitions, Them are abstracted to MutablePropertyValues, so definition. GetPropertyValues (). The add (” proxyTargetClass “, Boolean TRUE); Just like our normal JavaBean set method is the same.

Simple understanding @ Import and ImportBeanDefinitionRegistrar

Below we through two cases to understand @ Import and ImportBeanDefinitionRegistrar

Import the class via @import and let Spring manage it

public class NeedImportBean {

	public void doSomething(a){
		Logger.getGlobal().info("Through @Import registry to a bean "); }}Copy the code
  • Import NeedImportBean in the startup class
/ * * *@author jaymin
 * 2020/11/30 20:13
 */
@Configuration
@ComponentScan(value = "com.xjm")
@Import(NeedImportBean.class)
@EnableAspectJAutoProxy
public class ApplicationConfig {
	public static AnnotationConfigApplicationContext getApplicationContext(a) {
		return newAnnotationConfigApplicationContext(ApplicationConfig.class); }}Copy the code
  • To do getBean NeedImportBean
public class BeanFactoryDemo {
	public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext applicationContext = ApplicationConfig.getApplicationContext(); NeedImportBean needImportBean = applicationContext.getBean(NeedImportBean.class); }}Copy the code
  • The output

As you can see, classes that are not marked by Spring annotations can be registered with @import and then used as normal beans.

Implement ImportBeanDefinitionRegistrar

Now we are in a way, let NeedImportBean ImportBeanDefinitionRegistrar interface, and then verify two points:

  1. Whether the registerBeanDefinitions method is called back.
  2. Whether NeedImportBean is available via 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(a){
		Logger.getGlobal().info("Through @Import registry to a bean "); }}Copy the code
  • Result

The test method is the same as above. I won’t repeat it here.

Here, we verify:

  1. After implements ImportBeanDefinitionRegistrar interface can implement the callback
  2. After implements ImportBeanDefinitionRegistrar interface, through@ImportIt does not register as a Bean.

Further reading

In the process of search for information, found this article comparing ImportBeanDefinitionRegistrar parsing thoroughly. If you are interested, you can read: click on me to go

conclusion