【spring】启动和扫描逻辑实现-01

1.spring 启动和扫描逻辑实现

1.spring 启动和扫描逻辑实现

在spring 容器启动的过程中,会扫描指定包路径下的class 文件,判断当前类是否是一个bean 对象,如果是一个bean对象,将其注入到spring 容器中。

基础配置类

AppConfig 配置类的信息

@ComponentScan("com.zhouyu.service")
public class AppConfig {

}

ComponentScan

ComponentScan——主要定义包扫描路径——spring在容器启动的过程中,会通过读取配置类AppConfig类的class 对象获取 @ComponentScant定义的扫描路径

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
    String value();
}

Component

表示这个类是一个bean

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
    String value() default "";
}

scope

定义Bean的作用域,是单例singleton还是原型 prototype

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {

    String value();
}

包扫描逻辑

新建 ZhouyuApplicationContext.java,作为spring容器的模拟实现

启动类

public class Test {
    public static void main(String[] args) throws Exception {
        ZhouyuApplicationContext applicationContext = new ZhouyuApplicationContext(AppConfig.class);

        Object userUservice = applicationContext.getBean("userService");
        Object userUservice1 = applicationContext.getBean("userService");

        System.out.println(userUservice);
        System.out.println(userUservice1);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T2UibU9Q-1658677523596)(…/学相伴笔记/04-SpringBoot/assets/微信截图_20220724103655.png)]

ZhouyuApplicationContext

  • Class configClass 属性
  • 带Class 参数的构造方法

在spring 启动类上通过传入一个Config配置类,作为Spring启动类的配置信息

scan——方法完成了包扫描逻辑,通过传入的AppConfig类的class对象,我们可以读取到AppConfig类上的@ComponentScan注解,其中定义了包扫描路径

// 解析配置类
// ComponentScan注解 --> 扫描路径 --> 扫描
ComponentScan annotations = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = annotations.value();// 扫描路径 com.zhouyu.service

在Java中,需要加class加载到JVM中,不同的类是由不同的类加载器去完成加载的,Bootstrap类加载器加载jre/lib目录下的类,Ext类加载器加载jre/ext/lib目录下的类,App类加载器加载classpath目录下的类。其中classpath就是我们的项目路径。

 private void scan(Class configClass) throws ClassNotFoundException {
        // 解析配置类
        // ComponentScan注解 --> 扫描路径 --> 扫描
        ComponentScan annotations = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = annotations.value();// 扫描路径 com.zhouyu.service

        //扫描
        // Bootstrap --> jre/lib
        // Ext ------> jre/ext/lib
        // App ------> classpath
        ClassLoader classLoader = ZhouyuApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource("com/zhouyu/service");
        File file = new File(resource.getFile());

        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(File f: files){
                //D:\2022-04-22-program\zhouYu\spring01\target\classes\com\zhouyu\service\UserService.class -> com.zhouyu.service.UserService
                String fileName = f.getAbsolutePath();
                if(fileName.endsWith(".class")){
                    String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                    className = className.replace("\\",".");

                    Class<?> clazz = classLoader.loadClass(className);
                    if(clazz.isAnnotationPresent(Component.class)){
                        // 表示当前这个类是一个Bean
                        // 解析类,判断当前bean是单例bean 还是prototype原型bean
                        // BeanDefinition
                        Component componentAnnotaion = clazz.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotaion.value();

                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setClazz(clazz);
                        // 判断是不是单例模式
                        if(clazz.isAnnotationPresent(Scope.class)){
                            Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                            beanDefinition.setScope(scopeAnnotation.value());
                        }else {
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                }

            }
        }
    }

BeanDefinition

对Bean 信息的定义

描述一个bean 的全部信息,比如 class 类型,bean 的作用域,是否懒加载

常用属性

  • beanClass:表示Bean类型,未加载类的时候存放Bean的名字,加载类后存放Bean的class信息。
  • scope:表示Bean的作用域,一般值为单例或者原型。
  • lazyInit:表示Bean是否是懒加载。
  • initMethodName:Bean初始化需要执行的方法。
  • destroyMethodName:Bean销毁时要执行的方法。
  • factoryBeanName:创建当前Bean的工厂。

BeanDefinition是如何存放的?

  • private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    当Bean注册完成后,会在spring容器中把扫描到的beanDefinition存放到beanDefinitionMap中,方便后续的使用。

单例池 singletonObjects

spring中定义了单例池存放单例bean,在spring扫描逻辑scan方法执行完之后,spring会将非懒加载的单例bean注入到spring容器中

// 单例池
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();



getBean

单例直接从单例池获取

多列通过createBean方法创建bean

public Object getBean(String beanName) throws Exception{
        if(beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if(beanDefinition.getScope().equals("singleton")){
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                // 创建 bean
                Object bean = createBean(beanDefinition);
                return bean;
            }
        }else {
            throw new NullPointerException();
        }
    }

参考

  1. spring
  2. 视频
阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring通过扫描指定的包路径来查找bean定义,并将它们注册到应用程序上下文中。Spring使用`@ComponentScan`注解来指定需要扫描的包路径。当使用`@ComponentScan`注解时,Spring扫描所有被`@Component`、`@Service`、`@Repository`和`@Controller`注解的类,并将它们注册为bean。 例如,以下代码演示了如何在Spring中使用`@ComponentScan`注解扫描名为`com.example`的包下的所有bean定义: ```java @Configuration @ComponentScan("com.example") public class AppConfig { // ... } ``` 除了`@ComponentScan`注解外,还可以使用`@Bean`注解手动注册bean,或者使用XML配置文件来定义bean。 ### 回答2: Spring框架通过Bean扫描机制可以自动检测和加载应用程序中的Bean。 Spring会在应用程序的类路径上扫描指定的包或类,寻找被Spring管理的Bean。这个过程通常在应用程序启动时发生,Spring会搜索并加载符合条件的类,并将其注册为Bean定义。Spring框架提供了几种方式来配置Bean的扫描路径。 首先,可以通过使用@ComponentScan注解来开启Bean的自动扫描。在配置类上加上@ComponentScan注解,并指定需要扫描的包路径,Spring框架将会递归地扫描这些包,查找被@Component、@Service、@Repository、@Controller等注解修饰的类,并将其注册为Bean。 其次,可以使用XML配置文件来进行扫描Bean。在配置文件中使用<context:component-scan>元素,并设置base-package属性为需要扫描的包路径,Spring框架会自动扫描该包下所有被标记的类,并加载注册为Bean。 除此之外,还可以使用基于Java的配置方式,通过编写配置类来定义Bean的扫描路径。在配置类上使用@Configuration和@ComponentScan注解,指定需要扫描的包路径,Spring框架会自动搜索并加载满足条件的类。 总之,无论是通过注解还是XML配置文件,Spring框架可以通过不同的方式来扫描Bean,从而实现自动加载和注册。这样可以避免手动配置每个Bean的信息,提高开发效率,使应用程序更加灵活和可扩展。 ### 回答3: Spring框架通过使用不同的机制来扫描和注册bean。 首先,Spring可以通过基于XML的配置文件来扫描bean。在XML配置文件中,我们可以使用`<bean>`元素来定义一个bean,并通过`id`或`name`属性来标识它。通过在`<bean>`元素中配置适当的信息,如类的全限定名、构造函数参数和属性值,Spring可以实例化和管理这些bean。然后,通过在XML配置文件中使用`<context:component-scan>`元素,可以使Spring自动扫描指定路径下的所有带有特定注解的类,并将它们注册为bean。 其次,Spring还可以通过基于注解的方式来扫描bean。通过在类上使用`@Component`注解或其派生注解(如`@Controller`、`@Service`、`@Repository`等),我们可以指示Spring将该类注册为一个bean。同时,通过使用`@Autowired`注解,我们可以将其他依赖的bean自动注入到当前bean中,实现依赖注入的功能。在启动Spring应用程序时,Spring会自动扫描指定路径下的所有类,并将被注解标记的类注册为bean。 最后,Spring还支持自定义扫描和注册bean的机制。我们可以实现`BeanDefinitionRegistryPostProcessor`接口来编写自定义的bean扫描器,通过编程方式将类注册为bean。我们可以根据自己的需求定义扫描路径、过滤条件等,实现更加灵活的bean扫描和注册逻辑。 总结起来,Spring可以通过XML配置文件、基于注解的方式以及自定义扫描器等多种机制来扫描和注册bean,从而实现对应用程序中的组件的管理和控制。这些机制使开发人员能够以更加便捷和灵活的方式管理和使用bean。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值