Spring Boot的一点点原理

Spring Boot

Spring Boot 配置文件

Spring Boot中支持 application.propertiesapplication.yamlapplication.yml三种配置,推荐使用后两种(是同一种)。

server.port=8080
server: 
  port: 8080

三种配置文件的优先级 application.properties > application.yml > application.yaml

除了使用配置文件配置属性,还可以使用Java系统指令命令行参数配置

  • Java系统属性

    -Dserver.port=8081
  • 命令行参数

    --server.port=8082

    分别对应:

Spring Boot的一点点原理_第1张图片

优先级:命令行参数 > Java指令 > application.properties > application.yml > application.yaml

运行jar包时指定优先级:

java -Dserver.port=8081 -jar 项目名.jar --server.port=8082

要打包一个Spring Boot项目,需要引入 spring-boot-maven-plugin 插件


    
        
            org.springframework.boot
            spring-boot-maven-plugin
        
    

管理Bean

获取Bean

对于单例非延时加载的Bean而言,Spring Boot项目在启动时会自动创建IOC容器,并把Bean对象创建好放入IOC容器中。这样编写程序时我们只需要注入IOC容器中需要依赖的Bean对象即可。

Spring Boot获取Bean的三种方式

import com.zhw.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;


@SpringBootTest
public class SpringBootGetBeanTest {

    @Autowired
    private ApplicationContext applicationContext;  // IOC容器对象

    @Test
    public void getBeanTest() {
        // 根据Bean的名称获取  声明一个Bean没有指定名称,默认是首字母小写
        UserController bean1 = (UserController) applicationContext.getBean("userController");
        System.out.println("bean1 = " + bean1);

        // 根据Bean的类型获取
        UserController bean2 = applicationContext.getBean(UserController.class);
        System.out.println("bean2 = " + bean2);

        // 根据Bean的名称和类型获取
        UserController bean3 = applicationContext.getBean("userController", UserController.class);
        System.out.println("bean3 = " + bean3);
    }

}

运行后结果为:

bean1 = com.zhw.UserController@3b8ec001
bean2 = com.zhw.UserController@3b8ec001
bean3 = com.zhw.UserController@3b8ec001
可以看出获取到的Bean对象都是同一个,也就是说 Spring Boot 的 Bean 默认是单例的

一般是直接@Autowired注入

Bean的作用域

在 Spring 框架中,有以下五种常用的作用域(Scope):

  • Singleton(单例作用域)

    这是默认的作用域,也是最常见的作用域。在 Singleton 作用域下,Spring 容器中只会创建一个共享的实例对象,并在整个应用程序生命周期内都重用该对象。无论何时请求获取该 Bean,都会返回同一个实例。
  • Prototype(原型作用域)

    在 Prototype 作用域下,每次请求获取 Bean 时,Spring 容器都会创建一个新的实例对象。这意味着每次获取该 Bean 都会得到一个不同的实例。
  • Request(请求作用域): — 了解即可

    在 Request 作用域下,每个 HTTP 请求都会创建一个新的 Bean 实例。这对于 Web 应用程序非常有用,因为它确保了每个请求都拥有自己独立的 Bean 实例,以避免并发问题。
  • Session(会话作用域): — 了解即可

    在 Session 作用域下,每个用户会话(通常是一个 Web 会话)都会创建一个新的 Bean 实例。这使得可以为每个用户维护一个独立的状态,通常用于 Web 应用程序中。
  • GlobalSession(全局会话作用域): — 了解即可

    GlobalSession 作用域通常用于分布式环境,如基于 Spring 的 Portlet 应用程序。它类似于 Session 作用域,但在集群环境中可以共享。
@Component
@Scope("prototype")
public class MyPrototypeBean {
    // ...
}

这里的 @Scope("prototype") 表示将该 Bean 的作用域设置为原型(Prototype)。

这个类是默认单例的,在项目启动时就会将该类实例化放入IOC容器中。

@Lazy
@Lazy
@Component
@Scope("prototype")
public class MyPrototypeBean {
    // ...
}
如果在类上加了@Lazy注解后,该类就会延迟初始化,会在第一次使用的时候加载。

第三方Bean

如果我们需要使用第三方Bean(无法声明@Component等注解加入IOC的类),就需要使用@Bean注解,将返回值加入IOC容器,这里以RedisTemplate为例

/**
 * 第三方Bean
 */
@Slf4j
@Configuration
public class CommonConfig {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("开始创建Redis模板对象......");
        RedisTemplate redisTemplate = new RedisTemplate<>();
        // 设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 设置redis key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

如果在声明第三方Bean时需要依赖注入,只需要将Bean对象作为方法形参传入即可。

例如上面redisTemplate方法的形参 redisTemplate(RedisConnectionFactory redisConnectionFactory) Spring 容器会自动装配,根据该类型(RedisConnectionFactory)自动到IOC容器找到Bean对象并注入

Spring Boot原理

Spring所有的框架都基于Spring Framework,但是基于Spring Framework开发比较繁琐。因此Spring4.0后推出了Spring Boot来简化Spring框架的开发。它有两个非常强大的功能:起步依赖自动配置

起步依赖

起步依赖的原理就是 maven 的依赖传递。比如导入spring-boot-starter-web 依赖后会依赖传递包含其它好几个依赖。

自动配置

Spring Boot 会在Spring容器启动后,将一些配置类、Bean对象自动创建并存到IOC容器中去。这就是自动配置,可以不用我们自己去创建对象,大大的简化了开发。

例如项目中我只引入了AMQP(包含RabbitMQ)的依赖


    org.springframework.boot
    spring-boot-starter-amqp

并且在项目中并没有去做声明RabbitTemplate的操作,但是却能注入RabbitTemplate

/**
 * Spring Boot 自动配置测试类
 */
@SpringBootTest
public class SpringBootAutoConfigurationTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void autoConfigurationTest() {
        rabbitTemplate.convertAndSend("", "demo.simple", "Success!!");
    }

}

@ComponentScan

启动类上的@SpringBootApplication就具有包扫描的功能,但是它的扫描范围是 当前包及其子包 ,扫描不到其他的包。因此如果我们需要扫描第三方包可以在启动类上加 @ComponentScan 注解。

使用 value 或者 basePackages 属性指定要扫描的包路径,默认是value

@ComponentScan({"com.demo", "com.test"})

但是使用这种方法去扫描包非常繁琐,并且一扫描就是一整个包,可能我们只是需要这个包下面的几个Bean呢?这样就会造成性能的浪费。

@Import(了解)

还可以使用@Import注解,将我们需要的单个Bean注入Spring的IOC容器,这样就不用扫描一整个包了。

导入普通类,即用 @Component 注解声明的类

@Component
public class Generate {
    // ...
}
@Import({Generate.class}) // 导入普通类

导入配置类,用 @Configuration 注解声明的类。配置类中的所有Bean对象都会交由Spring的IOC管理

@Configuration
public class DemoConfig {

    @Bean
    public Product product() {
        return new Product();
    }

    @Bean
    public Consumer consumer() {
        return new Consumer();
    }

}
@Import({DemoConfig.class}) // 导入配置类

导入 ImportSelector 接口实现,返回的是一个String类型的数组,里面写类的全路径

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.demo.MyImportSelector"};
    }
}
@Import({MyImportSelector.class}) // 导入 `ImportSelector` 接口实现

@Enablexxx(推荐使用)

看到这里就会想,之前导入的那些依赖也没有配置包扫描路径,是怎么拿到的?对于要使用哪些Bean,第三方依赖已经帮我们提供了 @Enablexxx 这样的注解指定要导入的Bean,它实际上就是封装了 @Import 注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({MyImportSelector.class})
public @interface EnableMyConfig {
}
@EnableMyConfig
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

你可能感兴趣的:(Spring Boot的一点点原理)