最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot參數(shù)校驗及原理全面解析

 更新時間:2024年11月11日 10:53:16   作者:苦練基本功  
文章介紹了SpringBoot中使用@Validated和@Valid注解進行參數(shù)校驗的方法,包括基本用法和進階用法,如自定義驗證注解、多屬性聯(lián)合校驗和嵌套校驗,并簡要介紹了實現(xiàn)原理

前言

平時服務(wù)端開發(fā)過程中,不可避免的需要對接口參數(shù)進行校驗,比較常見的比如用戶名不能為空、年齡必須大于0、郵箱格式要合規(guī)等等。

如果通過if else去校驗參數(shù),校驗代碼會跟業(yè)務(wù)耦合,且顯得很冗長。

SpringBoot提供了一種簡潔、高效的方式,通過@Validated/@Valid注解來做參數(shù)校驗,大大提高了工作效率

一、基本用法

總共三種方式:

  • Controller的@RequestBody參數(shù)校驗
  • Controller的@RequestParam/@PathVariable參數(shù)校驗
  • 編程式校驗,直接調(diào)用hibernate的validate方法

三種方式都需要加上以下依賴。里面有所需的jakarta.validation-api和hibernate-validator包

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

@RequestBody參數(shù)校驗

該方式適用于Controller中POST/PUT方法的參數(shù)校驗,校驗失敗會拋MethodArgumentNotValidException

1.首先在參數(shù)類的屬性上聲明約束注解,比如@NotBlank、@Email等

@Data
public class UserVo implements Serializable {
    @NotBlank(message = "名字不能為空")
    @Size(min = 2, max = 50, message = "名字長度的范圍為2~50")
    private String name;

    @Email(message = "郵箱格式不對")
    private String email;

    @NotNull(message = "年齡不能為空")
    @Min(18)
    @Max(100)
    private Integer age;

    @NotEmpty(message = "照片不能為空")
    private List<String> photoList;
}

2.接著在Controller方法@RequestBody旁加上@Validated注解

@Slf4j
@RestController
public class UserController {
    @ApiOperation("保存用戶")
    @PostMapping("/save/user")
    public Result<Boolean> saveUser(@RequestBody @Validated UserVo user) {
        return Result.ok();
    }
}

@RequestParam/@PathVariable參數(shù)校驗

該方式適用于Controller中GET方法的參數(shù)校驗,校驗失敗會拋ConstraintViolationException。它是通過類上加@Validated注解,方法參數(shù)前加@NotBlank等約束注解的方式來實現(xiàn)的,所以其它Spring Bean的方法也適用

1.Controller類上加@Validated注解;@RequestParam/@PathVariable旁加上@NotBlank、@Max等注解

@Slf4j
@RestController
@Validated
public class UserController {
    @ApiOperation("查詢用戶")
    @GetMapping("/list/user")
    public Result<List<UserVo>> listUser(
            @Min(value = 100, message = "id不能小于100") @RequestParam("id") Long id,
            @NotBlank(message = "名稱不能為空") @RequestParam("name") String name,
            @Max(value = 90, message = "年齡不能大于90") @RequestParam("age") Integer age) {
        List<UserVo> list = new ArrayList<>();
        return Result.ok(list);
    }
}

編程式校驗

該方式適用于Service參數(shù)的校驗,校驗失敗手動拋ValidationException

1.通過@bean注解初始化Validator對象

public class ValidatorConfig {
    @Bean
    public Validator validator() {
        return Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失敗模式
                .failFast(true)
                .buildValidatorFactory()
                .getValidator();
    }
}

2.在Service方法中調(diào)用hibernate的validate方法對參數(shù)進行校驗

@Service
@Slf4j
public class UserService {
    @Autowired
    private Validator validator;

    public boolean editUser(UserVo user) {
        Set<ConstraintViolation<UserVo>> validateSet = validator.validate(user);
        if (CollectionUtils.isNotEmpty(validateSet)) {
            StringBuilder errorMessage = new StringBuilder();
            for (ConstraintViolation<UserVo> violation : validateSet) {
                errorMessage.append("[").append(violation.getPropertyPath().toString()).append("]")
                        .append(violation.getMessage()).append(";");
            }
            throw new ValidationException(errorMessage.toString());
        }
        return Boolean.TRUE;
    }
}

二、進階用法

自定義驗證注解

jakarta.validation-api和hibernate-validator包中內(nèi)置的注解有些場景可能不支持,比如添加用戶時,需要校驗用戶名是否重復(fù),這時可以通過自定義注解來實現(xiàn)

1.首先自定義注解

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Repeatable(UniqueName.List.class)
@Constraint(validatedBy = {UniqueNameValidator.class})
public @interface UniqueName {
    String message() default "用戶名重復(fù)了";
    // 分組
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        UniqueName[] value();
    }
}

2.接著給自定義注解添加驗證器

  • 實現(xiàn)ConstraintValidator接口,并指定自定義注解<UniqueName>和驗證的數(shù)據(jù)類型 <String>
  • 重寫isValid方法,實現(xiàn)驗證邏輯
@Component
public class UniqueNameValidator implements ConstraintValidator<UniqueName, String> {

    @Autowired
    private UserService userService;

    @Override
    public boolean isValid(String name, ConstraintValidatorContext context) {
        if (StringUtils.isBlank(name)) {
            return true;
        }
        UserVo user = userService.getByName(name);
        if (user == null) {
            return true;
        }
        return false;
    }
}

3.使用自定義注解

@Data
public class UserVo implements Serializable {
    @UniqueName
    private String name;
}

多屬性聯(lián)合校驗

一個字段的校驗依賴另一個字段的值時,需要用到多屬性聯(lián)合校驗,或者叫分組校驗。

舉個例子,某個系統(tǒng)提交用戶信息時需要做校驗,當性別為女時,照片信息不能為空。這時,照片信息能否為空,依賴于性別的取值。

hibernate-validator提供了DefaultGroupSequenceProvider接口供我們自定義分組,具體使用如下:

1.首先定義兩個組,Boy和Girl

public interface Boy {
}

public interface Girl {
}

2.分組邏輯實現(xiàn),當性別為女時,將用戶分到Girl組

public class CustomGroupSequenceProvider implements DefaultGroupSequenceProvider<UserVo> {

    @Override
    public List<Class<?>> getValidationGroups(UserVo user) {
        List<Class<?>> defaultGroupSequence = new ArrayList<>();
        defaultGroupSequence.add(UserVo.class);
        if (user != null) {
            String sex = user.getSex();
            if ("女".equals(sex)) {
                defaultGroupSequence.add(Girl.class);
            }
        }
        return defaultGroupSequence;
    }
}

3.使用分組校驗photoList字段

  • 實體類上添加@GroupSequenceProvider(CustomSequenceProvider.class)注解
  • 字段上添加@NotEmpty(message = "性別為女時照片不能為空", groups = {Girl.class})注解
@Data
@GroupSequenceProvider(CustomSequenceProvider.class)
public class UserVo implements Serializable {
    @NotBlank(message = "性別不能為空")
    private String sex;

    @NotEmpty(message = "性別為女時照片不能為空", groups = {Girl.class})
    private List<String> photoList;
}

嵌套校驗

當VO對象中存在對象屬性需要校驗時,可以使用嵌套校驗,

1.在對象屬性上加@Valid注解

@Data
public class UserVo implements Serializable {
    @Valid
    @NotNull(message = "地址不能為空")
    private Address address;
}

2.然后在內(nèi)嵌對象中聲明約束注解

@Data
public class Address implements Serializable {
    @NotBlank(message = "地址名稱不能為空")
    private String name;

    private String longitude;

    private String latitude;

}

三、實現(xiàn)原理

@RequestBody參數(shù)校驗實現(xiàn)原理

所有@RequestBody注釋的參數(shù)都要經(jīng)過RequestResponseBodyMethodProcessor類處理,該類主要用于解析@RequestBody注釋方法的參數(shù),以及處理@ResponseBody注釋方法的返回值。其中,resolveArgument()方法是解析@RequestBody注釋參數(shù)的入口

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        parameter = parameter.nestedIfOptional();
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        String name = Conventions.getVariableNameForParameter(parameter);

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }

        return adaptArgumentIfNecessary(arg, parameter);
    }
}

resolveArgument方法中的validateIfApplicable(binder, parameter)會對帶有@valid/@validate注解的參數(shù)進行校驗

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
			Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
			if (validationHints != null) {
				binder.validate(validationHints);
				break;
			}
		}
	}

//會對@Validated注解或者@Valid開頭的注解進行校驗

public static Object[] determineValidationHints(Annotation ann) {
		Class<? extends Annotation> annotationType = ann.annotationType();
		String annotationName = annotationType.getName();
		if ("javax.validation.Valid".equals(annotationName)) {
			return EMPTY_OBJECT_ARRAY;
		}
		Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
		if (validatedAnn != null) {
			Object hints = validatedAnn.value();
			return convertValidationHints(hints);
		}
		if (annotationType.getSimpleName().startsWith("Valid")) {
			Object hints = AnnotationUtils.getValue(ann);
			return convertValidationHints(hints);
		}
		return null;
	}

Spring通過一圈適配轉(zhuǎn)換后,會把參數(shù)校驗邏輯落到hibernate-validator中,在ValidatorImpl#validate(T object, Class<?>... groups)中做校驗

public class ValidatorImpl implements Validator, ExecutableValidator {

    @Override
    public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
        Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
        sanityCheckGroups( groups );

        @SuppressWarnings("unchecked")
        Class<T> rootBeanClass = (Class<T>) object.getClass();
        BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );

        if ( !rootBeanMetaData.hasConstraints() ) {
            return Collections.emptySet();
        }

        BaseBeanValidationContext<T>
                validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object );

        ValidationOrder validationOrder = determineGroupValidationOrder( groups );
        BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(
                validatorScopedContext.getParameterNameProvider(),
                object,
                validationContext.getRootBeanMetaData(),
                PathImpl.createRootPath()
        );

        return validateInContext( validationContext, valueContext, validationOrder );
    }
    
}

具體校驗過程在validateConstraintsForSingleDefaultGroupElement方法中,它會遍歷@NotNull、@NotBlank、@Email這些約束注解,看參數(shù)是否符合限制

public class ValidatorImpl implements Validator, ExecutableValidator {

    private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext<?> validationContext, ValueContext<U, Object> valueContext, final Map<Class<?>, Class<?>> validatedInterfaces,
                                                                        Class<? super U> clazz, Set<MetaConstraint<?>> metaConstraints, Group defaultSequenceMember) {
        boolean validationSuccessful = true;

        valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() );
        //metaConstraints是@NotNull、@NotBlank、@Email這些約束注解的集合,一個個驗證
        for ( MetaConstraint<?> metaConstraint : metaConstraints ) {
            final Class<?> declaringClass = metaConstraint.getLocation().getDeclaringClass();
            if ( declaringClass.isInterface() ) {
                Class<?> validatedForClass = validatedInterfaces.get( declaringClass );
                if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) {
                    continue;
                }
                validatedInterfaces.put( declaringClass, clazz );
            }

            boolean tmp = validateMetaConstraint( validationContext, valueContext, valueContext.getCurrentBean(), metaConstraint );
            if ( shouldFailFast( validationContext ) ) {
                return false;
            }

            validationSuccessful = validationSuccessful && tmp;
        }
        return validationSuccessful;
    }
}

validator.isValid()是所有驗證器的入口,包括hibernate-validator內(nèi)置的,以及自定義的

public abstract class ConstraintTree<A extends Annotation> {

    protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(
            ValueContext<?, ?> valueContext,
            ConstraintValidatorContextImpl constraintValidatorContext,
            ConstraintValidator<A, V> validator) {
        boolean isValid;
        try {
            @SuppressWarnings("unchecked")
            V validatedValue = (V) valueContext.getCurrentValidatedValue();
            isValid = validator.isValid( validatedValue, constraintValidatorContext );
        }
        catch (RuntimeException e) {
            if ( e instanceof ConstraintDeclarationException ) {
                throw e;
            }
            throw LOG.getExceptionDuringIsValidCallException( e );
        }
        if ( !isValid ) {
            //We do not add these violations yet, since we don't know how they are
            //going to influence the final boolean evaluation
            return Optional.of( constraintValidatorContext );
        }
        return Optional.empty();
    }

}

以下是@NotBlank約束注解驗證器的具體實現(xiàn)

public class NotBlankValidator implements ConstraintValidator<NotBlank, CharSequence> {

	/**
	 * Checks that the character sequence is not {@code null} nor empty after removing any leading or trailing
	 * whitespace.
	 *
	 * @param charSequence the character sequence to validate
	 * @param constraintValidatorContext context in which the constraint is evaluated
	 * @return returns {@code true} if the string is not {@code null} and the length of the trimmed
	 * {@code charSequence} is strictly superior to 0, {@code false} otherwise
	 */
	@Override
	public boolean isValid(CharSequence charSequence, ConstraintValidatorContext constraintValidatorContext) {
		if ( charSequence == null ) {
			return false;
		}

		return charSequence.toString().trim().length() > 0;
	}
}

@RequestParam/@PathVariable參數(shù)校驗實現(xiàn)原理

該方式本質(zhì)是通過類上加@Validated注解,方法參數(shù)前加@NotBlank等約束注解來實現(xiàn)的。底層使用的是Spring AOP,具體來說是通過MethodValidationPostProcessor動態(tài)注冊AOP切面,然后使用MethodValidationInterceptor對切點方法織入增強。

以下是容器啟動時初始化@Validated切點,以及MethodValidationInterceptor增強

public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
        implements InitializingBean {
    private Class<? extends Annotation> validatedAnnotationType = Validated.class;

    @Nullable
    private Validator validator;

    @Override
    public void afterPropertiesSet() {
        Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
        this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
    }
    
    protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
        return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
    }
}

具體增強邏輯在MethodValidationInterceptor中

public class MethodValidationInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
        if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
            return invocation.proceed();
        }

        Class<?>[] groups = determineValidationGroups(invocation);

        // Standard Bean Validation 1.1 API
        ExecutableValidator execVal = this.validator.forExecutables();
        Method methodToValidate = invocation.getMethod();
        Set<ConstraintViolation<Object>> result;

        try {
            result = execVal.validateParameters(
                    invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
        }
        catch (IllegalArgumentException ex) {
            // Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
            // Let's try to find the bridged method on the implementation class...
            methodToValidate = BridgeMethodResolver.findBridgedMethod(
                    ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
            result = execVal.validateParameters(
                    invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
        }
        if (!result.isEmpty()) {
            throw new ConstraintViolationException(result);
        }

        Object returnValue = invocation.proceed();

        result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);
        if (!result.isEmpty()) {
            throw new ConstraintViolationException(result);
        }

        return returnValue;
    }
}

其中execVal.validateParameters()方法是用來做參數(shù)校驗的,最終會進到hibernate-validator中。后面的邏輯跟上面類似,此處就不再贅述

public class ValidatorImpl implements Validator, ExecutableValidator {
    @Override
	public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) {
		Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
		Contracts.assertNotNull( method, MESSAGES.validatedMethodMustNotBeNull() );
		Contracts.assertNotNull( parameterValues, MESSAGES.validatedParameterArrayMustNotBeNull() );

		return validateParameters( object, (Executable) method, parameterValues, groups );
	}
}

項目源碼

https://github.com/layfoundation/spring-param-validate

附件

jakarta.validation-api(版本2.0.1)所有注解

注解說明
@AssertFalse驗證 boolean 類型值是否為 false
@AssertTrue驗證 boolean 類型值是否為 true
@DecimalMax(value)驗證數(shù)字的大小是否小于等于指定的值,小數(shù)存在精度
@DecimalMin(value)驗證數(shù)字的大小是否大于等于指定的值,小數(shù)存在精度
@Digits(integer, fraction)驗證數(shù)字是否符合指定格式
@Email驗證字符串是否符合電子郵件地址的格式
@Future驗證一個日期或時間是否在當前時間之后
@FutureOrPresent驗證一個日期或時間是否在當前時間之后或等于當前時間
@Max(value)驗證數(shù)字的大小是否小于等于指定的值
@Min(value)驗證數(shù)字的大小是否大于等于指定的值
@Negative驗證數(shù)字是否是負整數(shù),0無效
@NegativeOrZero驗證數(shù)字是否是負整數(shù)
@NotBlank驗證字符串不能為空null或"",只能用于字符串驗證
@NotEmpty驗證對象不得為空,可用于Map和數(shù)組
@NotNull驗證對象不為 null
@Null驗證對象必須為 null
@past驗證一個日期或時間是否在當前時間之前。
@PastOrPresent驗證一個日期或時間是否在當前時間之前或等于當前時間。
@Pattern(value)驗證字符串是否符合正則表達式的規(guī)則
@Positive驗證數(shù)字是否是正整數(shù),0無效
@PositiveOrZero驗證數(shù)字是否是正整數(shù)
@Size(max, min)驗證對象(字符串、集合、數(shù)組)長度是否在指定范圍之內(nèi)

hibernate-validator(版本6.0.17.Final)補充的常用注解

注解說明
@Length被注釋的字符串的大小必須在指定的范圍內(nèi)
@Range被注釋的元素必須在合適的范圍內(nèi)
@SafeHtml被注釋的元素必須是安全Html
@URL被注釋的元素必須是有效URL

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java Record簡介

    Java Record簡介

    Java Record是一種特殊的類,旨在簡化與數(shù)據(jù)傳輸對象(DTO)相關(guān)的工作,下面通過本文給大家介紹Java Record的相關(guān)知識,感興趣的朋友一起看看吧
    2025-06-06
  • 幾種JAVA細粒度鎖的實現(xiàn)方式

    幾種JAVA細粒度鎖的實現(xiàn)方式

    這篇文章主要為大家詳細介紹了幾種JAVA細粒度鎖的實現(xiàn)方式,感興趣的小伙伴們可以參考一下
    2016-05-05
  • 深入理解java虛擬機的故障處理工具

    深入理解java虛擬機的故障處理工具

    大家都知道在給系統(tǒng)定位問題的時候,知識、經(jīng)驗是關(guān)鍵基礎(chǔ),數(shù)據(jù)是依據(jù),工具是運用知識處理數(shù)據(jù)的手段。Java開發(fā)人員可以在jdk安裝的bin目錄下找到除了java,javac以外的其他命令。這些命令主要是一些用于監(jiān)視虛擬機和故障處理的工具,下面來看看詳細的介紹。
    2016-11-11
  • 一文詳解Java中的finalize()方法

    一文詳解Java中的finalize()方法

    Java中的finalize()是Object類的一個方法,用于對象被垃圾回收前執(zhí)行清理操作,這篇文章主要介紹了Java中finalize()方法的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-09-09
  • java連接hdfs ha和調(diào)用mapreduce jar示例

    java連接hdfs ha和調(diào)用mapreduce jar示例

    這篇文章主要介紹了Java API連接HDFS HA和調(diào)用MapReduce jar包,需要的朋友可以參考下
    2014-03-03
  • Java集合中獲取數(shù)據(jù)前驅(qū)和后繼元素的實現(xiàn)

    Java集合中獲取數(shù)據(jù)前驅(qū)和后繼元素的實現(xiàn)

    使用一致性hash時,如何找到一個hash值對應(yīng)的臨近節(jié)點,可以使用集合中獲取數(shù)據(jù)的前驅(qū)和后繼元素實現(xiàn),所以本文給大家介紹了Java集合中獲取數(shù)據(jù)前驅(qū)和后繼元素的實現(xiàn),文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-05-05
  • java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解

    java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解

    這篇文章主要介紹了java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • SpringSecurity、Shiro?和?Sa-Token怎么選,哪個更好

    SpringSecurity、Shiro?和?Sa-Token怎么選,哪個更好

    本文介紹了SpringSecurity、ApacheShiro和Sa-Token三個主流安全框架的特點,并分析了它們在不同場景下的適用性,文章提供了一個決策流程圖,幫助開發(fā)者根據(jù)具體需求選擇合適的框架,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • Java如何獲取List<String>中的String詳解

    Java如何獲取List<String>中的String詳解

    工作了這么長時間了,一直沒有記錄的習慣,以至于導(dǎo)致我即便是查過的東西總會忘記,下面這篇文章主要給大家介紹了關(guān)于Java如何獲取List<String>中String的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • Spring事務(wù)事件監(jiān)控的實現(xiàn)

    Spring事務(wù)事件監(jiān)控的實現(xiàn)

    這篇文章主要介紹了Spring事務(wù)事件監(jiān)控的實現(xiàn)。本文首先會使用實例進行講解Spring事務(wù)事件是如何使用的,然后會講解這種使用方式的實現(xiàn)原理。感興趣的小伙伴們可以參考一下
    2018-10-10

最新評論

棋牌| 军事| 长治市| 阳泉市| 乐山市| 平凉市| 安达市| 漳平市| 奉新县| 丽江市| 天峨县| 辛集市| 交城县| 清水河县| 来宾市| 开封县| 遂宁市| 淮滨县| 南城县| 买车| 和静县| 台山市| 通河县| 抚宁县| 错那县| 涪陵区| 友谊县| 子洲县| 天长市| 桐柏县| 汾西县| 碌曲县| 昂仁县| 泰宁县| 凌海市| 临泽县| 新蔡县| 达孜县| 读书| 广东省| 舟曲县|