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的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程