springBoot实战 第三章自定义配置

本章内容 
覆盖自动配置的Bean 
用外置属性进行配置 
自定义错误页

3.1 覆盖Spring Boot自动配置

一般来说,如果不用配置就能得到和显式配置一样的结果,那么不写配置是直接的选择。

既然如此,那干嘛还要多做额外的工作呢?如果不用编写和维护额外的配置代码也行,那何必还要它们呢? 大多数情况下,自动配置的Bean刚好能满足你的需要,不需要去覆盖它们。但某些情况下, Spring Boot在自动配置时还不能很好地进行推断。这里有个不错的例子:当你在应用程序里添加安全特性时,自动配置做得还不够好。

安全配置并不是放之四海而皆准的,围绕应用程序安全有很多决策要做,Spring Boot不能替你做决定。

虽然Spring Boot为安全提供了一些基本的自动配置,但是你还是需要自己覆盖一些配置以满足特 定的安全要求。

想知道如何用显式的配置来覆盖自动配置,我们先从为阅读列表应用程序添加Spring Security入手。

在了解自动配置提供了什么之后,我们再来覆盖基础的安全配置,以满足特定的 场景需求

3.1.1 保护应用程序
Spring Boot自动配置让应用程序的安全工作变得易如反掌,
你要做的只是添加Security起步 依赖。以Gradle为例,应添加如下依赖:
compile(“org.springframework.boot:spring-boot-starter-security”)

如果使用Maven,那么你要在项目的块中加入如下:

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

这样就搞定了!重新构建应用程序后运行即可,现在这就是一个安全的Web应用程序了!
Security起步依赖在应用程序的Classpath里添加了Spring Secuirty(和其他一些东西)。
Classpath里 有Spring Security后,自动配置就能介入其中创建一个基本的Spring Security配置。

试着在浏览器里打开该应用程序,你马上就会看到HTTP基础身份验证对话框。
此处的用户 名是user,密码就有点麻烦了。密码是在应用程序每次运行时随机生成后写入日志的,

你需要查 找日志消息(默认写入标准输出),找到此类内容:
Using default security password: d9d8abe5-42b5-4f20-a32a-76ee3df658d9

3.1.2 创建自定义的安全配置

package com.example.readinglist;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;/*** 覆盖自动配置很简单,就当自动配置不存在,直接显式地写一段配置。* 这段显式配置的形式 不限,Spring支持的XML和Groovy形式配置都可以。* 在编写显式配置时,我们会专注于Java形式的配置。在Spring Security的场景下,* 这意味着写 一个扩展了WebSecurityConfigurerAdapter的配置类*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate ReaderRepository readerRepository;@Overrideprotected  void configure(HttpSecurity http)throws Exception{http.authorizeRequests().antMatchers("/").access("hasRole('READER')").antMatchers("/**").permitAll().and().formLogin().loginPage("/login").failureUrl("/login?error=true");}@Overrideprotected  void configure(AuthenticationManagerBuilder auth)throws Exception{auth.userDetailsService(new UserDetailsService() {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//eturn readerRepository.getOne(username);return readerRepository.getOne(username);}});}
}
package com.example.readinglist;import org.springframework.data.jpa.repository.JpaRepository;
import  java.util.List;/*** 接下来,我们就要定义用于把Book对象持久化到数据库的仓库了。* ①因为用了Spring Data JPA, 所以我们要做的就是简单地定义一个接口,* 扩展一下Spring Data JPA的JpaRepository接口:*//*** 通过扩展JpaRepository,ReadingListRepository直接继承了18个执行常用持久化操作 的方法。* JpaRepository是个泛型接口,有两个参数:仓库操作的领域对象类型,及其ID属性的 类型*/
public interface ReadingListRepository extends JpaRepository<Book,Long> {/*** 可以根据读者的用户名来查找阅读列表。* @param reader* @return List*/List<Book> findByReader(String reader);
}
package com.example.readinglist;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;import java.util.List;//这样组件扫描会自动将ReadingListController注册为 Spring应用程序上下文里的一个Bean
@Controller
//将其中所有的处理器 方法都映射到了“/”这个URL路径上。
@RequestMapping("/")
public class ReadingListController {private ReadingListRepository readingListRepository;public ReadingListController(ReadingListRepository readingListRepository){this.readingListRepository = readingListRepository;}/*** 处理/{reader}上的HTTP GET请求,根据路径里指定的读者,从(通 过控制器的构造器注入的)仓库获取Book列表。* 随后将这个列表塞入模型,用的键是 books,后返回readingList作为呈现模型的视图逻辑名称。* @param reader* @param model* @return String*/@RequestMapping(value="/{reader}",method = RequestMethod.GET)public String readersBooks(@PathVariable("reader") String reader, Model model){List<Book> readingList = readingListRepository.findByReader(reader);if(readingList != null){model.addAttribute("books",readingList);}return "readingList";}/***  addToReadingList():处理/{reader}上的HTTP POST请求,将请求正文里的数据绑定 到一个Book对象上。*  该方法把Book对象的reader属性设置为读者的姓名,*  随后通过仓 库的save()方法保存修改后的Book对象,*  后重定向到/{reader}(控制器中的另一个方 法会处理该请求)。* @param reader* @param book* @return*/@RequestMapping(value = "/{reader}",method = RequestMethod.POST)public String addToReadingList(@PathVariable("reader") String reader,Book book){book.setReader(reader);readingListRepository.save(book);return "redirect:/{reader}";}
}
package com.example.readinglist;import org.springframework.data.jpa.repository.JpaRepository;//通过JPA持久化读者 Java Persistence API
public interface ReaderRepository extends JpaRepository<Reader,String> {}
package com.example.readinglist;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Arrays;
import java.util.Collection;//Reader用了@Entity注解,所以这是一个JPA实体
@Entity
//你应该还注意到Reader实现了UserDetails接口以及其中的方法,这样Reader就能代表 Spring Security里的用户了
public class Reader implements UserDetails {private static final long serialVersionUID = 1L;
//此外,它的username字段 上有@Id注解,表明这是实体的ID@Idprivate String username;private String fullname;private String password;public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public String getUsername() {return username;}public String getFullname() {return fullname;}public void setFullname(String fullname) {this.fullname = fullname;}public void setPassword(String password) {this.password = password;}/**getAuthorities()方法被覆盖过了,始终会为用户授予READER 权限* 授予Reader权限* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return Arrays.asList(new SimpleGrantedAuthority("READER"));}//不过期,不加锁,不禁用//isAccountNonExpired()、 isAccountNonLocked()、isCredentialsNonExpired()// 和isEnabled()方法都返回true,这样读者账户就不会过期,不会被锁定,也不会被撤销。@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}/*
在一个大型应用程序里,赋予用户的授权本身也可能是实体,它们被 维护在独立的数据表里。
同样,表示一个账户是否为非过期、非锁定且可用的布尔值也 是数据库里的字段。
但是,出于演示考虑,我决定让这些细节保持简单,
以免分散我们 的注意力,影响正在讨论的话题——我说的是覆盖Spring Boot自动配置。*//*
再重申一次,想要覆盖Spring Boot的自动配置,你所要做的仅仅是编写一个显式的配置。Spring Boot会发现你的配置,随后降低自动配置的优先级,以你的配置为准。
想弄明白这是如何 实现的,让我们揭开Spring Boot自动配置的神秘面纱,
看看它是如何运作的,以及它是怎么允许 自己被覆盖的。*/
package com.example.readinglist;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;import java.util.List;//这样组件扫描会自动将ReadingListController注册为 Spring应用程序上下文里的一个Bean
@Controller
//将其中所有的处理器 方法都映射到了“/”这个URL路径上。
@RequestMapping("/")
public class ReadingListController {private ReadingListRepository readingListRepository;public ReadingListController(ReadingListRepository readingListRepository){this.readingListRepository = readingListRepository;}/*** 处理/{reader}上的HTTP GET请求,根据路径里指定的读者,从(通 过控制器的构造器注入的)仓库获取Book列表。* 随后将这个列表塞入模型,用的键是 books,后返回readingList作为呈现模型的视图逻辑名称。* @param reader* @param model* @return String*/@RequestMapping(value="/{reader}",method = RequestMethod.GET)public String readersBooks(@PathVariable("reader") String reader, Model model){List<Book> readingList = readingListRepository.findByReader(reader);if(readingList != null){model.addAttribute("books",readingList);}return "readingList";}/***  addToReadingList():处理/{reader}上的HTTP POST请求,将请求正文里的数据绑定 到一个Book对象上。*  该方法把Book对象的reader属性设置为读者的姓名,*  随后通过仓 库的save()方法保存修改后的Book对象,*  后重定向到/{reader}(控制器中的另一个方 法会处理该请求)。* @param reader* @param book* @return*/@RequestMapping(value = "/{reader}",method = RequestMethod.POST)public String addToReadingList(@PathVariable("reader") String reader,Book book){book.setReader(reader);readingListRepository.save(book);return "redirect:/{reader}";}
}


3.1.3 掀开自动配置的神秘面纱

Spring Boot的DataSourceAutoConfiguration中定义的JdbcTemplate Bean就是一个非常简 单的例子,
演示了@ConditionalOnMissingBean如何工作:

@Bean
@ConditionalOnMissingBean(JdbcOperations.class)public JdbcTemplate jdbcTemplate() {   return new JdbcTemplate(this.dataSource); }

jdbcTemplate()方法上添加了@Bean注解,在需要时可以配置出一个JdbcTemplate Bean。
但它上面还加了@ConditionalOnMissingBean注解,
要求当前不存在JdbcOperations 类型(JdbcTemplate实现了该接口)的Bean时才生效。
如果当前已经有一个JdbcOperations Bean了,条件即不满足,不会执行jdbcTemplate()方法。
什么情况下会存在一个JdbcOperations Bean呢?Spring Boot的设计是加载应用级配置,随后再考虑自动配置类。
因此,如果你已经配置了一个JdbcTemplate Bean,
那么在执行自动配置 时就已经存在一个JdbcOperations类型的Bean了,于是忽略自动配置的JdbcTemplate Bean

关于Spring Security,自动配置会考虑几个配置类。
在这里讨论每个配置类的细节是不切实 际的,但覆盖Spring Boot自动配置的安全配置时,
重要的一个类是SpringBootWebSecurity- Configuration。以下是其中的一个代码片段:
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ EnableWebSecurity.class })
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
@ConditionalOnWebApplication public class SpringBootWebSecurityConfiguration {

}

如你所见,SpringBootWebSecurityConfiguration上加了好几个注解。
看到@Condi- tionalOnClass注解后,你就应该知道Classpath里必须要有@EnableWebSecurity注解。
@ConditionalOnWebApplication 说明这必须是个Web 应用程序。
@ConditionalOn- MissingBean注解才是我们的安全配置类代替SpringBootWebSecurityConfiguration的关 键所在。

@ConditionalOnMissingBean注解要求当下没有WebSecurityConfiguration类型的 Bean。
虽然表面上我们并没有这么一个Bean,但通过在SecurityConfig上添加@EnableWeb-Security注解,
我们实际上间接创建了一个WebSecurityConfiguration Bean。所以在自动 配置时,这个Bean就已经存在了,
@ConditionalOnMissingBean条件不成立,SpringBoot- WebSecurityConfiguration提供的配置就被跳过了

虽然Spring Boot的自动配置和@ConditionalOnMissingBean让你能显式地覆盖那些可以 自动配置的Bean,但并不是每次都要做到这种程度。
让我们来看看怎么通过设置几个简单的配置 属性调整自动配置组件吧。

3.2 通过属性文件外置配置

待续…

《SpringBoot实战》笔记3相关推荐

  1. java虚拟机内存分为,深入理解Java虚拟机笔记(一)----内存划分

    Java内存划分 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,如下图 一.程序计数器 程序计数器(Program Counter Register)是一块很小 ...

  2. 深入理解Java虚拟机--笔记1

    Java内存区域与内存溢出异常 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域. 1 程序计数器--Program Counter Regis ...

  3. 深入理解java虚拟机-笔记

    java内存区域与内存溢出异常 java虚拟机自动内存管理机制,不用像C/C++为每一个new操作去写配对delete/free代码 java虚拟机在执行java程序的过程中 会把内存划分为若干个不同 ...

  4. 深入理解Java虚拟机 笔记

    对象内存布局: 对象头 实例数据 对齐填充 对象头: Mark Word:存储自身的运行时数据,如hashcode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳 类型指针,指向其类 ...

  5. 深入理解java虚拟机 新生代_深入理解java虚拟机:笔记

    1.运行时数据区域 1.程序计数器 当前线程执行字节码的行号指示器,字节码解释器工作通过改变这个计数器的值来选取下一条需要执行的字节码指令,每一个线程拥有独立的程序计数器,线程私有的内存 2.虚拟机栈 ...

  6. 深入理解Java虚拟机笔记之六内存分配与回收策略

    对象的内存分配,对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配.少数情况下也可能会直接分配在老年代中,分配的规则并不是百分百固定的,其细节取决于当前使用的 ...

  7. 2020-5-9 开始阅读深入理解java虚拟机

    深入理解java虚拟机笔记 day1 读完前三章 关于Jit编译器和解释器和关系,见博客:https://www.cnblogs.com/insistence/p/5901457.html HotSp ...

  8. 《深入理解JAVA虚拟机》详细解读(第二章 ):JAVA内存区域与内存溢出异常

    目录 一.JAVA内存区域与内存溢出异常 1. 概述 2. 运行时数据区域 2.1 程序计数器 2.2 Java虚拟机栈 2.3本地方法栈 2.4 堆 2.5 方法区 2.6 运行时常量池 2.7直接 ...

  9. 《深入理解Java虚拟机 - Jvm高级特性与最佳实践(第三版)》阅读笔记

    <深入理解Java虚拟机>阅读笔记 本repository为<深入理解Java虚拟机 - Jvm高级特性与最佳实践(第三版)>阅读笔记,因为第一章主要讲的是Java的发展历史, ...

  10. 《深入理解java虚拟机》学习笔记之虚拟机即时编译详解

    郑重声明:本片博客是学习<深入理解java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块 ...

最新文章

  1. Ukbench图像数据集
  2. 高并发,分布式系统要点
  3. python的总结与心得词云设计理念_Python编程语言:使用词云来表示学习和工作报告的主题...
  4. vue-router 动态路由匹配
  5. Tornado 高并发源码分析之六---异步编程的几种实现方式
  6. layui 表格点击图片放大
  7. 【Kafka】【未解决】kafka反序列化数据报错jackson2 JsonParseException: Invalid UTF-8 middle byte 0xc0
  8. Pytorch安装步骤
  9. 流水灯及注释c语言,流水灯 - 单片机教程 - C语言网
  10. SUM OF SUB RECTANGLE AREAS(打表+oeis+c++大数类板子)
  11. 指纹算法 c语言,指纹识别算法研究
  12. 正则表达式最好的书籍_正则表达式的最佳做法
  13. npoi 删除多行 操作excel_NPOI操作EXCEL 【只争朝夕】
  14. Windows下和Linux下VS Code与C/C++ Python Go Java Vue3 Git vim Latex开发环境搭建和配置史上最详细易懂的讲解
  15. html 英文逗号,英语写作中不可小觑的五大错误 逗号别乱用
  16. 在我的电脑里计算机管理在哪里,电脑控制面板在哪里查找步骤 一起了解下吧...
  17. 回顾知识点:计算机网络篇
  18. office 2010很强大很好用
  19. 在线文档预览解决方案-Office Web Apps在Windows Server 2008 R2部署教程
  20. 数仓 - 促销敏感度、评论敏感度

热门文章

  1. JProfiler 使用说明
  2. gazebo打不开world
  3. 高通又失一员大将,英特尔成功挖角高通CFO
  4. STM32F4: Generating parallel signals with the FSMC
  5. 带你快速玩转canvas——写个折线图
  6. Windows button控件(按钮控件)
  7. iOS.Debug.Simulator
  8. 205615872 能用来干么?
  9. [Linux]不可重入函数
  10. js 使用Math函数取得数组最大最少值