Spring06:使用配置属性详解(application.yml或application.properties)

1 引言

Spring05:配置Spring Security、保护web请求

https://www.cnblogs.com/fancy2022/p/16789970.html 在上一篇中,我们使用spring security为Taco Cloud应用完善了基本安全性功能;在前几篇内容中,我们也一直在使用Spring不断构建和完善该应用,包括主页展示、订单、用户注册,但是,你也许注意到了在过程中 我们没有像Javaweb阶段一样去配置一个类似applicationContext.properties的配置文件,而在示例代码中,有两个空的配置文件(如下图)-application.yml、application.properties,接下来我们将深入了解它。

【本文内容】

  • 回顾Javaweb阶段的xml配置文件,以及相关实现原理

  • 了解并创建spring的配置文件

  • 了解spring profile的作用和使用方法

2 回顾Javaweb阶段的配置文件

① IOC和DI

在回顾Javaweb阶段的配置文件applicationContext.xml之前,我们需要知道一个重要概念——IOC:控制反转,我认为他是Spring最核心的概念。(IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现)

IOC

IOC—Inversion of Control,即“控制反转”,是一种设计思想。

  1. 之前在Servlet中,我们创建service对象 , FruitService fruitService = new FruitServiceImpl(); 这句话如果出现在servlet中的某个方法内部,那么这个fruitService的作用域(生命周期)应该就是这个方法级别; 如果这句话出现在servlet的类中,也就是说fruitService是一个成员变量,那么这个fruitService的作用域(生命周期)应该就是这个servlet实例级别

  2. 之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产生fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory中 因此,我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转

在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。 我们系统架构或者是设计的一个原则是: 高内聚低耦合。 层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合(就是没有耦合)

public class FruitServiceImpl implements FruitService {
    private FruitDAO fruitDAO = new FruitDAOImpl();
public class FruitController extends ViewBaseServlet {
​
    private FruitService fruitService = new FruitServiceImpl();

所谓的解耦,不是说完全没关系,而是在代码层面解除锁定,打个比方就是用胶水黏在一起,还是像乐高那样拼接,以上代码可以看出我们目前的系统各层之前耦合度较高

DI

DI-Dependency Injection,即依赖注入

DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。

在Javaweb阶段,我们实现IOC的步骤是:

①修改配置文件applicationContext

②添加BeanFactory接口

③添加ClassPathXmlApplicationContext实现BeanFactory

④改原先在DispatcherServlet的init方法中的获取对应Controller的方法体

⑤修改中央控制器DispatcherServlet中的代码

⑥去掉FruitServiceImpl和FruitController中的耦合

public class FruitController { private FruitService fruitService = null;}

② 基于xml管理bean

根据ApplicationContext.xml配置文件中配置的bean节点和property属性(如下图)

将bean实例对象保存到map容器中,

我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。

各property属性对应的都按配置文件中的关系实例化了,并保存在beanMap中;

<?xml version="1.0" encoding="utf-8"?>
​
<!DOCTYPE beans [
    <!ELEMENT beans (bean*)>
    <!ELEMENT bean (property*)>
    <!ELEMENT property (#PCDATA)>
​
    <!ATTLIST bean id ID #REQUIRED>
    <!ATTLIST bean class CDATA #IMPLIED>
    <!ATTLIST property name CDATA #IMPLIED>
    <!ATTLIST property ref IDREF #IMPLIED>
]>
​
<beans>
    <bean id="page" class="com.atguigu.myssm.myspringmvc.PageController"/>
​
    <!-- DAO -->
    <bean id="userDAO" class="com.brovovax.attdesk.dao.impl.UserDAOImpl"/>
    <bean id="consumableDAO" class="com.brovovax.attdesk.dao.impl.ConsumableDAOImpl"/>
    <bean id="listItemDAO" class="com.brovovax.attdesk.dao.impl.ListItemDAOImpl"/>
    <bean id="ioOrderDAO" class="com.brovovax.attdesk.dao.impl.IoOrderDAOImpl"/>
    <bean id="ioOrderItemDAO" class="com.brovovax.attdesk.dao.impl.IoOrderItemDAOImpl"/>
​
    <!-- service -->
    <bean id="userService" class="com.brovovax.attdesk.service.impl.UserServiceImpl">
        <property name="userDAO" ref="userDAO"/>
    </bean>
    <bean id="consumableService" class="com.brovovax.attdesk.service.impl.ConsumableServiceImpl">
        <property name="consumableDAO" ref="consumableDAO"/>
    </bean>
    <bean id="listItemService" class="com.brovovax.attdesk.service.impl.ListItemServiceImpl">
        <property name="listItemDAO" ref="listItemDAO"/>
        <property name="consumableService" ref="consumableService"/>
    </bean>
    <bean id="ioOrderService" class="com.brovovax.attdesk.service.impl.IoOrderServiceImpl">
        <property name="ioOrderDAO" ref="ioOrderDAO"/>
        <property name="ioOrderItemDAO" ref="ioOrderItemDAO"/>
        <property name="listItemService" ref="listItemService"/>
        <property name="consumableService" ref="consumableService"/>
    </bean>
​
    <!-- controller -->
    <bean id="user" class="com.brovovax.attdesk.controller.UserController">
        <property name="userService" ref="userService"/>
        <property name="listItemService" ref="listItemService"/>
    </bean>
    <bean id="consumable" class="com.brovovax.attdesk.controller.ConsumableController">
        <property name="consumableService" ref="consumableService"/>
    </bean>
    <bean id="inventory" class="com.brovovax.attdesk.controller.InventoryController">
        <property name="listItemService" ref="listItemService"/>
    </bean>
    <bean id="order" class="com.brovovax.attdesk.controller.IoOrderController">
        <property name="ioOrderService" ref="ioOrderService"/>
    </bean>
</beans>

以上就是我们Javaweb阶段的配置文件applicationContext.xml的相关原理,温故而知新,接下来让我们开始了解Spring阶段的配置文件。

3 Spring阶段的配置文件

①基于注解管理bean

经过之前的学习,你已经发现我们现在不需要配置applicationContext.xml了,而是使用了注解,极大简化了开发。

和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。

②bean的属性注入

在Javaweb阶段,我们为bean设置属性是通过xml的 <property/>标签进行

在spring中,我们通过@configuration、@Bean注解将方法的返回值注入到spring容器,给bean的属性赋值

@Configuration//声明一个类是一个java配置类, 相当于一个xml配置文件
@EnableConfigurationProperties(JdbcProperties.class)
public class JdbcConfiguration {
    //  @Autowired
    private JdbcProperties jdbcProperties;
​
    public JdbcConfiguration(JdbcProperties jdbcProperties) {
        this.jdbcProperties = jdbcProperties;
    }
​
    @Bean//把方法的返回值注入到spring 容器
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(this.jdbcProperties.getDriverClassName());
        dataSource.setUrl(this.jdbcProperties.getUrl());
        dataSource.setUsername(this.jdbcProperties.getUsername());
        dataSource.setPassword(this.jdbcProperties.getPassword());
        return dataSource;
    }
}
​
​

以上方式无论是使用xml文件还是再构造一个JdbcProperties对象,通过.@Autowired和@Bean注入属性来获取一个数据库的连接的方式,都存在扩展性的问题,能否和配置bean一样,只配置一个xml文件就可以帮我们实现属性注入呢?借助配置文件和Spring Boot的自动配置就可以实现。

③spring的各种配置属性

在了解配置文件前,我们需要知道Spring有哪些属性源(如下图)

Spring Boot自动配置的bean都可以通过Spring环境提取的属性进行配置。

4 application.yml或application.properties详解

4.1 配置文件的内容分析

这里以application.yml文件来列举项目src/main/resources/application.yml的配置文件(YAML 是一种用于指定分层配置数据的便捷格式

①数据库

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/DatebaseName
    username: root
    password: 123456
    driverClassName: com.mysql.jdbc.Driver

②服务器

# 服务器配置
server:
  port: 8082

③日志

日志级别、日志文件路径

loggingh:
  root: WARN
  org:
    springframwork:
      security: DEBUG

更多配置请见官方文档:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/html/appendix-application-properties.html#core-properties 在之后的Spring系列文章中还会有更多配置,这里仅作为部分示例。

4.2 创建自定义的配置文件

第3节中已经说明,我们创建配置文件是为了给bean注入属性,那么具体是如何应用的呢?

①订单OrderController

@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
public class OrderController {
 private OrderRepository orderRepo;
  private OrderProps props;//针对OrderController中德pageSize属性单独抽取的类
​
  public OrderController(OrderRepository orderRepo,
          OrderProps props) {
    this.orderRepo = orderRepo;
    this.props = props;
  }
    @GetMapping
  public String ordersForUser(
      @AuthenticationPrincipal User user, Model model) {
​
    Pageable pageable = PageRequest.of(0, props.getPageSize());
    model.addAttribute("orders",
        orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
​
    return "orderList";
  }
}

其中的ordersForUser给用户展示了订单列表,一般情况下我们肯定不会一次性展示全部的订单信息,所以我们需要设置一个PageSize来控制一次请求所返回的订单数,这时就需要用到配置文件了。

②配置类

@Component
@ConfigurationProperties(prefix="taco.orders")
@Data
// end::notValidated[]
@Validated
//tag::notValidated[]
public class OrderProps {
  
//end::notValidated[]
  @Min(value=5, message="must be between 5 and 25")
  @Max(value=25, message="must be between 5 and 25")
//tag::notValidated[]
  private int pageSize = 20;
​
}

此配置类只有1个属性pageSize,默认一次展示20页

@ConfigurationProperties(prefix="taco.orders")表示它是一个配置类并且引用了配置文件中的taco.orders.pageSize属性

@Component表示Spring会将这个类扫描到,创建为Spring应用上下文中的bean

@Validation是Java定义的一套基于注解的数据校验规范

@Null 限制只能为null
@NotNull 限制必须不为null
@NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
@NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位
数不能超过fraction
@Future 限制必须是一个将来的日期
@Past 验证注解的元素值(日期类型)比当前时间早
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
​
​
注意:
@NotNull 适用于任何类型被注解的元素必须不能与NULL
@NotEmpty 适用于String Map或者数组不能为Null且长度必须大于0
@NotBlank 只能用于String上面 不能为null,调用trim()后,长度必须大于0

这里用到了@Max和@Min,表示限制了pageSize的最大值和最小值

③自定义的application.yml

taco:
  orders:
    pageSize: 10

这样我们就可以灵活地对pageSize进行设置,而非硬编码的方式。

4.3 创建配置文件遇到的问题

本节介绍创建配置文件时可能遇到的问题

①鼠标悬停在配置文件Unknown property ‘taco’

原因:创建配置属性缺少元数据

解决方法:在src/main/resources/META-INF下配置属性的说明

additional-spring-configuration-metadata.json

{"properties": [
  {
    "name": "taco.orders.page-size",
    "type": "java.lang.String",
    "description": "Sets the maximum number of orders to display in a list."
  },
  {
    "name": "taco.discount.codes",
    "type": "java.util.Map<String, Integer>",
    "description": "A map of discount codes to a discount percentage."
  }
]}

效果:

在src/main/resources/META-INF/additional-spring-configuration-metadata.json添加

  {
    "name": "taco.orders.pageSize",
    "type": "java.lang.String",
    "description": "Description Test."
}

若无法显示,请在pop.xml中添加依赖

(参考https://docs.spring.io/spring-boot/docs/2.0.4.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

然后重新运行启动类才能生效,更新也需要重启。

posted @ 2022-11-09 20:27  Fancy[love]  阅读(309)  评论(0编辑  收藏  举报