Spring 是一个轻量级、非入侵式的控制反转 (IoC) 和面向切面 (AOP) 的框架

一、spring注入方式选择

        1、属性注入

@Service
public class AService {

    @Autowired
    BService bService;
}

        属性注入限制:类只能在IOC容器中使用,对于IOC容器以外的环境,除了使用反射获取依赖之外,无法复用该实现类

        2、SET方法注入

@Service
public class AService {

    BService bService;

    @Autowired
    public void setbService(BService bService) {
        this.bService = bService;
    }
}

        Spring3.0 文档中,官方提倡 set 方法注入,说构造方法注入属性太多可能会让代码变得非常臃肿

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_spring

        3、构造方法注入

@Service
public class AService {

    BService bService;
    
    @Autowired
    public AService(BService bService) {
        this.bService = bService;
    }
}

        Spring4.x 开始,官方推荐通过构造方法注入的方式,能够保证注入的组件不可变,并且能够确保需要的依赖不为空

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_java_02

        优点: 

        1、依赖不可变:对象创建后无法再通过set方法等改变

         2、依赖不为空:自动检查注入对象不为空才注入成功

         3、完全初始化:获取依赖对象已经初始化了,拿到的对象已经初始化

二、spring容器注入bean方式

        1、@Configuration + @Bean

                1.1、@Configuration声明配置类, @Bean 注解加入容器

                

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_java_03

                

        2、@Componet + @ComponentScan

                2.1、@Componet声明组件类

                

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_java_04

                 2.2、@ComponentScan扫描注入spring容器

     

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_后端_05

      

         3、@Import注解导入

         3.1、@Import导入需要注入到容器类

        

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_06

        3.2、@Import + ImportSelector:自定义ImportSelector,重写selectImports 方法将注入spring容器的bean全量名

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_后端_07

         3.3 @Import + ImportBeanDefinitionRegistrar

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_08

         3.4 @Import + DeferredImportSelector

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_后端_09

       

         4、使用FactoryBean接口

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_hive_10

        5、使用 BeanDefinitionRegistryPostProcessor

 Spring容器启动执行 BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry 方法,通过手动向beanDefinitionRegistry中注册了person的BeanDefinition,将Person对象管理到Spring容器中

        

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_11

三、ApplicationRunner、CommandLineRunner、InitializingBean、@PostConstruct 执行顺序

        3.1、CommandLineRunner和ApplicationRunner的作用相同,入参不同        

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_后端_12

         3.2、InitializingBean为bean提供了初始化方法的方式,afterPropertiesSet方法

@PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner

三、springboot运行jar包

        1、springboot提供插件spring-boot-maven-plugin把程序打包成一个可执行jar包:project-name.jar


<build> <finalName>project-name</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin </artifactId> </plugin> </plugins> </build>       


2、project-name.jar结构

                

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_hive_13

         2.1、META-INF程序入口:pom文件、MAINIFEST.MF jar包信息

        Main-Class是org.springframework.boot.loader.JarLauncher,当我们使用java -jar project-name.jar 调用JarLauncher的main方法,而不是我们编写的SpringApplication。

        SpringBoot内部提供SpringBootLoader用于执行SpringApplication工具类

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_hive_14

 

         2.2、BOOT-INF:资源文件、依赖包

                        

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_15

           2.3、org.springframework.boot.laoder:SpringBootLoader代码

                        

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_16

                Launcher:基础抽象类:JarLauncher、WarLuncher、PropertiesLauncher

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_17

                Archive:归档文件的基础抽象类,

                                JarFileArchive implements Archive:包文件(getUrl、getManifest):内部依赖的jar对应的URL,会使用 !/ 分隔开的协议,SpringBoot 使用org.springframework.boot.loader.jar.Handler处理这些URL

                                ExplodedArchive implements Archive:文件目录的抽象

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_spring_18

 

                 JarFile:jar包的封装。JarFileArchive构造方法new JarFile(file)

 

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_hive_19

 

3、执行过程

        3.1、JarLauncher构造调用父类ExecutableArchiveLauncher构造方法

              

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_后端_20

 

        3.2、JarLauncher启动入库:构造JarLauncher调用launch方法(参数传递)

                

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_spring_21

         3.3、ExecutableArchiveLauncher实现Launcher类执行launch方法

                getClassPathArchives方法在会去找lib目录下对应的依赖JarFileArchive,同时项目自身的JarFileArchive

                LaunchedURLClassLoader类加载器的父类加载器是当前执行类JarLauncher的类加载器

                getMainClass方法会去项目自身的Archive中的Manifest中找出key为Start-Class的类

         

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_java_22

 

 4、MainMethodRunner的run方法

在非Spring Bean中获取Bean 如何避免为null的情况 spring非单例注入_jar_23

SpringBoot在可执行jar包中定义了自己的一套规则,比如第三方依赖jar包在/lib目录下,jar包的URL路径使用自定义的规则并且这个规则需要使用org.springframework.boot.loader.jar.Handler处理器处理。它的Main-Class使用JarLauncher,如果是war包,使用WarLauncher执行。这些Launcher内部都会另起一个线程启动自定义的SpringApplication类。

这些特性通过spring-boot-maven-plugin插件打包完成。