spring源码篇之demo1
2022/4/6 1:19:11
本文主要是介绍spring源码篇之demo1,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
public class SpringApplicaitonDemo1 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class); } }
这里会来加载非懒加载的单例bean到容器中来,那么如何来做到的?看一下构造方法:
public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); // 可以看到这里有具体的扫描,那么去具体的看一下里面的scanner方法 this.scanner = new ClassPathBeanDefinitionScanner(this); }
那么到这里的scan方法中来进行查看一下对应的方法:
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); this.doScan(basePackages); if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return this.registry.getBeanDefinitionCount() - beanCountAtScanStart; }
直接查看对应的doScan方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet(); String[] var3 = basePackages; int var4 = basePackages.length; for(int var5 = 0; var5 < var4; ++var5) { String basePackage = var3[var5]; // 找到所有的候选的componnet注解标注了的类!所以这里将会是第一个重点,是如何来查找到的? // 看到这里方法的返回值是所有的BeanDefinition的集合,那么这个是核心方法 Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage); Iterator var8 = candidates.iterator(); while(var8.hasNext()) { BeanDefinition candidate = (BeanDefinition)var8.next(); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate); } if (this.checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); this.registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
来查看一下,如何找到所有的候选的component注解标注的类:
public Set<BeanDefinition> findCandidateComponents(String basePackage) { return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : this.scanCandidateComponents(basePackage); }
只有两行代码,但是我们这里会来使用后面的方法,而前面的方法几乎不需要来进行查看了:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { LinkedHashSet candidates = new LinkedHashSet(); try { // 在这里首先将classpath*:来进行拼接,找到需要扫描的包在哪个文件之中来 // 对应的也就是:classpath*:com/guang/spring/**/*.class String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 然后利用getResources找到所有的class文件,通过class文件来进行操作 // 按照spring中的节奏,应该是利用ASM技术来对class文件来进行解析,按照字节码文件格式来对class文件来进行筛选 Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = this.logger.isTraceEnabled(); boolean debugEnabled = this.logger.isDebugEnabled(); Resource[] var7 = resources; int var8 = resources.length; // 使用for循环来进行操作 for(int var9 = 0; var9 < var8; ++var9) { Resource resource = var7[var9]; if (traceEnabled) { this.logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { // 看到了这个读取器来对文件来进行读取,也就是来解析class文件中的信息 MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource); // 在这里来检查是否是属于候选的 if (this.isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) { if (debugEnabled) { this.logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else if (debugEnabled) { this.logger.debug("Ignored because not a concrete top-level class: " + resource); } } else if (traceEnabled) { this.logger.trace("Ignored because not matching any filter: " + resource); } } catch (Throwable var13) { throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13); } } else if (traceEnabled) { this.logger.trace("Ignored because not readable: " + resource); } } return candidates; } catch (IOException var14) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14); } }
首先判断,存在着哪些需要进行排除的,哪些需要进行包含的字节码信息!这是只是来做相对应的判断信息而已。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 需要排除掉哪些类的bean定义信息 Iterator var2 = this.excludeFilters.iterator(); TypeFilter tf; do { if (!var2.hasNext()) { // 需要将哪些bean包含进来?那么这里又是如何来进行定义的? // 直接去看构造方法中的初始化方法查看: var2 = this.includeFilters.iterator(); do { if (!var2.hasNext()) { return false; } tf = (TypeFilter)var2.next(); } while(!tf.match(metadataReader, this.getMetadataReaderFactory())); // 程序走到了这里,如果返回的是true,那么将会在上面的if判断执行! return this.isConditionMatch(metadataReader); } tf = (TypeFilter)var2.next(); } while(!tf.match(metadataReader, this.getMetadataReaderFactory())); return false; }
在ClassPathBeanDefinitionScanner中的构造方法中默认注册了过滤器:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { this.beanDefinitionDefaults = new BeanDefinitionDefaults(); this.beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE; this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(); this.includeAnnotationConfig = true; Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; if (useDefaultFilters) { this.registerDefaultFilters(); } this.setEnvironment(environment); this.setResourceLoader(resourceLoader); }
接下向下看,默认是以Component注解来进行添加的!那么也就是说查找bean的时候,只需要查找到类上有@Component的即可,接下来将会来进行匹配:
protected void registerDefaultFilters() { // 默认是一这个注解来进行表示的! this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false)); this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException var4) { } try { this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false)); this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException var3) { } }
看下上面的条件匹配条件,匹配条件无非是类上有没有@Component注解信息,但是应该spring支持条件匹配,所以接下来还需要支持条件匹配:
private boolean isConditionMatch(MetadataReader metadataReader) { if (this.conditionEvaluator == null) { this.conditionEvaluator = new ConditionEvaluator(this.getRegistry(), this.environment, this.resourcePatternResolver); } // 如果类上没有Conditional注解,那么就不要跳过,也就是说需要来进行扫描进行加载的。 return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata()); }
那么也就是说判断完成Conditional注解之后,还需要来判断是否有的对应的@Conditional注解注解
所以下面来看一下案例:
public class User { } public class UserCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { try { Thread.currentThread().getContextClassLoader().loadClass("com.guang.scan.user.bean.User"); return true; } catch (ClassNotFoundException e) { e.printStackTrace(); } return false; } } @Service @Conditional(value = {UserCondition.class}) public class UserService { public void test(){ System.out.println("test"); } }
然后运行测试,发现是可以的。但是如果没有User类的出现,那么就会抛出异常,返回false,那么就会导致userService没有这个bean了,下面来测试一下:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userService' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:863) at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1344) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1160) at com.guang.scan.SpringApplicaitonDemo1.main(SpringApplicaitonDemo1.java:15)
在当期环境中找不到User,导致类加载器加载不到该类,从而导致@Conditional返回的是false,所以无法加载到当前的程序中:
那么判断成功之后,继续执行:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { LinkedHashSet candidates = new LinkedHashSet(); try { String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = this.logger.isTraceEnabled(); boolean debugEnabled = this.logger.isDebugEnabled(); Resource[] var7 = resources; int var8 = resources.length; for(int var9 = 0; var9 < var8; ++var9) { Resource resource = var7[var9]; if (traceEnabled) { this.logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource); if (this.isCandidateComponent(metadataReader)) { // 创建对象,这里有一个步骤需要注意: ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); // 接下来又来了一个判断!上面判断过了,这里又要来进行判断 if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) { if (debugEnabled) { this.logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else if (debugEnabled) { this.logger.debug("Ignored because not a concrete top-level class: " + resource); } } else if (traceEnabled) { this.logger.trace("Ignored because not matching any filter: " + resource); } } catch (Throwable var13) { throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13); } } else if (traceEnabled) { this.logger.trace("Ignored because not readable: " + resource); } } return candidates; } catch (IOException var14) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14); } }
需要注意的步骤:
public ScannedGenericBeanDefinition(MetadataReader metadataReader) { Assert.notNull(metadataReader, "MetadataReader must not be null"); this.metadata = metadataReader.getAnnotationMetadata(); this.setBeanClassName(this.metadata.getClassName()); this.setResource(metadataReader.getResource()); } // 也就是这里!这里的beanClass是一个Object数据类型,但是此时此刻设置进去的是当前bean的名称 // 而不是真正的bean对象 public void setBeanClassName(@Nullable String beanClassName) { this.beanClass = beanClassName; }
主要是因为当前还只是加载阶段,也就是生成对应的beandifinition阶段,还没有到初始化对象阶段。
真正创建bean的时候,才需要去加载得到这个类。也就是分为了两步执行:1、先是名字;2、再是bean的实例
看一下判断的条件:
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { AnnotationMetadata metadata = beanDefinition.getMetadata(); // 1、只有顶级类和静态内部类才是独立的以及不是接口或者是抽象类 // 通常写的类都是顶级类直接加上@Component注解(内部类的很少见) // 2、类是抽象类,但是类中的方法有lookup注解; return metadata.isIndependent() && (metadata.isConcrete() || metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())); }
判断类是不是需要依赖其他的类并且是抽象的;或者类是抽象的,但是方法有LoocUp注解
也来个例子来进行说明:
@Component @Scope(value = "prototype") public class User { } @Service public class UserService { @Autowired private User user; public void test(){ System.out.println(user); } } public class SpringApplicaitonDemo1 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class); UserService userService = context.getBean("userService", UserService.class); userService.test(); userService.test(); userService.test(); userService.test(); } }
即使user是多例的,但是因为userservice是单例的,只会被注入一次。所以打印出来的是单例的。那么这里想要看到的效果是多例的,又将如何来做到?
只需要多谢一个方法,加载一个注解,携带对象的名称即可,如下所示:
@Service public class UserService { @Autowired private User user; public void test(){ System.out.println(getUser()); } @Lookup(value = "user") public User getUser(){ return null; } }
再看下控制台的输出信息可以看到,每次打印的bean都是不同的。
执行完上面的方法之后,也就是对生成后的class文件进行扫描之后,将一些不符合条件的进行剔除(如:是否有@Component注解、是有有conditional注解、是否是接口或者是抽象类)等,只有满足了对应的条件,才会生成对应的beandefinition。
但是代码执行到了这里,是否是懒加载的、作用域都没有,只是设置了个beanName而已
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet(); String[] var3 = basePackages; int var4 = basePackages.length; for(int var5 = 0; var5 < var4; ++var5) { String basePackage = var3[var5]; Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage); Iterator var8 = candidates.iterator(); while(var8.hasNext()) { // 代码走到了这里之后,才会对这里的BeanDefinition再做一次筛选 BeanDefinition candidate = (BeanDefinition)var8.next(); // 获取得到BeanDefinition的作用于范围 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 获取得到BeanDefinition的名称 // 如果component上面有value值指定,那么直接获取;如果没有,根据类名首字母小写来获取 // 如果类名是ABc,那么bean的name还会是ABc String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 给BeanDefinition设置一些默认的值 if (candidate instanceof AbstractBeanDefinition) { this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName); } // 然后对@Lazy、@Primary、@DepentOn、@Role以及@Description注解的解析 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate); } // 检查当前容器中是否已经存在了beanName了 if (this.checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 将BeanDefinition注册到registry中来 this.registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
检查到容器中已经有了对应的名称的bean了,看下检查逻辑:
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { if (!this.registry.containsBeanDefinition(beanName)) { return true; } else { BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } // 是否兼容! if (this.isCompatible(beanDefinition, existingDef)) { return false; } else { throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); } } }
我们大多数情况下遇到的都是可以兼容的情况,如果是不兼容的,可以抛出异常,也就是返回FALSE(不会注册到bean容器中,说明容器中还是只会有一个bean),表示容器中有两个相同类名的bean了。
可以看下如何判断兼容的:
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) { return !(existingDefinition instanceof ScannedGenericBeanDefinition) || newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource()) || newDefinition.equals(existingDefinition); }
多创建两个相同名称的bean:
@ComponentScan(basePackages = "com.guang.scan") public class ScanConfig { } @ComponentScan(basePackages = "com.guang.scan") public class ScanConfig1 { }
看一下启动类:
public class SpringApplicaitonDemo1 { public static void main(String[] args) { // AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(ScanConfig.class); context.refresh(); AnnotationConfigApplicationContext context1 = new AnnotationConfigApplicationContext(); context1.register(ScanConfig.class); context1.refresh(); UserService userService = context.getBean("userService", UserService.class); userService.test(); } }
可以看到这里注册了两次,但是并不影响后续操作。
这篇关于spring源码篇之demo1的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南