Spring-使用配置属性


概述

  • 微调自动配置的beans
  • 应用程序组件配置
  • 使用Spring profiles

对自动配置进行微调

1. Spring环境

Spring环境是所有配置属性的一站式商店,它抽象了配置属性的起源,以便需要这些属性的bean可以直接从Spring本身使用它们;Spring环境是从几个属性源获取的,即将这些属性源聚合到一个源(Spring环境)中,再从这个源注入Spring beans中,因此这些属性都可以直接通过Spring环境进行配置

  • JVM系统属性
  • 操作系统环境变量
  • 命令行参数
  • 应用程序属性配置文件

eg. 服务器端口配置

  • src/main/resources/application.properties配置

    server.port=9090
  • *YAML配置方式: * src/main/ resources/application.yml

    server:
     port: 9090
  • *命令行参数方式: * 启动时指定端口

    $ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090
  • *操作系统环境变量方式: * 应用程序总是在一个端口启动

    $ export SERVER_PORT=9090

2. 配置DataSource

  • 推荐使用YAML配置文件方式配置
#配置MySQL数据源
spring:
 datasource:
 url: jdbc:mysql://localhost/tacocloud
 username: tacodb
 password: tacopassword
 # 非必须,spring boot可以自动查找到,如果有问题则指定
 driver-class-name: com.mysql.jdbc.Driver
 schema:
 - order-schema.sql
 - ingredient-schema.sql
 - taco-schema.sql
 - user-schema.sql
 data:
 - ingredients.sql
 initialization-mode: always
 #如果使用spring需要设置启动时只更新表,不删除重建表
 jpa:
  show-sql: true
  hibernate:
   ddl-auto: update
  • JNDI方式
spring:
 datasource:
  jndi-name: java:/comp/env/jdbc/tacoCloudDS

3. 配置内置服务器

  • 端口配置如果设置为0,则启动时自动检测可用随机端口进行使用

    server:
     port: 0
  • 配置HTTPS访问(配置SSL证书)

    • 使用JDK的keytool命令自建一个证书,或者可以去申请一个SSL证书

      keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA
    • Tomcat支持JKS格式证书,从Tomcat7开始也支持PFX格式证书,其他格式需要转换

  • 实例(tomcat为例)

    • 配置https访问
    # https监听端口为8443
    server:
      port: 8443
      ssl:
        key-store: classpath:mykeys.jks
        key-store-password: letmein
        key-password: letmein
    • 配置将http请求(8080端口)重定向到https端口(8443)访问
    @Configuration
    public class TomcatConfiguration {
        @Bean
        public Connector connector(){
            Connector connector=new Connector("org.apache.coyote.http11.Http11NioProtocol");
            connector.setScheme("http");
            connector.setPort(8080);
            connector.setSecure(false);
            connector.setRedirectPort(8443);
            return connector;
        }
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory(Connector connector){
            TomcatServletWebServerFactory tomcat=new TomcatServletWebServerFactory(){
                @Override
                protected void postProcessContext(Context context) {
                    SecurityConstraint securityConstraint=new SecurityConstraint();
                    securityConstraint.setUserConstraint("CONFIDENTIAL");
                    SecurityCollection collection=new SecurityCollection();
                    collection.addPattern("/*");
                    securityConstraint.addCollection(collection);
                    context.addConstraint(securityConstraint);
                }
            };
            tomcat.addAdditionalTomcatConnectors(connector);
            return tomcat;
        }
    }
    

4. 配置日志

  • 默认情况下Spring boot通过Logback配置日期记录,在info级别写入控制台,详情参考(http://logback.qos.ch)

  • 如果需要自定义详细配置可以在src/main/resources目录下创建logback.xml文件,如:

    <configuration>
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
     <encoder>
     <pattern>
     %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
     </pattern>
     </encoder>
     </appender>
     <logger name="root" level="INFO"/>
     <root level="INFO">
     <appender-ref ref="STDOUT" />
     </root>
    </configuration>
  • 对于比较常见的属性配置(日志级别和设置日志写入文件)可以直接在application.yml文件中配置

    logging:
      level:
        root: WARN
        org.springframework.security: DEBUG
      file:
        path: logs
    #    name: taco.log
    # file.name 和 file.path同时使用只会生效file.name,
    # 日志默认生成目录为项目根目录,默认文件名为spring.log
    # 日志默认上限为10M,达到上限就会切换一个新文件
    
    # 对于包名可以合并为一行或者分开
    # org:
    # springframework:
    # security: DEBUG

5. 使用${}获取指定属性的值

  • 对配置属性的值,不一定需要设置为硬编码形式的值,可以通过${}获取其他配置属性值

    greeting:
     welcome: You are using ${spring.application.name}.

创建自定义配置属性

1.概述

通过在配置文件中添加自定义配置属性,可以在代码中进行使用,实现更加灵活的功能

1.1 硬编码方式
//例如在此查询时当前页查询条数Pageable直接使用硬编码方式不够灵活,可以使用从配置文件加载自定义配置属性
//Pageable对象是Spring Data提供的一种选择子集的分页方式
@GetMapping
public String ordersForUser(
 @AuthenticationPrincipal User user, Model model) {
 Pageable pageable = PageRequest.of(0, 20); //硬编码方式,不灵活
 model.addAttribute("orders",
 orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
 return "orderList";
}
1.2 使用自定义配置方式
  • domain

    @Data
    @Entity
    @Table(name = "POP_IN_STOCK")
    public class PopInStock {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        @Column(name = "TASKID")
        private Integer task;
        @Column(name = "POP_CODE")
        private String code;
        @Column(name = "POP_NAME")
        private String name;
        private String version;
        private String brand;
        @Column(name = "IN_STOCK_COUNT")
        @JsonProperty(value = "count")
        private Double inStockCount;
        private String comment;
    }
  • properties: application.yml/application.properties

    stock:
      page-size: 15
    # 与下相同
    #stock:
    #  pageSize: 15
  • Controller

    @Controller
    @RequestMapping("/stock")
    @ConfigurationProperties(prefix = "stock") //引入前缀为stock的配置属性 stock.pageSize = 15
    public class PopStockController {
        private JpaPopInStockRepository popInStockRepository;
        @Autowired
        public void setPopInStockRepository(JpaPopInStockRepository popInStockRepository) {
            this.popInStockRepository = popInStockRepository;
        }
        //会根据自定义的配置属性自动设置pageSize的值
        private int pageSize = 1; //默认值为1
        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }
    
        @GetMapping
        public String show(){
            return "jpa/popInStock";
        }
    
        @ResponseBody //list转json返回
        @PostMapping
        public String process() throws JsonProcessingException {
            Pageable pageable = PageRequest.of(0,pageSize);
            List<PopInStock> list = popInStockRepository.findPopInStocksByOrderByTaskDesc(pageable);
            return new ObjectMapper().writeValueAsString(list);
        }
    }
  • Repository

    public interface JpaPopInStockRepository extends CrudRepository<PopInStock,Integer> {
        List<PopInStock> findPopInStocksByOrderByTaskDesc(Pageable pageable);
    }
  • html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Stock</title>
        <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
        <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    </head>
    <body>
    <div id="app">
        <table>
            <tr>
                <td>TASKID</td>
                <td>CODE</td>
                <td>NAME</td>
                <td>BRAND</td>
                <td>COUNT</td>
            </tr>
            <tr v-for="item in items">
                <td>{{ item.task }}</td>
                <td>{{ item.code }}</td>
                <td>{{ item.name }}</td>
                <td>{{ item.brand }}</td>
                <td>{{ item.count }}</td>
            </tr>
        </table>
    </div>
    <script>
        new Vue({
            el: '#app',
            data () {
                return {
                    items: null
                }
            },
            mounted () {
                axios
                    .post('/stock').then(response => (this.items = response.data))
                    .catch(function (error) {
                        console.log(error);
                    });
            }
        })
    </script>
    </body>
    </html>

2. 使用配置类管理自定义属性推荐

虽然直接使用@ConfigurationProperties(注解一般放在bean上)可以使用自定义属性,但是更好的方式是使用自定义一个holder类来管理这些自定义属性,并且能对属性的值进行校验,其他对象需要使用时常规的注入此bean即可

  • 配置类holder

    @Data
    @Component
    @ConfigurationProperties(prefix = "stock")
    @Validated //数据校验注解
    public class ViewProps {
        @Max(value = 25,message = "必须在5~25之间")
        @Min(value = 5,message = "必须在5~25之间")
        private int pageSize = 1;
    }
  • Controller: 使用,其他代码同上例

    @Controller
    @RequestMapping("/stock")
    //@ConfigurationProperties(prefix = "stock") //引入前缀为stock的配置属性 stock.pageSize = 15
    public class PopStockController {
        private JpaPopInStockRepository popInStockRepository;
        @Autowired
        public void setPopInStockRepository(JpaPopInStockRepository popInStockRepository) {
            this.popInStockRepository = popInStockRepository;
        }
        //会根据自定义的配置属性自动设置pageSize的值
        /*
        private int pageSize = 1; //默认值为1
        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }*/
    
        private ViewProps viewProps;
        @Autowired //注入: 配置类holder的bean
        public void setViewProps(ViewProps viewProps) {
            this.viewProps = viewProps;
        }
    
        @GetMapping
        public String show(){
            return "jpa/popInStock";
        }
    
        @ResponseBody //list转json返回
        @PostMapping
        public String process() throws JsonProcessingException {
            //Pageable pageable = PageRequest.of(0,pageSize);
            Pageable pageable = PageRequest.of(0,viewProps.getPageSize());
            List<PopInStock> list = popInStockRepository.findPopInStocksByOrderByTaskDesc(pageable);
            return new ObjectMapper().writeValueAsString(list);
        }
    }

3. 声明配置属性元数据

创建配置属性元数据即给自定义的配置属性增加一种注释,可以在使用属性时,工具能根据自定义的注释进行代码提示

  • 虽然不声明配置属性元数据,不影响程序运行,但是还是推荐添加元数据,确保自定义的配置属性有信息提示

    增加可读性

  • 引入Maven依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
  • IDEA为例: 鼠标移动到自定义属性上面,然后点击弹窗的Define configuration key 'xxx'或快捷键Alt+Shift+Enter然后会生成文件src/main/resources/META-INF/additional-spring-configuration-metadata.json

  • additional-spring-configuration-metadata.json 中修改description等即可

    {
      "properties": [
        {
          "name": "stock.page-size",
          "type": "java.lang.String",
          "description": "每页记录数."
      }
    ] }

配置profiles

通过配置profiles可以实现在不同的环境中加载不同的配置,按条件注册bean,如开发环境和生产环境使用不同的配置

1. 创建profile配置文件

1.1 方式一: 定义一个只在生产环境使用的配置文件
  • 文件命名规则: application-{profile name}.ymlapplication-{profile name}.properties

  • application-prob.yml

    logging:
      level:
        root: WARN
        org.springframework.security: WARN
      file:
        path: logs
1.2 方式二: 直接在application.yml中写,只支持YAML配置方式
logging:
  level:
    root: WARN
    org.springframework.security: DEBUG
  file:
    path: logs
# 使用 --- 分隔,下面的为prod配置,等价于创建一个application-prod.yml文件并写入
---
spring:
  profiles: prod

logging:
  level:
    root: WARN
    org.springframework.security: WARN

2. 激活profile配置文件

默认情况下使用的是application.ymlapplication.properties文件中spring.profiles.default的配置(可以省略),如果要使用其他的profile则需要激活

  • spring.profiles.default 默认值,优先级低,当active没有配置时,使用此变量

  • spring.profiles.active 优先级高,指定当前容器使用哪个profile。

  • 直接在application.yml文件配置进行激活(硬编码方式不推荐)

    • 激活单个配置文件

      spring:
        profiles:
          active: prod
    • 激活多个配置文件

      spring:
        profiles:
          active:
            - prod
            - adult
            - ha
    • 通过环境变量进行激活(推荐)

      • 普通方式

        % export SPRING_PROFILES_ACTIVE=prod
      • 如果使用可执行的jar运行的应用程序

        % java -jar taco-cloud.jar --spring.profiles.active=prod
      • 激活多个配置文件

        % export SPRING_PROFILES_ACTIVE=prod,audit,ha

3. 根据不同的profile配置注册不同的bean

对于配置的bean有些可能只在某个profile配置激活的情况下才需要创建,因此需要使用@Profile注解进行按条件注册bean

@Bean
@Profile("dev") //dev激活时创建
public CommandLineRunner dataLoader(IngredientRepository repo,
 UserRepository userRepo, PasswordEncoder encoder) {
 //...
}
@Bean
@Profile({"dev", "qa"}) //dev或者qa激活时创建
public CommandLineRunner dataLoader(IngredientRepository repo,
 UserRepository userRepo, PasswordEncoder encoder) {
 //...
}
@Bean
@Profile("!prod") //prod没有激活时创建
public CommandLineRunner dataLoader(IngredientRepository repo,
 UserRepository userRepo, PasswordEncoder encoder) {
 //...
}
@Profile({"!prod", "!qa"}) //可在类上使用,prob和qa没有激活时创建
@Configuration
public class DevelopmentConfig {
 @Bean
 public CommandLineRunner dataLoader(IngredientRepository repo,
 UserRepository userRepo, PasswordEncoder encoder) {
 //...
 }
}

文章作者: Bryson
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bryson !
评论
 上一篇
下一篇 
  目录