朝小闇的博客

海上月是天上月,眼前人是心上人

Spring实战(一)——Spring基础(含Spring实战第五版源码和pdf)

Spring实战第五版英文版及源码资源链接:https://pan.baidu.com/s/18N8tt62cE5H8yXCRh95Jcw 提取码:ko2n

1.注解原理探索及使用

@Repository、@Controller、@Service、@Component 四个包级注解,都实现了@Bean注解,即可以将标注类交托给springbean作为组件处理,除此之外:

  • @Repository:
    • 数据访问层,一般是mapper或者dao层,将标注类中抛出的数据访问异常封装为Spring数据访问异常;
  • @Controller:
    • 控制层,一般是controller层,标注控制器,实现action,能够填充可选的数据模型model并将请求传递给一个视图view;
  • @Service:
    • 服务层(业务逻辑层),一般是service或者manager层,可以理解为对一个或多个DAO的再次封装;
  • @Component:
    • 泛指各种组件,组件不属于以上三种时使用该注解;

(层次结构):

  • pojo:实体层,实体类;
  • repository/mapper/dao:数据访问层,接口类;
  • manager/service(serviceImpl):服务层,接口类和实现类,一般可直接省略接口直接实现;
  • controller:控制层,实现路由跳转等基本控制;
  • config:配置层;

注解学习请参看博客:http://blog.kunpw.cn/2020/10/23/java/%E6%B3%A8%E8%A7%A3%E5%92%8C%E5%8F%8D%E5%B0%84/

  1. @ConfigurationConfiguration.java

    • 自定义一个Springbean组件;
    • 指示一个类声明了一个或多个@Bean方法,并且可以由Spring容器进行处理,以在运行时为这些bean生成bean定义和服务请求;
    1
    // Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime
  2. @BeanBean.java

    • 通常@Bean方法在@Configuration类中申明,此时bean方法通过直接调用该方法来引用同一类中其它@Bean方法,且@Configuration类及其下方法不能被定义为final或private;
    1
    // Typically, @Bean methods are declared within @Configuration classes. In this case, bean methods may reference other @Bean methods in the same class by calling them directly. This ensures that references between beans are strongly typed and navigable. Such so-called 'inter-bean references' are guaranteed to respect scoping and AOP semantics, just like getBean() lookups would. These are the semantics known from the original 'Spring JavaConfig' project which require CGLIB subclassing of each such configuration class at runtime. As a consequence, @Configuration classes and their factory methods must not be marked as final or private in this mode. 
  3. @ControllerController.java

    • 表示带注释的类是“控制器”(例如Web控制器)。 此注释用作{@link Component @Component}的特化,允许通过类路径扫描自动检测实现类。它通常与基于{@link org.springframework.web.bind.annotation.RequestMapping}注释的带注释的处理程序方法结合使用;
    1
    // Indicates that an annotated class is a "Controller" (e.g. a web controller). <p>This annotation serves as a specialization of {@link Component @Component}, allowing for implementation classes to be autodetected through classpath scanning. It is typically used in combination with annotated handler methods based on the {@link org.springframework.web.bind.annotation.RequestMapping} annotation.
  4. @RestControllerRestController.java

    • 由{@link Controller @Controller}和{@link ResponseBody @ResponseBody}进行注释,即实现两者功能;
    • 相较于@Controller,由于@ResponseBody的作用,对于前端HTML视图页面,如果返回值为String类型,则不会跳转访问html资源,而是直接将字符串写到客户端;
    1
    // A convenience annotation that is itself annotated with {@link Controller @Controller} and {@link ResponseBody @ResponseBody}.
  5. @ResponseBodyResponseBody.java

    • 将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。如果返回值是字符串,那么直接将字符串写到客户端;如果是一个对象,会将对象转化为json串,然后写到客户端;
  6. @RequestMappingRequestMapping.java

    • 通用请求处理,一般用于类级别,其余五个具体请求处理一般用于通用处理下方法级别;

    • 一般在控制器层,和@Controller同时使用;

    • 一般一个控制器使用一个@RequestMapping注解路径,其余不同请求可以分发在相同父路径下;

    • 注解 描述
      @RequestMapping 通用请求处理
      @GetMapping 处理HTTP GET请求
      @PostMapping 处理HTTP Post请求
      @PutMapping 处理HTTP Put请求
      @DeleteMapping 处理HTTP Delete请求
      @PatchMapping 处理HTTP Patch请求
  7. @DataData.java

    • 属于lombok,等价于{@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode},即实现了实体类的多种方法,也就是一个简单的方法封装;
  8. @Slf4j

    • 属于lombok,类级,在运行时,会在这个类中自动生成一个SLF4J(Simple Logging Facade For Java) Logger,生成日志;
  9. @Qualifier

    • 当一个接口有多个实现类的时候,用来指明具体调用哪一个实现类;

其余亟待补充……


2.Spring结构原理

image-20210318100228918

Spring的核心是提供一个容器(container),通常又称为Spring应用上下文(Spring application context),该容器会创建和管理应用组件(bean)

依赖注入(dependency injection,DI):将bean装配在一起,应用依赖于容器来创建和维护所有组件,并将其注入到需要它们的bean中,而bean不需要再去创建和管理它所依赖的组件。

Bean生命周期:

img

2.1 SpringBoot DevTools:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
  • 代码变更自动重启,即热部署(有三种方式,devtools只是一种):
    • DevTools运行时,应用程序会被加载到JVM两个独立的类加载器中,一个类加载器加载依赖的库,不常变化,另一个类加载器加载java代码、属性文件以及src/main/的文件,经常发生变化;
    • 当探测到变更的时候,DevTools会且仅重新加载包含项目代码的类加载器,并重启Spring的应用上下文
  • 面向浏览器的资源如模板、JS、CSS发生变化时,自动刷新浏览器;
  • 自动禁用模板缓存,相当于Thymeleaf模板在配置文件手动关闭缓存(部署到生产环境时要打开缓存,缓存只在第一次请求时解析一次);
  • 如果使用H2数据库,能内置H2控制台;

2.2 Controller(控制器层):

整个框架服务调用过程为:实体层创建实体对象、数据访问层根据实体对象连接数据库操作、服务层对数据访问层的方法进行再次封装实现具体服务,控制器层调用服务实现请求响应。

填充数据模型Model:

1
2
3
4
5
6
7
8
9
// 删去了具体内容,只留下了Model部分展示
@Controller
public class EmployeesController {
@GetMapping("/employees")
public String getAllEmployees(Model model){
model.addAttribute("emps",employees);
return "employee/list";
}
}
  • 请求访问该/employees路径时,Model对象model在方法中以参数形式传入用于增加具体属性,存放在Model属性中的数据将会复制到Servlet request的属性中,视图employee/list从Servlet Response获取Model属性数据;
  • 视图层无法直接感知Spring的模型抽象,无法获取Model属性数据,只能通过第三方Servlet的属性协作获取数据;

与Config视图映射比较:

  • Config继承WebMvcConfigurer并重写addViewControllers方法:
    • 该视图映射仅实现路由跳转,而没有Controller中的Model和View交互,一般用于统一配置无动作视图;
    • 本质上是自动生成了简单的视图控制器;
1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
// 视图映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
// 将所有/main.html路径全部映射到dashboard页面
registry.addViewController("/main.html").setViewName("dashboard");
}
}

3.数据交互

3.1 Spring JdbcTemplate:

  • JdbcTemplate 实现了增删改查方法,不过需要自己显式写入sql语句放入方法传参;
  • 也可以通过占位符传参,具体实战可以参看本人博客SpringBoot系列
1
2
3
4
5
6
7
8
9
10
11
// Template是Spring默认配置好的模板bean,可以拿来即用
@Autowired
JdbcTemplate jdbcTemplate;

// 查询数据库所有信息,没有实体类时使用Map获取数据库中的数据
@GetMapping("/userList")
public List<Map<String,Object>> userList(){
String sql = "select * from user";
List<Map<String,Object>> mapList = jdbcTemplate.queryForList(sql);
return mapList;
}

3.2 Spring Data JPA:

Spring Data 由多个子项目组成,其中大多数子项目都关注对不同的数据库类型进行数据持久化。JPA即Java Persistence API,java持久层API。

比较流行的一些Spring Data子项目包括:

  • Spring Data JPA:基于关系型数据库进行JPA持久化;
  • Spring Data MongoDB:持久化到MongoDB文档数据库;
  • Spring Data Neo4j:持久化到Neo4j图数据库;
  • Spring Data Redis:持久化到Redis key-value存储;
  • Spring Data Cassandra:持久化到Cassandra数据库;

Spring Data 为所有项目提供了一项特性:基于repository规范接口自动生成repository的功能。

  1. 添加Spring Data JPA的Maven依赖:默认传递性引入Hibernate的JPA实现

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
  2. 映射注解标注实体类:

    • 类级@Entity注解;
    • id属性使用@Id注解;
    • 类级@NoArgsConstructor(Lombok无参构造);
  3. 声明JPA repository:继承相应接口,如CrudRepository能自动实现增删查改接口

    1
    2
    public interface repository extends CrudRepository<pojo,String>{
    }
    • 其中第一个参数pojo是相应实体类,第二个接口是响应实体类中@Id标注属性的数据类型;
  4. 当然,除继承JPA repository外,还可以自定义JPA repository,这里不赘述;

推荐一篇Spring Data JPA文章:https://blog.csdn.net/wujiaqi0921/article/details/78789087

4.SpringSecurity

本部分具体实战可观看本人SpringBoot系列。

  1. 启用SpringSecurity,添加maven依赖:

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
  2. 配置SpringSecurity:

    1
    2
    3
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    }

    SpringSecurity配置用户存储有以下几种方式,都是通过认证实现:

    1
    2
    3
    4
    // 重写configure(AuthenticationManagerBuilder auth)
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    }
    • 基于内存的用户存储:

      1
      2
      3
      4
      5
      6
      7
      8
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      // 此处密码未加密,直接存储信息于内存中
      auth.inMemoryAuthentication()
      .withUser("admin").password("123456").authorities("ROLE_ADMIN")
      .and()
      .withUser("root").password("123456").authorities("ROLE_ROOT");
      }
    • 基于JDBC的用户存储:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 设置一个数据源
      @Autowired
      DataSource dataSource;
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.jdbcAuthentication().dataSource(dataSource)
      .usersByUsernameQuery("select username,password,enable from Users" + "where username=?")
      .authoritiesByUsernameQuery("select username,authority from UserAuthorities" + "where username=?")
      .passwordEncoder(new BCryptPasswordEncoder());
      // passwordEncoder为密码转码器,数据库中的密码不会解码(已转码),只对登录时的密码进行转码匹配
      }

      SpringSecurity加密模块:也可以尝试自定义加密

      BCryptPasswordEncoder 使用bcrypt强哈希加密
      NoOpPasswordEncoder 不进行任何转码,已弃用
      Pbkdf2PasswordEncoder 使用PBKDF2加密
      SCryptPasswordEncoder 使用scrypt哈希加密
      StandardPasswordEncoder 使用SHA-256哈希加密,已弃用
    • 以LDAP作为后端的用户存储:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      // 分别搜索用户和组,提供基础查询和提供过滤
      // passwordCompare比对认证方式是在LDAP服务期内完成的实际密码能保持私密,并对密码转码以及设置配对的密码属性
      // 引用远程LDAP服务器或配置嵌入式LDAP服务器
      auth.ldapAuthentication()
      .userSearchBase("ou=people")
      .userSearchFilter("(uid={0})")
      .groupSearchBase("ou=groups")
      .groupSearchFilter("member={0}")
      .passwordCompare()
      .passwordEncoder(new BCryptPasswordEncoder())
      .passwordAttribute("passcode")
      .and()
      .contextSource()
      .root("dc=com");
      }
    • 自定义用户详情服务:

      • 定义实体用户类以及持久化JPA(@Entity)实现SpringSecurity的UserDetails接口;
      • 重写getAuthorities方法获取用户权限;
      • 相应创建用户详情服务实现UserDetailsService接口;
    • 对于密码转码器,可以配置一个默认转码器统一转码所有密码,加入Bean:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 由于Bean注解在Spring应用上下文中申明PasswordEncoder bean,对于encoder()的任何调用都会被拦截,并且返回应用上下文中的bean实例
      @Bean
      public PasswordEncoder encoder(){
      return new BCryptPasswordEncoder();
      }
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.userDetailsService(userDetailsService)
      .passwordEncoder(encoder());
      }

4.1 用户注册

  1. RegistrationController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Controller
    @RequestMapping("/register")
    public class RegistrationController {

    private UserRepository userRepo;
    private PasswordEncoder passwordEncoder;

    public RegistrationController(
    UserRepository userRepo, PasswordEncoder passwordEncoder) {
    this.userRepo = userRepo;
    this.passwordEncoder = passwordEncoder;
    }
    @GetMapping
    public String registerForm() {
    return "registration";
    }
    // save方法是CrudRepository接口中定义的,即增加一个新的实体账号
    @PostMapping
    public String processRegistration(RegistrationForm form) {
    userRepo.save(form.toUser(passwordEncoder));
    return "redirect:/login";
    }
    }
  2. 增加表单视图registration

  3. 增加实体类RegistrationForm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Data
    public class RegistrationForm {

    private String username;
    private String password;
    private String fullname;
    private String street;
    private String city;
    private String state;
    private String zip;
    private String phone;

    public User toUser(PasswordEncoder passwordEncoder) {
    return new User(
    username, passwordEncoder.encode(password),
    fullname, street, city, state, zip, phone);
    }
    }

4.2 保护Web请求

通过configure(HttpSecurity http)权限设置保护:

1
2
3
@Override
protected void configure(HttpSecurity http) throws Exception {
}

可配置功能包括:

  • 请求拦截;
  • 配置自定义登录页以及登录跳转页;
  • 支持用户退出登录以及退出跳转页;
  • 预防跨站请求伪造CSRF;
  • ……
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
protected void configure(HttpSecurity http) throws Exception {
// authorizeRequests权限请求,具备相应权限才能访问/design、/orders路径,且不同antMatchers顺序很重要,前序优先级高
// 设置权限方法一hasRole('ROLE_USER')、permitAll,方法二是access(),后者能通过表达式丰富安全规则
http
.authorizeRequests()
.antMatchers("/design", "/orders")
.access("hasRole('ROLE_USER')")
.antMatchers("/", "/**").access("permitAll")

// formLogin()下loginPage()替换内置登录页
// defaulySuccessUrl("",true)有true登录则强制跳转,没有则只默认跳转,如登录前已经访问页面则跳转相应页面
// loginProcessingUrl()监听对/authenticated的请求并设置username/password参数对应form表单参数
.and()
.formLogin()
.loginPage("/login")
.defaulySuccessUrl("/design")
.loginProcessingUrl("/authenticated")
.usernameParameter("user")

// logout()退出,下设logoutSuccessUrl()退出成功跳转
.and()
.logout()
.logoutSuccessUrl("/")

// 预防CSRF方法是在表单中增加一个隐藏域存放CSRF token,随着表单提交一起提交
// SpringSecurity提供内置CSRF保护,默认开启,需要确保表单中有一个名为"_csrf"的字段持有CSRF token
// <input type="hidden" name="_csrf" th:value="${_csrf.token}" />
// 使用SpringMVC的JSP标签库或者SpringSecurity-Thymeleaf则不需要明确包含这个隐藏域(相关实战请参考本人博客SpringBoot系列)
.and()
.csrf()
.ignoringAntMatchers("/h2-console/**");
}
  1. authorizeRequests()拦截路径配置:

    方法 含义
    access(String) 给定的SpEL表达式结果为true,允许访问
    anonymous() 允许匿名用户访问
    authenticated() 允许认证过的用户访问
    denyAll() 无条件拒绝所有访问
    fullyAuthenticated() 用户不是通过rememberMe功能认证的,就允许访问
    hasAnyAuthority(String…) 用户具备给定权限中的一种则允许访问
    hasAnyRole(String…) 用户具备给定角色中的一种则允许访问
    hasAuthority(String) 用户具备给定权限则允许访问
    hasIpAddress(String) 请求来自给定的IP地址则允许访问
    hasRole(String) 用户具备给定角色则允许访问
    not() 对其它访问方法结果求反
    permitAll() 无条件允许所有访问
    rememberMe() 用户通过rememberMe功能认证则允许访问
  2. access()的扩展SpEL:

    安全表达式 计算结果
    authentication 用户的认证对象
    denyAll 结果始终为false
    hasAnyRole(list of roles) 如果用户被授予了列表中任意指定角色,结果为true
    hasRole(role) 如果用户被授予了指定角色,结果为true
    hasIpAddress(IP Address) 如果请求来自指定IP,结果为true
    isAnonymous() 如果当前用户为匿名用户,结果为true
    isAuthenticated() 如果当前用户进行了认证,结果为true
    isFullyAuthenticated() 如果当前用户不是通过rememberMe功能进行的认证,结果为true
    isRememberMe() 如果当前用户是通过RememberMe功能进行的认证,结果为true
    permitAll 结果始终为true
    principal 用户的principal对象

4.3 获取登录用户信息

常用方式:

  • 注入Principal对象到控制器方法中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 能够正常运行,但是在与安全无关的功能中掺杂了安全性的代码
    @PostMapping
    public String processRegistration(@Valid Order order, Errors errors,
    SessionStatus sessionStatus,
    Principal principal) {
    ...
    User user = userRepository.findByUsername(principal.getName());
    order.setUser(user);
    ...
    }
  • 注入Authentication对象到控制器方法中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // getPrincipal()返回的是java.lang.Object对象,需要转换数据类型
    @PostMapping
    public String processRegistration(@Valid Order order, Errors errors,
    SessionStatus sessionStatus,
    Authentication authentication) {
    ...
    User user = (User) authentication.getPrincipal();
    order.setUser(user);
    ...
    }
  • 使用SecurityContextHolder来获取安全上下文:

    1
    2
    3
    // 该方法包含大量安全性代码,但是优势是可以在程序的任何地方使用,不仅仅在控制器中,非常适合在较低级别的代码中使用
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    User user = (User)authentication.getPrincipal();
  • 使用@AuthenticationPrincipal注解来标注方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 这种方式更为简洁,直接接收一个User参数,但是需要添加注解通过认证才会变成认证的令牌
    // 且该方式不需要类型转换,同时能将安全相关的代码紧紧局限于注解本身
    @PostMapping
    public String processRegistration(@Valid Order order, Errors errors,
    SessionStatus sessionStatus,
    @AuthenticationPrincipal User user) {
    ...
    if(errors.hasErrors()){
    return "orderForm";
    }
    order.setUser(user);
    ...
    }
  • TODO->principal、authentication、SecurityContextHolder在生命周期中的位置;

5.配置

在Spring中有两种配置:

  • bean装配:声明在Spring应用上下文中创建哪些应用组件以及它们之间如何互相注入的配置;
  • 属性注入:设置Spring应用上下文中bean的的配置;

Spring环境抽象:

  • 属性源:
    • JVM系统属性;
    • 操作系统环境变量;
    • 命令行参数;
    • Application.properties;
    • Application.yml;
  • Spring环境从各个属性源拉取属性,并让Spring应用上下文中的bean使用它们;
  • 在yml文件中设置属性的时候可以使用${}占位符;
image-20210408211818632

5.1 创建自己的配置属性

以书籍源码ch05为例。

为了支持配置属性的诸如,Spring Boot提供了@ConfigurationProperties注解,在OrderController中添加方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
// 意味着设置pageSize时要使用名为taco.orders.page-size属性
@ConfigurationProperties(prefix="taco.orders")
public class OrderController {
private int pageSize = 20;
@GetMapping
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
// 增加属性用于配置数量值,Pageable是Spring Data格局页号和煤业数量选取结果的子集的一种方法
// 源码中是将自定义配置属性单独拎出来作为一个配置属性类
Pageable pageable = PageRequest.of(0, pageSize);
// 在OrderRepository接口中增加findByUserOrderByPlacedAtDesc()方法,设置数量降序排列
model.addAttribute("orders",
orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));

return "orderList";
}
}

注入bean并单独持有配置属性:

  • @ConfigurationProperties实际上通常会放到一种特定类型的bean中,这种bean的目的就是持有配置数据;
  • 这样能够更加整洁,并且其余代码部分也能引用此属性;
  1. 创建配置类OrderProps

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // @Component注解会将其扫描成bean
    @Component
    @ConfigurationProperties(prefix="taco.orders")
    @Data
    @Validated
    public class OrderProps {
    @Min(value=5, message="must be between 5 and 25")
    @Max(value=25, message="must be between 5 and 25")
    private int pageSize = 20;

    }
  2. 直接在OrderController中引用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Controller
    @RequestMapping("/orders")
    @SessionAttributes("order")
    public class OrderController {
    private OrderProps props;
    public String ordersForUser(
    @AuthenticationPrincipal User user, Model model) {
    // 增加属性用于配置数量值,Pageable是Spring Data格局页号和煤业数量选取结果的子集的一种方法
    // 源码中是将自定义配置属性单独拎出来作为一个配置属性类
    Pageable pageable = PageRequest.of(0, props.getPageSize);
    // 在OrderRepository接口中增加findByUserOrderByPlacedAtDesc()方法,设置数量降序排列
    model.addAttribute("orders",
    orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));

    return "orderList";
    }
    }
  3. 声明配置属性元数据:不声明也可以在yml中使用,但是会没有提示

    • 在项目路径src/main/resources/META-INF目录下新建文件additional-spring-configuration-metadata.json

      1
      2
      3
      4
      5
      6
      7
      8
      // 对了,注意在yml文件中pageSize属性可以使用page-size表示
      {"properties": [
      {
      "name": "taco.orders.page-size",
      "type": "java.lang.String",
      "description": "Sets the maximum number of orders to display in a list."
      }
      ]}

5.2 使用profile进行配置

当应用部署到不同的运行时环境时,有些配置细节会有差异,通常使用环境变量这种方式来制定配置属性解决问题,而不是在yml文件中进行定义。

定义特定profile相关的属性的两种方式:

  • 创建另一个yml或属性文件,命名遵守约定:application-{profile}.ymlapplication-{profile}.properties

  • application.yml文件中通过---符号分割,并使用spring.profiles属性命名profile:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    taco:
    orders:
    pageSize: 10

    discount:
    codes:
    abcdef: 10

    ---
    spring:
    profiles: prod

    datasource:
    url: jdbc:mysql://localhost/tacocloud
    username: tacouser
    password: tacopassword

    logging:
    level:

激活profile:

  • 方式一:不好,意味着一直默认开启某一个profile,这体现不出不同profile的优越处

    1
    2
    3
    4
    spring:
    profiles:
    active:
    - prod
  • 方法二:通过环境变量配置,一般使用这种方式

    1
    % export SPRING_PROFILES_ACTIVE=prod

5.3 在不同profile条件下条件化创建bean

增加@Profile注解:

1
2
3
4
@Bean
@Profile({"dev","prod"})
// 或者取反
@Profile("!dev")

OK,Spring实战第一部分Spring基础就到这里了,大家想要详细实战的话可以参看本人博客SpringBoot系列。

-------- 本文结束 感谢阅读 --------