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();
}
}
可以看到这里注册了两次,但是并不影响后续操作。
标签:resource,candidate,spring,demo1,class,bean,源码,new,public 来源: https://www.cnblogs.com/likeguang/p/16104693.html