【笔记】SpringBoot

  • @SpringBootAppliaction
    • 相当于下面三个注解:
    • @EnableAutoConfiguration:启动自动装配。
    • @ComponentScan:在当前包启动组件扫描。
    • @SpringBootConfiguration:@Configuration的特例,将该类声明为配置类。
  • @Controller
    • @Component的特例
    • 用于表明该类为控制器类,@Controller注解的类会自动被扫描并检测到,通常与@RequestMapping搭配来表明该类处理Web请求。
    • Spring会扫描所有带@Controller注解的类,并在其中寻找@RequestMapping注解,以便映射请求到这些类的方法当中。
  • @RequestMapping
    • 可用于类或者方法
    • 当用于类时,用于表明该类接收Web请求,并可指明该类负责的URL范围,其所有方法都接收该范围内的特定请求。
    • 当用于方法时,通常会使用下面几个对应HTTP动作的特定注解:
      • @GetMapping
      • @DeleteMapping
      • @PostMapping
      • @PutMapping
      • @PatchMapping
  • @Slf4j
    • Slf4j的全称:Simple Logging Facade for Java
    • Slf4j是一个日志解决方案规范。它本身是不提供日志功能的具体实现的,而是提供了日志功能的一种API规范。具体的实现可以是log4j、log4j2等日志组件,默认实现是logback。
    • Slf4j使用统一的接口屏蔽了具体日志组件的差异,在项目中使用Slf4j可以自由切换日志功能的实现而无需更改日志输出的代码。
    • @Slf4j注解是由Lombok提供的,可以在当前类自动生成一个Slf4j的Logger静态属性。
  • @SessionAttributes
    • 该注解用于类型级别,声明一个将在Serlet HTTP对话中保持的模型属性,通常和@ModelAttribute搭配使用。
  • @ModelAttribute
    • 该注解用于绑定模型属性到某个方法或者方法参数中,这些属性会可以在View层中访问。
    • 当用于注解方法时,表示该方法将创建并添加模型属性。Spring会保证@ModelAttribute注解的方法一定会在@RequestMapping注解的方法调用之前被调用。
    • 当用于注解方法参数时,表示应当从模型属性中检索来获取该参数。如果没有使用该注解,那么方法参数将尝试从请求属性中检索。
  • @Valid
    • 该注解由JavaBean Validation API提供(JSR-303)
    • 当用于注解方法参数时,表示该参数需要进行校验,校验的时机是数据绑定之后,请求处理的方法被调用之前,若校验不通过,请求将响应HTTP 400 Bad Request。
  • @Repository
    • @Component的特例
    • 该注解用于声明一个数据访问层(持久层)对象,即DAO。
    • 使用该注解,Spring会将特定的数据访问组件抛出的异常转换为DataAccessException并重新抛出,这是通过PersistenceExceptionTranslationPostProcessor实现的。
  • JDBC
    • 全称:Java Database Connectivity
    • 广义上说,JDBC是JAVA中用于连接和读写数据库的一种解决方案。狭义上说,JDBC是指SUM公司为JAVA语言提供的一套用于读写数据库的标准接口(即API规范),数据库厂商可以按照JDBC API编写适用于本家数据库的JDBC驱动,程序员通过加载驱动即可使用JDBC API读写特定的数据库了。
    • JDBC是一个比较底层的数据库读写的方式(类似于ADO.NET),它依旧需要在代码中编写SQL语句并手动进行数据和对象之间的映射。
  • JdbcTemplate
    • JdbcTemplate类是Spring在原生JDBC上的一层封装,它简化了使用JDBC时要编写的重复性代码,包括异常处理等。同时它也作为Spring JDBC中最基础的类。
  • JPA
    • 全称:Java Persistence API
    • 与JDBC一样,JPA其实是一种标准,不同的是它是为ORM框架定制的持久化标准,JPA有许多实现,比如Hiberenate。
    • JPA标准在JDBC标准之上规定了很多新的内容,包括对象数据映射、读缓存、实体导航等。
  • Spring Data
    • Spring Data是Spring技术栈中的用于简化数据访问层(DAO)代码编写的模块,它的主要目的是为了将程序员从琐碎的样板代码中解放出来。
    • Spring Data在JDBC或JPA之上定义了一层新的抽象接口,这些接口可以让程序员很方便地编写Spring下的Repository,甚至不需要编写实现,Spring Data内置了一组领域特定语言,可以根据存储库的方法名自动在编译期生成实现。
    • Spring Data不仅提供了对关系型数据库,还包括了对非关系型数据库以及NoSQL数据库的支持。
  • Spring Security
    • Spring技术栈的一个重要模块,用于提供安全性解决方案。
    • 引入Spring Security依赖后,自动配置功能会初始化一些安全配置
      • 所有HTTP请求路径都需要认证,认证是通过默认的登录页实现的
      • 此时没有特定的角色和权限,只有唯一用户User
    • 使用@Configuration注解修饰配置类,在配置类中使用@Bean方法可以对Spring Security进行配置。
  • PasswordEncoder Bean
    • 用于配置密码加密方式
      @Bean
      public PasswordEncoder PpasswordEncoder(){
          return new BCryptPasswordEncoder();
      }
  • UserDetailsService Bean
    • 用于配置用户服务,Spring Security本身内置了几个关于UserDetailsService接口的实现,包括基于内存的和基于JDBC等。也可以自己实现该接口,只需实现一个方法loadUserByUsername
      @Bean
      public UserDetailsService userDetailsService(UserRepository userRepo){
          return username -> {
              User user = userRepo.findByUsername(username);
              if(user != null) return user;
              throw new UsernameNotFoundException(username);
          };
      }
  • UserDetails 接口
    • 可用于实现用户实体类,它定义了一些常用的关于用户状态的方法。
      其中的getAuthorities方法用于设定用户的权限。
      @Entity
      @Data
      @NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
      @RequiredArgsConstructor
      @Table(name = "MyUser")
      public class User implements UserDetails {
      
          private static final long serialVersionUID = 1L;
      
          @Id
          @GeneratedValue(strategy = GenerationType.AUTO)
          private Long id;
      
          private final String username;
          private final String password;
      
      
      
          @Override
          public Collection<? extends GrantedAuthority> getAuthorities() {
              return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
          }
      
          @Override
          public boolean isAccountNonExpired() {
              return true;
          }
      
          @Override
          public boolean isAccountNonLocked() {
              return true;
          }
      
          @Override
          public boolean isCredentialsNonExpired() {
              return true;
          }
      
          @Override
          public boolean isEnabled() {
              return true;
          }
      }
  • SecurityFilterChain Bean
    • 用于配置Web请求相关的权限。HttpSecurity作为其构造器,使用Builder模式配置。
      @Bean
      public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
          return http
                  .authorizeRequests()
                  .antMatchers("/design", "/orders").hasRole("USER")
                  .antMatchers("/","/**").permitAll()
                  .and()
                  .formLogin()
                  .loginPage("/login")
                  .loginProcessingUrl("/login")
                  .usernameParameter("username")
                  .passwordParameter("password")
                  .defaultSuccessUrl("/design", true)
                  .and()
                  .build();
      }

      其中authorizeRequests可以根据URL模式来配置安全需求,前面的安全规则比后面的安全规则有更高优先级。

       formLogin表示自定义登录页,而loginPage表示登录页的路径(用于Spring Security重定向)。loginProcessingUrl表示接受登录请求(一般是POST请求)的路径,Spring Security会监视该路径,并通过usernameParameter和passwordParameter指定的字段来获取请求参数中的用户名和密码。

      如果登录成功,则会根据defaultSuccessUrl跳转页面,否则会跳转到用户登录前浏览的页面,如果登录失败,则会跳转到登录页,并指定query参数error。

      Spring Security默认启动CSRF,可以通过如下方法关闭:

      .and()
      .csrf().disable()
      .build();

      使用thymeleaf时,规定th:action属性后,表单会自动生成CSRF令牌。

  • @PreAuthorize和@PostAuthorize
    • 用于方法级别的安全配置,它们可以接受一个SpEL表达式,并在不满足时抛出AccessDeniedException。
    • 需要启用@EnableMethodSecurity注解标记安全配置类才能使用这两个注解。
    • 区别在于PreAuthorize在方法执行前校验,而PostAuthorize在方法执行后校验,后者可以通过在SpEL表达式中使用returnObject来引用方法返回值,例如:
      @GetMapping
      @PostAuthorize("hasRole('USER') && returnObject.user.username == authentication.name")
      public TacoOrder getOrder(Long id){
          return orderRepo.findById(id).orElse(null);
      }
  • 获取当前登录用户的方法
    • 注入参数  java.security.Principal,getName方法返回用户名。
    • 注入参数 org.springframework.security.core.Authentication,其getPrincipal返回用户对象,返回的是Object类型,需要作类型转换。Authentication对象也可以通过SecurityContextHolder.getContext().getAuthentication()得到。
    • @AuthenticationPrincipal注解方法参数,直接得到强类型对象。
  • Spring Boot的配置属性
    • 配置属性本质就是Spring应用上下文(Bean)中带有@ConfigurationProperties注解的属性
    • 配置属性可以通过application.properties或application.yml来配置。除了配置文件,Spring环境会从JVM系统属性、操作系统环境变量、命令行参数等属性源拉取配置值,这些值会被注入到Bean当中。
    • 例如,可以在application.yml配置服务器端口:
      server:
        port: 4567

      如果将server.port属性设置为0,它会任选一个端口,可用于集成化测试中防止端口冲突。

      实际上这个配置属性是由下面的JAVA代码定义的:

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

      使用@ConfigrationProperties注解,并用prefix指定配置前缀,便可以把类的属性值映射到某个配置属性上,Spring从属性源拉取值后,通过注入的方式为类的属性赋值。

      注意到这里的类ServerProperties是一个专门用于存放服务器相关属性的类,这是因为可能存在很多功能类都会使用到其中的属性,所以可以单独设定一个配置类来持有这些属性,而消费这些属性的功能类只需通过Bean注入该配置类即可。

  • Spring Profile
    • profile是一种条件化配置,用于解决不同环境下需要多套配置的需求。
    • 可以通过application-{profile_name}.properties或application-{profile_name}.yml来为名为{profile_name}的profile设置配置属性,只有当profile属于激活状态时这些配置才会生效。而application.properties或application.yml则总是生效。
    • 如何激活profile:
      • 通过application.properties或application.yml中设置spring.profiles.active配置属性
      • 通过环境变量SPRING_PROFILES_ACTIVE
  • @RestController
    • 类似于@Controller,但多了一个@ResponseBody注解的效果,它告诉Spring该控制器的处理方法的返回值都要经过序列化后直接写入响应体,而不是渲染某个视图。
    • 当使用@RestController时,需要指定@RequestMapping中的produces参数,它指定该控制器生成的Content-type,同时该控制器的所有处理方法只会处理Accept头信息包含produces参数的请求。
      @RestController
      @RequestMapping(path = "/api/tacos", produces = "application/json")
      @CrossOrigin(origins = "*")
      public class TacoController {

      @CrossOrigin用于指定允许跨域资源共享(CORS)的域名

      @PostMapping(consumes = "application/json")
      @ResponseStatus(HttpStatus.CREATED)
      public Taco postTaco(@RequestBody Taco taco){
          return tacoRepo.save(taco);
      }

      当使用@RestController时,PostMapping的consumes参数可以指定请求输入的类型,这表示该方法只会处理Content-type为consumes参数的请求。

    • @RequestBody注解表明请求体应该被转换(反序列化)为一个对象并绑定到所注解的参数上。如果不使用@RequestBody注解,Spring会试图将查询参数或者表单参数绑定到该参数上。

    • @ResponseStatus注解用于指定响应的HTTP状态码,正常情况下所有响应的HTTP状态码都是200,通过该注解可以指定语义更明确的状态码。
  • @PathVariable
    • 该注解用于获取路径中的参数,参数的占位符在路径中使用{XX}来指定。
      @GetMapping("/{id}")
      public ResponseEntity<Taco> tacoById(@PathVariable("id") Long id){
          Optional<Taco> taco = tacoRepo.findById(id);
          if(taco.isPresent()) return new ResponseEntity<>(taco.get(), HttpStatus.OK);
          else return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
      }
    •  @PathVariable可以指定参数名,也可以不指定,不指定时会使用变量名作为参数名。@PathVariable可以注解一个Map<string, string>类型的参数,用于将所有路径参数填充到该Map中。
  • ResponseEntity<T>
    • 继承于HttpEntity<T>,可以用于接收RestTemplate.getForEntity的返回值,也可以用于Controller方法的返回值。
    • 返回ResponseEntity<T>可以通过HttpStatus指定HTTP状态码。
  • Spring Data REST
    • 使用Spring Data REST可以自动为Spring Data创建的所有存储库创建REST API
    • 依赖:spring-boot-starter-data-rest
    • 可以通过spring.data.rest.base-path参数来调整自动生成的API的基础路径
    • Spring Data REST会根据Repository的接口来生成REST API,当接口包含参数为Pageable、返回值为Page<T>的方法时,生成的REST API就会包含分页和排序功能。
      @Repository
      public interface TacoRepository extends CrudRepository<Taco, Long> {
          Page<Taco> findAll(Pageable page);
      }
      

      分页和排序功能通过URL的size、page、sort参数来启用。

  • RestTemplate
    • 用于发起REST请求的类,由Spring核心框架提供的简单、同步REST客户端。
    • getForEntity方法发起GET请求,返回一个ResponseBody
    • getForObject方法发起GET请求,返回一个对象,对象的类型在参数中指定
    • execute方法用于发起特定的HTTP方法,它是最通用的请求方法,可以自定义所有的细节。RestTemplate的所有请求方法都是在execute方法的基础上实现的。
      public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException

      其requestCallback参数是一个函数式接口,其中可以通过ClientHttpRequest在请求发出前进行配置。其ResponseExtractor<T>参数也是一个函数式接口,其中可以通过ClienthttpResponse对响应进行反序列化,返回值是Object类型。

    • exchange方法用于发起特定的HTTP方法,但可以直接传递HttpEntity,并且会根据传递的responseType对响应进行反序列化,返回值是具体的类类型。
      public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)