最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于spring5的那些事:@Indexed 解密

 更新時間:2021年11月09日 09:44:42   作者:布道  
這篇文章主要介紹了關(guān)于spring5的那些事:@Indexed 解密,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

隨著云原生的發(fā)展,很多技術(shù)會被重新掂量,重新定義,歷來技術(shù)的發(fā)展也是遵循天時地利,以其勢盡享其利。再云原生下,jdk的最大的問題在于笨重(幾百mb),啟動慢,而像Serverless架構(gòu),NodeJS技術(shù)??芍^更完美。

其實(shí)在jdk9中倡導(dǎo)模塊化本質(zhì)在于減少JVM的體積,不需要資源(Jar)不用再加載,而啟動慢的問題其實(shí)也有解決方案GraalVM (一款類似于HotSpot VM),它的先進(jìn)之處在于縮短運(yùn)行的成本將.java文件直接編譯成native code,而jvm則多了一個環(huán)節(jié),首先將.java文件編譯成字節(jié)碼(.class),再借助JVM運(yùn)行時JIT技術(shù)編譯成native code。

spring5.0開始支持@Indexed來提升進(jìn)應(yīng)用啟動速度,通過Annotation Processing Tools API在編譯時來構(gòu)建索引文件,本質(zhì)是通過靜態(tài)化來解決啟動時Bean掃描加載的時間長的問題。

what is Annotation Processing Tools API?

不是什么黑科技,之前的系列也講過,有點(diǎn)類似lombok。

哪些資源會被索引?

默認(rèn)支持標(biāo)記為Component及其派生注解(Controller、Repository、Service、Configuration等)的類,當(dāng)然也可以是非spring bean(@Indexed修飾的類)。

注:如果已經(jīng)是spring bean(Component修飾的類,并且Component已經(jīng)被標(biāo)記為@Indexed)了就沒必要再標(biāo)記@Indexed,否則索引文件會再追加一個相同的,感覺這是個bug

如何使用?

使用非常講的,添加依賴就可以了,install后默認(rèn)會生成一個META-INF/spring.components。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <optional>true</optional>
</dependency>
#spring.components
com.yh.rfe.lucky.day.service.impl.BasCostReportServiceImpl=org.springframework.stereotype.Component
com.yh.rfe.lucky.day.service.impl.BasShopRuleDetailServiceImpl=org.springframework.stereotype.Component

而CandidateComponentsIndexer負(fù)責(zé)對符合條件的注解生成索引文件,整個源碼也不是特別復(fù)雜,通過三個組件:StereotypesProvider、MetadataCollector、MetadataStore來完成。

public class CandidateComponentsIndexer implements Processor {
	@Override
	public synchronized void init(ProcessingEnvironment env) {
		this.stereotypesProviders = getStereotypesProviders(env);
		this.typeHelper = new TypeHelper(env);
		this.metadataStore = new MetadataStore(env);
		this.metadataCollector = new MetadataCollector(env, this.metadataStore.readMetadata());
	}
 
	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		this.metadataCollector.processing(roundEnv);
		roundEnv.getRootElements().forEach(this::processElement);
		if (roundEnv.processingOver()) {
			writeMetaData();
		}
		return false;
	}
}
//定義了哪些注解需要被索引
interface StereotypesProvider {
	/**
	 * Return the stereotypes that are present on the given {@link Element}.
	 * @param element the element to handle
	 * @return the stereotypes or an empty set if none were found
	 */
	Set<String> getStereotypes(Element element);
}
//獲取需要被索引的CandidateComponentsMetadata(元數(shù)據(jù))
class MetadataCollector {
	public CandidateComponentsMetadata getMetadata() {
		CandidateComponentsMetadata metadata = new CandidateComponentsMetadata();
		for (ItemMetadata item : this.metadataItems) {
			metadata.add(item);
		}
		if (this.previousMetadata != null) {
			List<ItemMetadata> items = this.previousMetadata.getItems();
			for (ItemMetadata item : items) {
				if (shouldBeMerged(item)) {
					metadata.add(item);
				}
			}
		}
		return metadata;
	}    
}
//將上面的結(jié)果輸出到spring.components中
class MetadataStore {
	static final String METADATA_PATH = "META-INF/spring.components";    
	public void writeMetadata(CandidateComponentsMetadata metadata) throws IOException {
		if (!metadata.getItems().isEmpty()) {
			try (OutputStream outputStream = createMetadataResource().openOutputStream()) {
				PropertiesMarshaller.write(metadata, outputStream);
			}
		}
	}    
}    

原理

其實(shí)在spring boot項目中絕對存在ComponentScan(在SpringBootApplication中),而傳統(tǒng)的spring項目中xml中對應(yīng)<context:component-scan>,通過指定的 package(路徑)來掃描注入spring bean,在掃描時通過讀取spring.components文件來讀取class(類全路徑)從而達(dá)到提升速度的目的。

CandidateComponentsIndex存儲了spring.components文件的內(nèi)容

public class CandidateComponentsIndex {
	private static final AntPathMatcher pathMatcher = new AntPathMatcher(".");
	private final MultiValueMap<String, Entry> index;
	/*返回指定的注解類型和包路徑相關(guān)候選類型
	* Set<String> candidates = index.getCandidateTypes("com.example", "org.springframework.stereotype.Component");
	*/
	public Set<String> getCandidateTypes(String basePackage, String stereotype) {
		List<Entry> candidates = this.index.get(stereotype);
		if (candidates != null) {
			return candidates.parallelStream()
					.filter(t -> t.match(basePackage))
					.map(t -> t.type)
					.collect(Collectors.toSet());
		}
		return Collections.emptySet();
	}    
}

CandidateComponentsIndexLoader從classloader中讀取,可以從多個jar中讀取多個索引文件。

public final class CandidateComponentsIndexLoader {
	public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";
	private static final ConcurrentMap<ClassLoader, CandidateComponentsIndex> cache =
			new ConcurrentReferenceHashMap<>();
	@Nullable
	public static CandidateComponentsIndex loadIndex(@Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = CandidateComponentsIndexLoader.class.getClassLoader();
		}
		return cache.computeIfAbsent(classLoaderToUse, CandidateComponentsIndexLoader::doLoadIndex);
	}
	@Nullable
	private static CandidateComponentsIndex doLoadIndex(ClassLoader classLoader) {
		if (shouldIgnoreIndex) {
			return null;
		}
		try {
			Enumeration<URL> urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
			if (!urls.hasMoreElements()) {
				return null;
			}
			List<Properties> result = new ArrayList<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
				result.add(properties);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + result.size() + "] index(es)");
			}
			int totalCount = result.stream().mapToInt(Properties::size).sum();
			return (totalCount > 0 ? new CandidateComponentsIndex(result) : null);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Unable to load indexes from location [" +
					COMPONENTS_RESOURCE_LOCATION + "]", ex);
		}
	}
}    

ClassPathBeanDefinitionScanner非常重要,它就是spring 中scan時干最臟最累的活的終結(jié)者。而ClassPathScanningCandidateComponentProvider非常重要可以視為scan的頂級實(shí)現(xiàn)類。

其中ClassPathMapperScanner是mybatis的mapper掃描類。

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
 
		doScan(basePackages);
 
		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}    
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//看這里吧
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
	private MetadataReaderFactory metadataReaderFactory;//這個之前講過類元數(shù)據(jù)讀取
	private CandidateComponentsIndex componentsIndex;//前面講過
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}
	private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			Set<String> types = new HashSet<>();
			for (TypeFilter filter : this.includeFilters) {
				String stereotype = extractStereotype(filter);
				if (stereotype == null) {
					throw new IllegalArgumentException("Failed to extract stereotype from "+ filter);
				}
				types.addAll(index.getCandidateTypes(basePackage, stereotype));
			}
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (String type : types) {
				MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type);
				if (isCandidateComponent(metadataReader)) {
					AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition(
							metadataReader.getAnnotationMetadata());
					if (isCandidateComponent(sbd)) {
						if (debugEnabled) {
							logger.debug("Using candidate component class from index: " + type);
						}
						candidates.add(sbd);
					}
					else {
						if (debugEnabled) {
							logger.debug("Ignored because not a concrete top-level class: " + type);
						}
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because matching an exclude filter: " + type);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}    
}

AnnotationConfigApplicationContext#scan你一定不陌生吧,這可是開發(fā)用戶級的API,其實(shí)它的scanner就是ClassPathBeanDefinitionScanner

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
	private final AnnotatedBeanDefinitionReader reader;
	private final ClassPathBeanDefinitionScanner scanner;
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}  
	public void register(Class<?>... annotatedClasses) {
		Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
		this.reader.register(annotatedClasses);
	}
}   

其實(shí)關(guān)于@Indexed個人覺得實(shí)現(xiàn)上還是有一定局限性(只是針對當(dāng)前maven的一個module,換言之是基于jar的),要基于當(dāng)前整個工程文件特別是org.springframework包(這個下面有很多待加載到ioc的bean的jar)工作量還是不少的,官方還沒考慮吧。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Docker 解決openjdk容器里無法使用JDK的jmap等命令問題

    Docker 解決openjdk容器里無法使用JDK的jmap等命令問題

    這篇文章主要介紹了Docker 解決openjdk容器里無法使用JDK的jmap等命令問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 解決springboot報錯Could not resolve placeholder‘xxx‘ in value“${XXXX}

    解決springboot報錯Could not resolve placeholder‘x

    這篇文章主要介紹了解決springboot報錯:Could not resolve placeholder ‘xxx‘ in value “${XXXX}問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • SpringBoot自定義轉(zhuǎn)換器應(yīng)用實(shí)例講解

    SpringBoot自定義轉(zhuǎn)換器應(yīng)用實(shí)例講解

    SpringBoot在響應(yīng)客戶端請求時,將提交的數(shù)據(jù)封裝成對象時,使用了內(nèi)置的轉(zhuǎn)換器,SpringBoot 也支持自定義轉(zhuǎn)換器,這個內(nèi)置轉(zhuǎn)換器在 debug的時候,可以看到,提供了124個內(nèi)置轉(zhuǎn)換器
    2022-08-08
  • Java 十大排序算法之計數(shù)排序刨析

    Java 十大排序算法之計數(shù)排序刨析

    計數(shù)排序是一個非基于比較的排序算法,該算法于1954年由 Harold H. Seward 提出。它的優(yōu)勢在于在對一定范圍內(nèi)的整數(shù)排序時,它的復(fù)雜度為Ο(n+k)(其中k是整數(shù)的范圍),快于任何比較排序算法
    2021-11-11
  • spring cloud 配置中心客戶端啟動遇到的問題

    spring cloud 配置中心客戶端啟動遇到的問題

    這篇文章主要介紹了spring cloud 配置中心客戶端啟動遇到的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法

    Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法

    這篇文章主要為大家介紹了Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟

    利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟

    這篇文章主要介紹了利用Java多線程技術(shù)導(dǎo)入數(shù)據(jù)到Elasticsearch的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • httpclient重定向之后獲取網(wǎng)址信息示例

    httpclient重定向之后獲取網(wǎng)址信息示例

    使用HttpClient進(jìn)行127.0.0.1:8080地址的問題,然后該地址自動重定向127.0.0.1:8080/mobserver,如何獲取該地址呢?使用HttpContext,下面是示例
    2014-02-02
  • java通過Idea遠(yuǎn)程一鍵部署springboot到Docker詳解

    java通過Idea遠(yuǎn)程一鍵部署springboot到Docker詳解

    這篇文章主要介紹了java通過Idea遠(yuǎn)程一鍵部署springboot到Docker詳解,Idea是Java開發(fā)利器,springboot是Java生態(tài)中最流行的微服務(wù)框架,docker是時下最火的容器技術(shù),那么它們結(jié)合在一起會產(chǎn)生什么化學(xué)反應(yīng)呢?的相關(guān)資料
    2019-06-06
  • Spring Boot 單元測試JUnit的實(shí)踐

    Spring Boot 單元測試JUnit的實(shí)踐

    JUnit是一款優(yōu)秀的開源Java單元測試框架,也是目前使用率最高最流行的測試框架,這篇文章主要介紹了Spring Boot 單元測試JUnit的實(shí)踐,感興趣的小伙伴們可以參考一下
    2018-11-11

最新評論

泰来县| 清镇市| 平遥县| 石首市| 垦利县| 阿荣旗| 金沙县| 大方县| 芦溪县| 宜城市| 利津县| 孟村| 布尔津县| 卢氏县| 鹰潭市| 托里县| 巧家县| 新河县| 大理市| 霍邱县| 金湖县| 乐都县| 涟源市| 嘉义县| 新乡县| 塔河县| 崇阳县| 昔阳县| 壤塘县| 铜山县| 蓝田县| 保靖县| 六枝特区| 北流市| 盱眙县| 桂林市| 武川县| 广丰县| 同德县| 沧源| 安顺市|