Spring Boot组件化与参数校验

Spring Boot组件化与参数校验

Spring Boot版本选择

  1. 2.3.x版本

  2. 2.6.x版本

Spring Boot核心思想

约定大于配置,简化繁琐的配置

Spring Boot自动配置原理

  • @SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot需要运行这个类的main方法来启动SpringBoot应用;

  • SpringBootApplication

    java 复制代码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
          @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
          @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
  • @SpringBootConfiguration:Spring Boot的配置类; 标注在某个类上,表示这是一个Spring Boot的配置类;

  • @Configuration:配置类上来标注这个注解; 配置类 ----- 配置文件;配置类也是容器中的一个组件;本质上是@Component

  • @EnableAutoConfiguration:开启自动配置功能; 以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置,会帮我们自动去加载 自动配置类

  • @ComponentScan : 扫描包 相当于在spring.xml 配置中context:component-scan 但是并没有指定basepackage,如果没有指定spring底层会自动扫描当前配置类所有在的包。TypeExcludeFilter:springboot对外提供的扩展类, 可以供我们去按照我们的方式进行排除,去找到所有自定义的TypeExcludeFilter的bean调用match方法,满足一个则排除。AutoConfigurationExcludeFilter:排除当前类路径下所有@Configuration修饰的类并且是自动配置的类(spring.factories中EnableAutoConfiguration配置了就是自动配置类)

  • @EnableAutoConfiguration

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // 略
}
  • @AutoConfigurationPackage 将当前配置类所在包保存在BasePackages的Bean中。供Spring内部使用。

  • @Import(AutoConfigurationImportSelector.class) 关键点! 可以看到,在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能,而AutoConfigurationImportSelector实现了DeferredImportSelector,Spring延迟到项目beanDefinition已经全被扫描完,才去执行selectImports,内部在解析@Import注解时会调用getAutoConfigurationEntry方法。 下面是2.3.5.RELEASE实现源码:getAutoConfigurationEntry方法进行扫描具有META-INF/spring.factories文件的jar包。

java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 从META-INF/spring.factories中获得候选的自动配置类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   // 排重
   configurations = removeDuplicates(configurations);
   //根据EnableAutoConfiguration注解中属性,获取不需要自动装配的类名单
   // 根据:@EnableAutoConfiguration.exclude
   // @EnableAutoConfiguration.excludeName
   // spring.autoconfigure.exclude  进行排除
   Set<String> exclusions = getExclusions(annotationMetadata, attributes); 
   // 检查如果exclusions排除的类不在自动配置类configurations里,抛出异常
   checkExcludedClasses(configurations, exclusions);
   // exclusions 也排除
   configurations.removeAll(exclusions);
   // 通过读取spring.factories 中AutoConfigurationImportFilter的配置类OnBeanCondition\OnClassCondition\OnWebApplicationCondition实例化进行过滤
   configurations = getConfigurationClassFilter().filter(configurations);
   // 通过读取spring.factories 中的AutoConfigurationImportListener类实例化(可以支持)处理AutoConfigurationImportEvent的事件
   // 分别把候选的配置名单,和排除的配置名单传进去做扩展
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

@Conditional扩展注解作用 (判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnExpression 满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项

我们可以通过设置配置文件中:启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

自定义starter

一、简介 SpringBoot 最强大的功能就是把我们常用的场景抽取成了一个个starter(场景启动器),我们通过引入springboot 为我提供的这些场景启动器,我们再进行少量的配置就能使用相应的功能。即使是这样,springboot也不能囊括我们所有的使用场景,往往我们需要自定义starter,来简化我们对springboot的使用。

模式

我们参照 spring-boot-starter 我们发现其中没有代码:

我们在看它的pom中的依赖中有个 springboot-starter

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

我们再看看 spring-boot-starter 有个 spring-boot-autoconfigure

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
  • 启动器(starter)是一个空的jar文件,仅仅提供辅助性依赖管理,这些依赖可能用于自动装配或其他类库。
  • 需要专门写一个类似spring-boot-autoconfigure的配置模块
  • 用的时候只需要引入启动器starter,就可以使用自动配置了

命名规范

官方命名空间

  • 前缀:spring-boot-starter-
  • 模式:spring-boot-starter-模块名
  • 举例:spring-boot-starter-web、spring-boot-starter-jdbc

自定义命名空间

  • 后缀:-spring-boot-starter
  • 模式:模块-spring-boot-starter
  • 举例:mybatis-spring-boot-starter

参数校验

  1. 引入依赖
xml 复制代码
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
	<!--hibernate validator依赖-->
	<dependency>
		<groupId>org.hibernate.validator</groupId>
		<artifactId>hibernate-validator</artifactId>
		<version>6.0.1.Final</version>
	</dependency>
  1. 添加配置
java 复制代码
@Bean
public Validator validator(){
    ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
            .configure()

            .failFast(true)
            .buildValidatorFactory();

return validatorFactory.getValidator();
}
  1. 使用

@Length(max = 64, message = "职务名称超长") 不校验null的字段

@Email 不去检验null和空字符串

@Pattern 可以允许当前字段为null,针对非null做校验

@AssertTrue 不去检验null值

(1)Controller上的参数校验,主要针对于@RequestBody的POJO

(2)Bean的方法参数校验

(3)针对bean的属性做参数校验

@Valid和@Validated区别

区别 @Valid @Validated
提供者 JSR-303 规范 Spring,意味着只能用于POJO之外的地方,比如controller类上、方法、参数上
是否支持分组 不支持 支持
标注位置 METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE TYPE, METHOD, PARAMETER
嵌套校验 支持 不支持
  1. 异常处理

需要全局处理MethodArgumentNotValidException、ConstraintViolationException、BindException异常

java 复制代码
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Response<?> exceptionHandler(HttpServletRequest httpServletRequest, MethodArgumentNotValidException e) {
	log.error("字段校验错误!", e);
	String msg = Optional.ofNullable(e.getBindingResult().getFieldError())
			.map(DefaultMessageSourceResolvable::getDefaultMessage).orElse(NETWORK_ERROR_MSG);
	return new Response<>(CommonErrorCode.FIELD_VALIDATE_FAIL.getCode(), msg);
}

@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Response<?> exceptionHandler(HttpServletRequest httpServletRequest, ConstraintViolationException e) {
	log.error("字段校验错误!", e);
	ConstraintViolation<?> constraintViolation = null;
	Iterator<ConstraintViolation<?>> iterator = e.getConstraintViolations().iterator();
	if (iterator.hasNext()) {
		constraintViolation = iterator.next();
	}
	String msg = Optional.ofNullable(constraintViolation).map(ConstraintViolation::getMessage)
			.orElse(NETWORK_ERROR_MSG);
	return new Response<>(CommonErrorCode.FIELD_VALIDATE_FAIL.getCode(), msg);
}

@ExceptionHandler(value = BindException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Response<?> exceptionHandler(HttpServletRequest httpServletRequest, BindException e) {
	log.error("字段校验错误!", e);
	String msg = Optional.ofNullable(e.getBindingResult().getFieldError())
			.map(DefaultMessageSourceResolvable::getDefaultMessage).orElse(NETWORK_ERROR_MSG);
	return new Response<>(CommonErrorCode.FIELD_VALIDATE_FAIL.getCode(), msg);
}
相关推荐
代码之光_198032 分钟前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi38 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
StayInLove1 小时前
G1垃圾回收器日志详解
java·开发语言
对许1 小时前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j
无尽的大道1 小时前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
小鑫记得努力1 小时前
Java类和对象(下篇)
java
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE1 小时前
【Java SE】StringBuffer
java·开发语言
老友@1 小时前
aspose如何获取PPT放映页“切换”的“持续时间”值
java·powerpoint·aspose