网站自适应 常用尺寸,全国室内设计公司排行榜,网页设计与制作哪家公司好,广告设计网站排行榜前十名有哪些文章目录 前言一、scanCandidateComponents1.1 isCandidateComponent1.1.1、排除/包含过滤器1.1.2、条件装配1.1.3、重载一1.1.4、重载二1.1.5、补充#xff1a;Lookup注解 总结 前言 原生的Spring在构造ApplicationContext时#xff0c;会调用refresh方法。其中就包含了扫描… 文章目录 前言一、scanCandidateComponents1.1 isCandidateComponent1.1.1、排除/包含过滤器1.1.2、条件装配1.1.3、重载一1.1.4、重载二1.1.5、补充Lookup注解 总结 前言 原生的Spring在构造ApplicationContext时会调用refresh方法。其中就包含了扫描所有包含Component及其子注解的类注解模式或解析xml配置文件xml模式将其注册为BeanDefinition的逻辑。
一、scanCandidateComponents scanCandidateComponents是扫描指定路径下的类并且将符合要求的类进行解析注册成BeanDefinition的逻辑。 在该方法中
将传入的类路径进行格式转换。获取指定类路径下的所有.class文件。通过MetadataReader 解析.class的元数据信息。 获得类的元数据信息判断类上是否有相关注解的方式有两种第一是通过JVM的类加载第二是MetadataReader 。为什么Spring选择的是后者因为JVM的类是懒加载的如果在Spring启动时就将所有目标路径下的类全部通过JVM加载那么就违背了JVM类加载的机制。并且如果目标路径下的类很多对于性能也有一定的损失。而MetadataReader 使用的是ASM技术 最终。得到的是BeanDefinition对象而不是在JVM中加载.class文件。 /*** 参数需要扫描的类路径 例com.itbaima* 返回值BeanDefinition的集合**/private SetBeanDefinition scanCandidateComponents(String basePackage) {SetBeanDefinition candidates new LinkedHashSet();try {//将参数中的类路径进行转换 com.itbaima-classpath*:com/itbaima/**/*.classString packageSearchPath ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) / this.resourcePattern;//得到指定类路径下的所有资源文件类的.class文件 //例:file [D:\Idea_workspace\2024\springplus\target\classes\com\itbaima\AppConfig.class]Resource[] resources getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled logger.isTraceEnabled();boolean debugEnabled logger.isDebugEnabled();//遍历这些资源文件for (Resource resource : resources) {if (traceEnabled) {logger.trace(Scanning resource);}try {//通过MetadataReader 对某个.class文件的元数据进行解析MetadataReader metadataReader getMetadataReaderFactory().getMetadataReader(resource);//1.1 isCandidateComponentif (isCandidateComponent(metadataReader)) {//创建BeanDefinitionScannedGenericBeanDefinition sbd new ScannedGenericBeanDefinition(metadataReader);//设置BeanDefinition的source属性 file [D:\Idea_workspace\2024\springplus\target\classes\com\itbaima\AppConfig.class]sbd.setSource(resource);//再次进行判断对应的类是不是接口或抽象类(和上面的isCandidateComponent是重载的方法)if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug(Identified candidate component class: resource);}//将该BeanDefinition放入集合中candidates.add(sbd);}else {if (debugEnabled) {logger.debug(Ignored because not a concrete top-level class: resource);}}}else {if (traceEnabled) {logger.trace(Ignored because not matching any filter: resource);}}}catch (FileNotFoundException ex) {if (traceEnabled) {logger.trace(Ignored non-readable resource : ex.getMessage());}}catch (Throwable ex) {throw new BeanDefinitionStoreException(Failed to read candidate component class: resource, ex);}}}catch (IOException ex) {throw new BeanDefinitionStoreException(I/O failure during classpath scanning, ex);}return candidates;}1.1 isCandidateComponent 在ClassPathScanningCandidateComponentProvider中isCandidateComponent有两个第一个主要是用于判断类元信息中是否需要排除/包含注解 ExcludeFilter表示排除过滤器IncludeFilter表示包含过滤器
1.1.1、排除/包含过滤器 ExcludeFilter的作用被排除在外的类即使类上加入了Component及其子注解也不会被扫描到
Component
public class OrderService {
}Component
public class UserService {
}ComponentScan(value org.ragdollcat,excludeFilters {ComponentScan.Filter(type FilterType.ASSIGNABLE_TYPE,classes UserService.class)})
public class AppConfig {
}public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);System.out.println(context.getBean(orderService));System.out.println(context.getBean(userService));}
} IncludeFilter的作用被包含的类即使类上没有加入Component及其子注解也会被扫描到
ComponentScan(value org.ragdollcat,includeFilters {ComponentScan.Filter(type FilterType.ASSIGNABLE_TYPE,classes UserService.class)})
public class AppConfig {
}public class UserService {
} 1.1.2、条件装配 这里还有一个条件装配的概念我们可以自定义一个类实现Condition 接口重写matches方法自定义匹配的逻辑
Component
public class MyConditional implements Condition {public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return false;}
}并且在需要条件装配的类上加入Conditional注解
Component
Conditional(MyConditional.class)
public class UserService {
}
1.1.3、重载一 在isCandidateComponent方法中
判断判断类元信息中是否需要排除/包含注解。如果类元信息中需要包含某个注解能匹配的上如果还有Conditional注解则需要再次判断是否符合条件。 /*** 判断类元信息中是否需要排除/包含注解* 参数类元信息*/protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {//如果当前元数据中的注解 有符合需要排除的注解 则返回falsefor (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}//如果当前元数据中的注解 有符合包含的注解 则再次进入判断for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}/*** 主要用于判断条件装配**/private boolean isConditionMatch(MetadataReader metadataReader) {if (this.conditionEvaluator null) {this.conditionEvaluator new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);}return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());}public boolean shouldSkip(Nullable AnnotatedTypeMetadata metadata, Nullable ConfigurationPhase phase) {//元数据为空 或者类上没有加Conditional注解 无需判断 直接返回falseif (metadata null || !metadata.isAnnotated(Conditional.class.getName())) {return false;}if (phase null) {if (metadata instanceof AnnotationMetadata ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);}return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);}ListCondition conditions new ArrayList();//得到所有实现了Condition接口的类for (String[] conditionClasses : getConditionClasses(metadata)) {for (String conditionClass : conditionClasses) {//转换为Condition 对象Condition condition getCondition(conditionClass, this.context.getClassLoader());//加入到集合中conditions.add(condition);}}AnnotationAwareOrderComparator.sort(conditions);for (Condition condition : conditions) {ConfigurationPhase requiredPhase null;if (condition instanceof ConfigurationCondition) {requiredPhase ((ConfigurationCondition) condition).getConfigurationPhase();}//关键点调用自定义实现了Condition接口的类 的match方法 查看返回结果if ((requiredPhase null || requiredPhase phase) !condition.matches(this.context, metadata)) {//如果自定义实现了Condition接口的类 的match方法 返回的是false 则 这里返回的true 表示需要跳过加上了Condition注解的类的扫描return true;}}return false;}在进行匹配时调用的核心方法 Overrideprotected boolean matchSelf(MetadataReader metadataReader) {//首先获取类元数据上的注解信息AnnotationMetadata metadata metadataReader.getAnnotationMetadata();//返回判断的结果//1、类上有Component及其子注解 或 2、considerMetaAnnotations 为true 并且类上有Component及其子注解return metadata.hasAnnotation(this.annotationType.getName()) ||(this.considerMetaAnnotations metadata.hasMetaAnnotation(this.annotationType.getName()));}1.1.4、重载二 第二个isCandidateComponent方法主要是判断当前类是否是接口或者抽象类有一种特殊情况即该类是抽象类但是有Lookup注解也会被装配。 protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata beanDefinition.getMetadata();return (metadata.isIndependent() (metadata.isConcrete() ||(metadata.isAbstract() metadata.hasAnnotatedMethods(Lookup.class.getName()))));}default boolean isConcrete() {//如果是接口或者抽象类 则返回falsereturn !(isInterface() || isAbstract());}1.1.5、补充Lookup注解 如果某个单例bean中有个属性是多例的在初始化单例后每次获取到的属性的地址值都是一样的
Component
Scope(prototype)
public class User {
}Component
public class OrderService {Autowiredprivate User user;public void test(){System.out.println(user);}// Lookup(user)
// public User m1(){
// return null;
// }
}public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);OrderService orderService (OrderService) context.getBean(orderService);orderService.test();orderService.test();orderService.test();}} 如果要每次获取不同的属性可以使用Lookup注解实现
Component
public class OrderService {Autowiredprivate User user;public void test(){System.out.println(m1());}Lookup(user)public User m1(){return null;}
}总结 在Scan方法中主要做了
将传入参数的路径进行转换转换为classpath的格式。获取路径下的所有资源文件.class。通过MetadataReader 解析.class的元数据信息。判断被扫描到的类上是否存在Component及其子注解并且有无需要排除某个类的情况。还需要判断类上是否加入了Conditional注解。如果有调用Conditional注解value中的类的.matches方法判断是否需要跳过该类。将被扫描的类包装成BeanDefinition对象。再次判断被扫描的类是否是接口/抽象类。如果是则不将其创建为BeanDefinition。有加入了Lookup的抽象类的特殊情况。将BeanDefinition对象加入集合。