spring源码解析------@Import注解解析与ImportSelector,ImportBeanDefinitionRegistrar以及DeferredImportSelector区别

1.@Import注解在springBoot中间接的广泛应用

 在springboot中并没有直接显式的使用@Import标签,而是通过@Import标签来间接的提供了很多自动配置的注解。比如@EnableAutoConfiguration@EnableConfigurationProperties等。这些标签的实现都是通过使用@Import标签来完成的。

......
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	......
}
......
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
	......
}

 可以发现都是通过@Import来完成的。

2.spring中的@Import注解
2.1@Import注解的作用

@Import标签可以导入一个或者多个组件类,通常是@Configuration 注入的bean。提供了与xml中<import/>标签类型的功能,能够导入@Configuration类。

public @interface Import {

	/**
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();

}

 导入的类可以是@Configuration类型的配置类,实现了ImportSelector接口的类,实现了ImportBeanDefinitionRegistrar的类或者常规的组件类。

2.2@Import的解析前的处理

@Import处理的位置其实跟@Conditional注解都在同一个类中,处理的时机也是一样的。这里可以去看看@Conditional注解解析的逻辑

2.2.1 容器的刷新时候的准备

 在容器刷新方法就定义在AbstractApplicationContext类的refresh方法中。在这个里面会有解析注册以及实例话bean和其他的步骤,我们要看的就是解析跟注册步骤。

	public void refresh() throws BeansException, IllegalStateException {
	......
	invokeBeanFactoryPostProcessors(beanFactory);
	}

 在invokeBeanFactoryPostProcessors方法中会实例化所有的BeanFactoryPostProcessor类型的类并调用实现的postProcessBeanFactory方法。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	......
}
2.2.2 PostProcessorRegistrationDelegate处理BeanDefinitionRegistry以及BeanFactoryPostProcessor

 这里直接进入到invokeBeanFactoryPostProcessors方法。会有两种处理方式:

  1. 当前的beanFactoryBeanDefinitionRegistry类型的
    (1) 先处理beanFactory,然后按照是否实现了PriorityOrdered,Ordered以及两个都没有实现的顺序来处理BeanDefinitionRegistryPostProcessor接口的实现类。
    (2)处理实现了BeanFactoryPostProcessor接口的子类依次调用实现的postProcessBeanFactory方法
  2. 当前的beanFactory不是BeanDefinitionRegistry类型的,则直接处理实现了BeanFactoryPostProcessor接口的子类依次调用实现的postProcessBeanFactory方法

 其中会处理@Import标签的ConfigurationClassPostProcessor实现类BeanDefinitionRegistryPostProcessor接口跟PriorityOrdered接口间接实现了BeanFactoryPostProcessor接口因此无论怎么样都会被调用的。

	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		//如果当前的beanFactory是BeanDefinitionRegistry的,则需要将已经存在的beanDefinition进行注册
		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			//保存注册bean的BeanDefinitionRegistryPostProcessor
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
			//迭代beanFactoryPostProcessors如果是BeanDefinitionRegistryPostProcessor子类则加入到registryProcessors集合中
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					//处理beanFactory中的beanDefinitionNames
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
			//先处理同时实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的实现类
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				//如果当前的BeanDefinitionRegistryPostProcessor类的实现类也实现了PriorityOrdered类,则加入当当前需要注册的BeanDefinitionRegistryPostProcessor集合
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			//进行排序
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			//加入到registryProcessors集合
			registryProcessors.addAll(currentRegistryProcessors);
			//调用实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();
			......
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
	}else {
			// Invoke factory processors registered with the context instance.
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
	}
}
2.2.3 ConfigurationClassPostProcessorprocessConfigBeanDefinitionsprocessConfigBeanDefinitions解析bean

 上面提到了ConfigurationClassPostProcessor无论怎么样都会被调用。这里先看看实现的postProcessBeanDefinitionRegistry方法跟postProcessBeanFactory的共同点。

	//在refresh方法中最先被调用
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		//给当前的BeanDefinitionRegistry类型的beanFactory设置一个全局hash值,避免重复处理
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		//加入到已经处理的PostProcessed集合中
		this.registriesPostProcessed.add(registryId);
		//进行处理
		processConfigBeanDefinitions(registry);
	}

	/**
	 * Prepare the Configuration classes for servicing bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
	 */
	//在refresh方法中第二被调用
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		//给当前的ConfigurableListableBeanFactory类型的beanFactory设置一个全局hash值,避免重复处理
		int factoryId = System.identityHashCode(beanFactory);
		if (this.factoriesPostProcessed.contains(factoryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
		}
		//加入到已经处理的集合中
		this.factoriesPostProcessed.add(factoryId);
		//如果当前的beanFactory不在registriesPostProcessed中则进行处理
		if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then.
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}
		//将beanFactory中用Configuration注解配置的bean进行动态加强
		enhanceConfigurationClasses(beanFactory);
		//增加一个ImportAwareBeanPostProcessor
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

 发现一个共同点就是,如果没有解析过的beanFactory进来都会调用processConfigBeanDefinitions方法来处理Configuration类。在这个方法中有两个解析的位置。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//循环处理知道所有的bean都处理完,包含bean内部定义的bean
		do {
			//第一个解析的位置,这里是解析能够直接获取的候选配置bean。可能是Component,ComponentScan,Import,ImportResource或者有Bean注解的bean
			parser.parse(candidates);
			parser.validate();
			//获取上面封装已经解析过的配置bean的ConfigurationClass集合
			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			//移除前面已经处理过的
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			//第二个解析的位置,这里是加载configurationClasse中内部可能存在配置bean,比如方法上加了@Bean或者@Configuration标签的bean
			this.reader.loadBeanDefinitions(configClasses);
			......
			}
			......
}

 两个解析的位置分别是:

  1. 第一个位置是ConfigurationClassParser类的parse方法,解析能够直接从beanFactory中获取的候选bean。
  2. 第二个是ConfigurationClassBeanDefinitionReaderloadBeanDefinitions方法,解析从直接获取的候选bean中内部定义的bean。
2.2.4 ConfigurationClassParser的解析过程

ConfigurationClassParserparse有很多重载的方法,但是内部逻辑都是调用的processConfigurationClass方法。

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
			//解析bean
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}
		//处理DeferredImportSelector类型的实现类
		this.deferredImportSelectorHandler.process();
	}

	protected final void parse(@Nullable String className, String beanName) throws IOException {
		......
		processConfigurationClass(new ConfigurationClass(reader, beanName));
	}

	protected final void parse(Class<?> clazz, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(clazz, beanName));
	}

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}

 从上面可以看出来,主要要看的逻辑还是在processConfigurationClass方法中。这里还需要注意一点就是在最上面的parse方法最后有一个逻辑this.deferredImportSelectorHandler.process()这个逻辑是处理@Import注解中指定的引入类是DeferredImportSelectorHandler子类的情况的。
 进入到processConfigurationClass方法,关于这个方法在@Conditional注解解析的逻辑这个里面说到过。这里只需要关注内部的doProcessConfigurationClass方法的逻辑。

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		//检查当前解析的配置bean是否包含Conditional注解,如果不包含则不需要跳过
		// 如果包含了则进行match方法得到匹配结果,如果是符合的并且设置的配置解析策略是解析阶段不需要调过
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
		//从缓存中尝试获取当前配置bean解析之后的ConfigurationClass对象
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			//检查当前这个配置bean是通过@Import标签引入的还是自动注入到另外一个配置类bean里面的
			if (configClass.isImported()) {
				//如果是通过@Import标签引入则将当前解析的配置bean加入到已经存在的解析过的bean的用来保存通过@Import标签引入的bean的集合中
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
				//将当前解析的配置bean代替之前的
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// Recursively process the configuration class and its superclass hierarchy.
		//递归获取原始的配置类信息然后封装为SourceClass
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			//处理configClass
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
		//保存到configurationClasses中
		this.configurationClasses.put(configClass, configClass);
	}

 在doProcessConfigurationClass方法中有很多标签的处理逻辑,比如@Component@PropertySources@ComponentScans等。对于@Import注解的处理方式,封装在了另外的一个方法processImports里面。

	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {
				.......
						processImports(configClass, sourceClass, getImports(sourceClass), true);
				......
			}
2.3 @Import注解的解析
2.3.1 processImports方法

 已经知道@Import注解在那个方法里面进行解析了。先看看对应的方法参数的含义。

参数含义
ConfigurationClass configClass当前需要解析的Configuration类
SourceClass currentSourceClass类的源数据封装对象
Collection importCandidatesConfiguration类引入的其他类,以及其他类中引入的别的类,比如A引入B,B引入C,C引入D。那么这个就包含了B,C,D
boolean checkForCircularImports是否检查循环引入

 现在对方法内部进行分析

	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
		//检查包含有Import注解的集合是不是空的,空的则表示没有
		if (importCandidates.isEmpty()) {
			return;
		}
		//检查是否存在循环引入,通过deque的方式来检查
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			//将当前bean放到importStack中,用于检查循环引入
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					//import指定的Bean是ImportSelector类型
					if (candidate.isAssignable(ImportSelector.class)) {
						//实例化指定的ImportSelector子类
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);
						//如果是ImportSelector的子类DeferredImportSelector的子类则按照DeferredImportSelectorHandler逻辑进行处理
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {//如果不是则获取指定的需要引入的class的ClassNames
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							//根据ClassNames获取并封装成一个SourceClass
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							//继续调用processImports处理
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}//如果是ImportBeanDefinitionRegistrar的子类
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
						//如果对应的ImportBeanDefinitionRegistrar子类对象,并放到configClass,这个是用来注册额外的bean的
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {//不是上面任何类的子类就可以进行处理了,将指定的需要引入的bean转化为ConfigurationClass,然后到processConfigurationClass方法中处理
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

 主要的步骤如下:

  1. 检查解析出来的Import注解集合是不是空的,空的则不处理,直接返回
  2. 如果设置了需要检查循环依赖,则进行循环依赖的检查,不需要则跳过,进入3步骤
  3. 进行处理
    (1) 将当前bean放到importStack中,用于检查循环引入
    (2)循环处理以下情况
      1)如果是ImportSelector类型的子类。先实例化这个类,然后检查这个类是不是DeferredImportSelector子类,是的则调用ConfigurationClassParser的内部类DeferredImportSelectorHandlerhandle方法处理。如果不是DeferredImportSelector子类则继续调用processImports方法处理。按照步骤1从头开始。
      2)如果是ImportBeanDefinitionRegistrar类型的子类,则实例化这个类,然后放到当前解析的bean的importBeanDefinitionRegistrars属性中,后面注册bean时候调用。
      3)没有实现以上任何接口,则将当前的被引入的bean加入到这个bean的importedBy属性中,然后调用processConfigurationClass方法。

 对上面的信息进行总结。这个方法作用就是解析时候将被用@Import注解引入的bean加入到使用@Import注解标签的bean的importedBy中后面进行解析时候用,还有就是后面注册bean的时候可能也会调用实现了ImportBeanDefinitionRegistrar类型的子类。

2.3.2 processConfigurationClass方法处理configClass

 上面processImports方法调用之后最后都会返回到processConfigurationClass。这里介绍以下这个方法的作用。这个方法会对ConfigurationClass对象(这个对象封装了贴了@Configuration或者@Bean注解的对象的信息)进行解析,解析上面的注解。就我们上面提到的@Component@PropertySources@ComponentScans等。最后保存起来,后面实例化的时候会用到(后面实例化的时候使用的是CGLIB的方式实例化的),跟其他的bean不一样。

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		......
		do {
			//处理configClass
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
		//保存到configurationClasses中
		this.configurationClasses.put(configClass, configClass);
	}

 到这里@Import注解的解析基本就完了。剩下的就是介绍ImportSelectorImportBeanDefinitionRegistrar以及DeferredImportSelector之间的区别了。下篇文章解析。

3.ImportSelectorImportBeanDefinitionRegistrar以及DeferredImportSelector

 在上面的processImports方法中已经讲解了对所有的@Import注解中value值为不同指的情况进行解析。有以下的情况:

  1. ImportSelector接口的实现类
  2. DeferredImportSelector接口的实现类
  3. ImportBeanDefinitionRegistrar接口的实现类
  4. 非以上3中接口的实现类,也就是普通的类

 这里主要讲解1到3这三个接口类的区别。

3.1 ImportSelector
public interface ImportSelector {

	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

ImportSelector接口作用将方法返回的字符串数组作为bean注入到容器中,注意这里的字符串需要是对象的全路径名称比如A.class.getName()这种。后面会讲spring的扩展的时候会使用的。

3.2 ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar这个接口作用是,用户可以实现了之后来自定义来注册需要注册的bean。可以设置自定义的BeanNameGeneratorbean名称生成规则。

public interface ImportBeanDefinitionRegistrar {

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}
3.3 DeferredImportSelector

ImportBeanDefinitionRegistrarImportSelector的子接口,在4.0版本加入的。在这个类里面添加分组的功能,能够将多个DeferredImportSelector进行分组。同一个组内的ImportBeanDefinitionRegistrar能够通过实现@Order注解去实现排序。在调用的时候处理的时候会先按照序号进行排序,然后依次调用对应实现的 ImportSelector接口的selectImports方法。
 还有一点就是DeferredImportSelector的调用逻辑在,所有的@Configuration已经解析了之后在调用的。这点可以在2.2.4 ConfigurationClassParser的解析过程代码中看出来。

public interface DeferredImportSelector extends ImportSelector {

	default Class<? extends Group> getImportGroup() {
		return null;
	}

	interface Group {

	......

	}

 这里将区别列举出来

作用
ImportSelector将方法返回的字符串数组作为bean注入到容器中
ImportBeanDefinitionRegistrar自定义来注册bean
DeferredImportSelectorImportSelector差不多只不过多了分组的功能,处理在@Configuration类型bean解析之后
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cache 中,我们可以通过使用注解来声明缓存操作。例如,@Cacheable 注解用于表示方法的结果应该被缓存,@CacheEvict 注解用于表示缓存应该被清除,@CachePut 注解用于表示方法的结果应该被缓存,等等。这些注解都是在运行时被解析的,Spring 通过解析这些注解来确定缓存操作应该被执行的方式。 在 Spring 中,注解解析器是用于解析注解的组件。在 Spring Cache 中,注解解析器的作用是解析 @Cacheable、@CachePut、@CacheEvict 和 @Caching 注解,并将它们转换为缓存操作的定义。Spring Cache 提供了 AnnotationCacheOperationSource 类来实现这个功能。 AnnotationCacheOperationSource 类实现了 CacheOperationSource 接口,它的主要作用是解析注解并返回缓存操作的定义。在 Spring Cache 中,每个缓存操作的定义都表示为一个 CacheOperation 对象。AnnotationCacheOperationSource 类的 getCacheOperations() 方法返回一个 List<CacheOperation> 对象,表示指定方法上的所有缓存操作的定义。 AnnotationCacheOperationSource 类的核心方法是 buildCacheOperations() 方法。这个方法接收一个 MethodInvocation 对象,它描述了要执行的方法及其参数。buildCacheOperations() 方法首先检查给定的方法是否具有 @Cacheable、@CachePut、@CacheEvict 或 @Caching 注解。如果有这些注解,它将解析它们并返回一个包含缓存操作定义的 List<CacheOperation> 对象。如果没有这些注解,它将返回一个空的 List<CacheOperation> 对象。 解析注解的过程非常复杂,AnnotationCacheOperationSource 类使用了多个辅助类来完成这个过程。这些辅助类包括 AnnotationCacheAspect、CacheAnnotationParser、CacheAnnotationParserUtils 和 CacheAspectSupport。AnnotationCacheAspect 是一个切面,它拦截所有具有 @Cacheable、@CachePut、@CacheEvict 或 @Caching 注解的方法。CacheAnnotationParser 类用于解析注解并返回相应的缓存操作定义。CacheAnnotationParserUtils 类包含一些通用的注解解析方法。CacheAspectSupport 类是一个抽象类,它提供了一些通用的缓存操作方法,它的子类可以继承这些方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值