SpingBoot原理

配置优先级

SpringBoot配置的优先级从高到低依次为命令行参数、JNDI属性、Java系统属性、操作系统环境变量、外部配置文件、内部配置文件、注解指定的配置文件和编码中直接指定的默认属性。具体如下:

  1. 命令行参数:启动应用时,通过命令行指定的参数拥有最高优先级。例如,使用--server.port=8081会直接改变应用程序的端口,无论在什么配置文件中定义过该值。
  2. JNDI属性:这些属性由当前J2EE应用的环境提供,并具有第二优先级。
  3. Java系统属性:这些属性通过-D参数设置,并优先于操作系统环境变量。
  4. 操作系统环境变量:设置在操作系统级别的环境变量也具有较高的优先级。
  5. 外部配置文件:位于JAR包外部的配置文件(如application.propertiesapplication.yml)优先于内部的配置文件。特定环境的配置文件(如application-dev.properties)会覆盖通用配置文件(即application.properties)中的相应属性。
  6. 内部配置文件:在同一级目录下,不同后缀配置文件的优先级为:.properties最高,其次是.yml.yaml最低。相同后缀配置文件的优先级中,带环境名的配置文件(如application-dev.yml)高于不带环境名的(如application.yml)。
  7. 注解指定的配置文件:通过@PropertySource注解指定的配置文件优先级较低。
  8. 默认属性:通过SpringApplication.setDefaultProperties指定的默认属性拥有最低优先级。

下面是一个SpringBoot配置优先级的示例:

假设有以下配置文件:

  • application.properties(通用配置文件)
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
  • application-dev.properties(开发环境配置文件)
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/dev_test
  • application-prod.properties(生产环境配置文件)
server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/prod_test
  • 命令行参数:--server.port=9090

在启动应用时,会按照以下顺序加载配置:

  1. 命令行参数:--server.port=9090
  2. JNDI属性、Java系统属性、操作系统环境变量等其他来源的配置选项。
  3. 外部配置文件:application.propertiesapplication-dev.propertiesapplication-prod.properties
  4. 内部配置文件:application.propertiesapplication-dev.propertiesapplication-prod.properties
  5. 注解指定的配置文件。
  6. 编码中直接指定的默认属性。

最终,应用程序的端口号为9090,数据源URL为jdbc:mysql://localhost:3306/dev_test

Bean管理

获取Bean

在Spring框架中,获取Bean的方法有多种,包括通过ApplicationContext、实现ApplicationContextAware接口、继承抽象类等方法。具体如下:

通过ApplicationContext获取Bean

  • 可以在初始化时保存ApplicationContext对象,然后通过这个对象获取Bean。例如,使用ClassPathXmlApplicationContext加载配置文件并获取Bean:
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    MyBean myBean = (MyBean) applicationContext.getBean("myBean");
    
  • 适用于独立应用程序和基于Web的应用程序,这种方法简单直接,但需要显式加载配置文件。

通过实现ApplicationContextAware接口获取Bean

  • 可以实现ApplicationContextAware接口并将ApplicationContext对象注入到实现类中,从而随时获取Bean。例如:
    public class SpringUtils implements ApplicationContextAware {
        private static ApplicationContext applicationContext;
        
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            SpringUtils.applicationContext = applicationContext;
        }
        
        public static <T> T getBean(String beanName) {
            return (T) applicationContext.getBean(beanName);
        }
    }
    
  • 这种方法的优点是不需要显式加载配置文件,Spring会自动注入ApplicationContext。

通过继承抽象类获取Bean

  • 可以继承ApplicationObjectSupportWebApplicationObjectSupport抽象类,调用父类的getApplicationContext()方法获取Spring容器对象。例如:
    @Service
    public class SpringContextHelper extends ApplicationObjectSupport {
        public Object getBean(String beanName) {
            return getApplicationContext().getBean(beanName);
        }
    }
    
  • 这种方法适用于需要在Spring容器管理的对象内部获取其他Bean的场景。

通过BeanFactory获取Bean

  • 虽然不推荐,但可以通过BeanFactory来获取Bean。例如,使用已废弃的XmlBeanFactory类:
    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    MyBean myBean = (MyBean) beanFactory.getBean("myBean");
    
  • 由于XmlBeanFactory已被废弃,这种方法不推荐使用,且存在潜在的维护问题。

通过Spring提供的工具类获取ApplicationContext对象

  • 在基于Web的应用程序中,可以通过WebApplicationContextUtils工具类从ServletContext对象获取ApplicationContext对象,再通过它获取需要的类实例。例如:
    ApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
    MyBean myBean = (MyBean) ac.getBean("myBean");
    
  • 这种方法适用于在Web应用程序中使用Spring框架的场景。

Bean作用域

Spring Bean的作用域定义了Bean在Spring容器中的生命周期和可见性,主要分为五种:singleton、prototype、request、session和application

作用域说明
singleton容器内同名称的bean只有一个实例(单例)(默认)
prototype每次使用该bean时会创建新的实例(非单例)
request每个请求范围内会创建新的实例(web环境中,了解)
session每个会话范围内会创建新的实例(web环境中,了解)
application每个应用范围内会创建新的实例(web环境中,了解)

 单例作用域(Singleton Scope)

  • 单例作用域是Spring的默认设置,在整个Spring IoC容器中只存在一个Bean实例。这意味着所有对该Bean的请求都会返回同一个实例。例如,在Spring配置文件中可以这样配置:
    <bean id="singletonBean" class="com.example.MySingletonBean" scope="singleton"/>
    
  • 适用于无状态的服务,如数据访问对象(DAO)、业务服务(Service)等,因为这些组件不需要存储特定于用户的信息,可以在多线程之间安全共享。

原型作用域(Prototype Scope)

  • 每次注入或通过getBean()方法调用时,都会创建一个新的Bean实例。这意味着每个请求得到的都是一个全新的对象。例如,在Spring配置文件中可以这样配置:
    <bean id="prototypeBean" class="com.example.MyPrototypeBean" scope="prototype"/>
    
  • 适用于需要频繁创建新对象的场景,如每次使用时需要新的状态或配置的Bean。

请求作用域(Request Scope)

  • 每个HTTP请求都会创建一个新的Bean实例,该实例仅在当前请求中有效,请求结束后即被销毁。这适用于需要在单个HTTP请求中保持状态的Bean。例如,在类上添加注解:
    @Component
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class MyRequestBean {
        // ...
    }
    
  • 适用于处理表单提交或执行某个请求特定操作的组件。

会话作用域(Session Scope)

  • 每个HTTP会话都会创建一个新的Bean实例,该实例在会话结束前一直有效。这适用于需要在多个HTTP请求之间保持状态的Bean。例如,在类上添加注解:
    @Component
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class MySessionBean {
        // ...
    }
    
  • 适用于需要跟踪用户会话信息的场景,如购物车或用户登录信息。

应用程序作用域(Application Scope)

  • 在ServletContext范围内,整个Web应用程序共享同一个Bean实例。该实例在应用程序启动时创建,应用程序关闭时销毁。例如,在类上添加注解:
    @Component
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class MyApplicationBean {
        // ...
    }
    
  • 适用于需要在整个Web应用程序中共享的状态或资源,如全局缓存或配置信息。

第三方Bean

在Spring框架中,第三方Bean的管理是通过配置类和注解来实现的,使得开发者可以在不修改原始代码的情况下定义和管理这些Bean。具体如下:

使用@Bean注解管理第三方Bean

  • 环境准备:需要创建一个Spring项目,并添加对应的依赖。例如,要管理Druid数据源,需要在pom.xml中添加Druid的依赖:
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
  • 配置类和@Bean注解:在配置类中使用@Bean注解标注的方法来创建和管理第三方Bean。比如,为Druid数据源创建一个DataSource Bean:
@Configuration
public class SpringConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

使用独立的配置类

  • 导入式管理:通过在核心配置类中使用@Import注解,手动添加其他配置类。例如,可以将JdbcConfig导入到SpringConfig中:
@Configuration
public class JdbcConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        // 相关配置
        return ds;
    }
}

@Configuration
public class SpringConfig {
    @Import(JdbcConfig.class)
    // 其他配置...
}
  • 扫描式管理:使用@ComponentScan注解自动加载对应包中的配置类。这种方法虽然简便,但可能隐藏性太强,不推荐使用。
@Configuration
@ComponentScan("com.itheima.config")
public class SpringConfig {}

第三方Bean的依赖注入

  • 简单类型依赖注入:使用@Value注解可以注入简单类型的值,如字符串、整数等。例如,注入数据库URL和用户名:
public class JdbcConfig {
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;

    @Value("root")
    private String userName;

    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl(url);
        ds.setUsername(userName);
        // 其他配置...
        return ds;
    }
}
  • 引用类型依赖注入:通过在Bean定义的方法中添加形参,Spring会自动装配同类型的Bean。例如,注入BookService:
@Bean
public DataSource dataSource(BookService bookService) {
    System.out.println(bookService);
    DruidDataSource ds = new DruidDataSource();
    // 属性设置
    return ds;
}

XML配置与注解配置的比对

  • 定义bean:XML配置使用标签;注解配置使用@Component及其衍生注解。
  • 依赖注入:XML配置通过标签设置属性;注解配置使用@Autowired、@Qualifier、@Value等。

SpringBoot原理

起步依赖

起步依赖是Spring Boot的核心特性之一,通过提供一系列预定义的依赖项集合来简化项目的搭建和依赖管理

起步依赖(Starter)在Spring Boot中起着至关重要的作用。它通过整合各种依赖库和自动配置,大大简化了开发者的工作。例如,当在项目中添加spring-boot-starter-web起步依赖时,会自动包含Spring MVC、Tomcat和其他Web开发所需的依赖,无需再手动逐个添加这些依赖。这样不仅提高了开发效率,还减少了因版本不兼容导致的问题。

起步依赖的原理是通过spring.factories文件和自动配置类来实现的。当项目引入某个Starter后,Spring Boot会在启动时读取META-INF/spring.factories文件中的配置,根据其中的自动配置类(如DataSourceAutoConfigurationWebMvcAutoConfiguration等)来自动配置相应的Bean。这些自动配置类通常包含一系列的条件注解(如@ConditionalOnClass@ConditionalOnMissingBean等),用于判断是否满足某些条件来决定配置哪些Bean。

除了使用官方提供的起步依赖外,Spring Boot还允许开发者自定义Starter。自定义Starter的命名通常遵循反向的格式,如官方的是spring-boot-starter-xxx,而自定义的则是xxx-spring-boot-starter。自定义Starter时,需要创建一个自动配置模块,并在其中定义相关的自动配置类和属性类,然后通过spring.factories文件将其与对应的Starter关联起来。

代码解释:

引入起步依赖:在项目的pom.xml文件中添加spring-boot-starter-web起步依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

自动配置类:当项目引入spring-boot-starter-web后,Spring Boot会在启动时读取META-INF/spring.factories文件,根据其中的自动配置类来自动配置相应的Bean。例如,WebMvcAutoConfiguration类会配置Spring MVC相关的Bean,如DispatcherServlet、ViewResolver等。

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(WebProperties.class)
@Import({ DispatcherServletAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    // ...
}

自定义属性类:除了自动配置类外,起步依赖还可能包含一些自定义的属性类,用于提供额外的配置选项。例如,WebProperties类提供了与Web相关的配置属性,如服务器端口号、上下文路径等。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    private Integer port;
    private String contextPath;
    // ...
}

使用起步依赖:在项目中可以直接使用起步依赖提供的Bean,无需手动创建或注入。例如,通过@Autowired注解注入一个RestTemplate实例:

@RestController
public class MyController {
    @Autowired
    private RestTemplate restTemplate;
    // ...
}

自定义Starter:除了官方提供的起步依赖外,开发者还可以自定义Starter。自定义Starter需要创建一个自动配置模块,并在其中定义相关的自动配置类和属性类,然后通过spring.factories文件将其与对应的Starter关联起来。

// 自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@Import(MyServiceAutoConfiguration.class)
public class MyServiceAutoConfiguration {
    // ...
}
// 属性类
@ConfigurationProperties(prefix = "myservice")
public class MyServiceProperties {
    private String url;
    // ...
}
// spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyServiceAutoConfiguration

Conditional注解

@Conditional注解是Spring中的一个强大的条件化配置工具,它用于控制特定Bean或配置类在满足某些条件时才被加载到Spring容器中。@Conditional可以应用在@Bean注解的方法上,也可以应用在整个@Configuration类上。具体如下:

@Conditional注解的作用

  • @Conditional注解的主要作用是提供一种机制,允许根据是否满足特定的条件来决定是否创建某个Bean或者是否执行某个配置块。这种条件可以是类路径中是否存在某个类、属性文件中的某个属性值、环境变量的值等等。

@Conditional的常见使用方式

  • @ConditionalOnClass:当类路径中存在指定的类时,条件匹配成功。例如,当需要根据项目是否使用了某个特定的数据库驱动来调整数据源的配置时,可以使用这个注解。
  • @ConditionalOnMissingClass:与@ConditionalOnClass相反,当类路径中不存在指定的类时,条件匹配成功。
  • @ConditionalOnBean:当Spring容器中存在指定类型的Bean时,条件匹配成功。这常用于基于容器中是否有某个特定Bean来调整配置。
  • @ConditionalOnMissingBean:与@ConditionalOnBean相反,当Spring容器中不存在指定类型的Bean时,条件匹配成功。
  • @ConditionalOnProperty:当指定的属性值满足条件时(如属性存在且其值为true),条件匹配成功。这常用于根据配置文件中的设置来决定是否启用某个配置。
  • @ConditionalOnExpression:基于SpEL表达式的条件判断,当表达式结果为true时,条件匹配成功。这提供了更灵活的条件判断方式。

自定义条件化配置

  • 除了上述的常见@Conditional注解外,Spring还允许开发者通过实现Condition接口或继承AbstractCondition类来创建自定义的条件化配置。这允许开发者根据具体的应用场景和需求来编写自己的条件判断逻辑。

代码示例:

@Configuration
public class MyConfiguration {
    
    @Bean
    @ConditionalOnClass(name = "com.example.MyService")
    public MyService myService() {
        return new MyService();
    }
    
    @Bean
    @ConditionalOnProperty(name = "my.property", havingValue = "true")
    public MyComponent myComponent() {
        return new MyComponent();
    }
    
    @Bean
    @ConditionalOnExpression("${my.expression:false}")
    public MyOtherComponent myOtherComponent() {
        return new MyOtherComponent();
    }
}
  • 72
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值