Spring下基於註解的引數校驗
前段時間,為了校驗介面資料方便,實現了一個簡易的引數校驗功能。通過引數上加註解並且配置spring aop實現的。
大概配置如下:
public Object ArroundParamCheck(ProceedingJoinPoint pj) throws Throwable{ if(null == pj){ log.info("傳入的物件是空的!!!!!!!!!!"); } log.info("引數校驗開始================"); Object[] obs = pj.getArgs(); Response_CM resp = paramCheckService.paramCheck(obs); log.info("引數校驗結束==============" + resp.toString()); if(!CodeMsgConstant.CODE_000000.equals(resp.getRespCode())){ MethodSignature methodSg = (MethodSignature) pj.getSignature(); Class<?> returnType = methodSg.getReturnType(); return paramCheckService.getCheckResult(returnType, resp); } Object pjResult = pj.proceed(); return pjResult; }
<bean id="paramCheckAop" class="com.huateng.iccs.pre.corp.aop.ParamCheckAop"></bean> <aop:config> <aop:pointcut expression="execution(* com.huateng.iccs.pre.corp.service.impl.ScfContractServiceImpl.*(..))" id="scfContractPc"/> <aop:pointcut expression="execution(* com.huateng.iccs.pre.corp.service.impl.ScfCustomerServiceImpl.*(..))" id="scfCustomerPc"/> <aop:pointcut expression="execution(* com.huateng.iccs.pre.corp.service.impl.ClientInformServiceImpl.*(..))" id="clientInformPc"/> <aop:aspect id = "paramCheck" ref ="paramCheckAop"> <aop:around pointcut-ref="scfContractPc" method="ArroundParamCheck"/> <aop:around pointcut-ref="scfCustomerPc" method="ArroundParamCheck"/> <aop:around pointcut-ref="clientInformPc" method="ArroundParamCheck"/> </aop:aspect> </aop:config>
但是引入的系統時候需要顯性的設定一個環繞增強,並且要在xml中配置切面切點。
使用過@Transactional的註解的應該都知道,只需要在方法或者類上標註@Transactional註解,就可以開啟spring事務。使用起來比較便捷。為了作者練手,並且使用方便,於是便想引數校驗做成類似模式。
1、spring下自定義xml解析的實現
使用@Transactional需要在spring xml下配置<tx:annotation-driven />來開啟註解事務。同樣這種方式的校驗也是從自定義xml解析開始。
這裡的xml解析很簡單
- 編寫對應的xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://www.component.org/schema/avd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool" targetNamespace="http://www.component.org/schema/avd" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:import namespace="http://www.springframework.org/schema/beans"></xsd:import> <xsd:element name="annotation-driven"></xsd:element> </xsd:schema>
這裡的xsd編寫也很簡單,因為只是一個標記,表示開啟自動引數解析,只需要配置一個element,不需要額外的配置。
<xsd:element name="annotation-driven"></xsd:element>
- 編寫對應的NamespaceHandlerSupport和BeanDefinitionParser。
public class AvdNamespaceHandler extends NamespaceHandlerSupport{
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AvdAnnotationDrivenBeanDefinitionParser());
}
}
public class AvdAnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser{
public static final String AVD_ADVISOR_BEAN_NAME = "com.fosun.component.validate.interceptor.avdAdvisor";
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
configureAutoProxyCreator(element,parserContext);
return null;
}
private void configureAutoProxyCreator(Element element, ParserContext parserContext){
/**
* 如果必要開啟自動建立代理物件
* 此方法可以將targetClass委託給spring讓spring為符合切點的目標類自動建立代理物件
*/
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String avdAdvisorName = AVD_ADVISOR_BEAN_NAME;
if(!parserContext.getRegistry().containsBeanDefinition(avdAdvisorName)){
Object eleSource = parserContext.extractSource(element);
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationAvdAttributeSource.class);
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
RootBeanDefinition interceptorDef = new RootBeanDefinition(AutoAvdInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryAutoAvdDefineAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("avdAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
parserContext.getRegistry().registerBeanDefinition(avdAdvisorName, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, avdAdvisorName));
parserContext.registerComponent(compositeDef);
}
}
}
- 配置spring.handlers和spring.schmas
兩個檔案的目錄格式如上圖所示(嚴格按照)spring.handlers
http\://www.component.org/schema/avd = com.fosun.component.validate.config.AvdNamespaceHandler
spring.schemashttp\://www.component.org/schema/avd.xsd = com/fosun/component/validate/config/component-avd-1.0.xsd
- pom檔案配置
<build><plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classesDirectory>target/classes/</classesDirectory>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
</plugins></build>
- spring配置檔案中使用
如上圖在配置檔案中增加
xmlns:avd="http://www.component.org/schema/avd"
http://www.component.org/schema/avd http://www.component.org/schema/avd.xsd
<avd:annotation-driven/>
2、Spring aop實現
同樣,如同普通的aop實現一樣,這種方式也需要配置增強、切點和切面。同時,需要宣告一個類似@Transactional的註解,用來標記在方法或者類上來表示該方法或者類開啟校驗。作者在這裡定義了@AutoAvd註解- 切點類
繼承StaticMethodMatcherPointcut表示這是一個靜態切點。這個方法的主要目的就是匹配目標方法或者類是否標記了@AutoAvd註解。
public abstract class AutoAvdDefinePointcut extends StaticMethodMatcherPointcut{
@Override
public boolean matches(Method method, Class<?> targetClass) {
AvdAttributeSource aad = getAvdAttributeSource();
return (aad == null || aad.getAutoAvdDefine(method, targetClass) != null);
}
public abstract AvdAttributeSource getAvdAttributeSource();
}
- 增強類
實現MethodInterceptor介面,標識這是一個環繞增強。
public class AutoAvdInterceptor implements MethodInterceptor{
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
ParamCheckService paramCheckService;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
// Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
Object[] obs = invocation.getArguments();
log.info("引數校驗開始================");
Response_CM resp = paramCheckService.paramCheck(obs);
log.info("引數校驗結束==============" + resp.toString());
if(!CodeMsgConstant.CODE_000000.equals(resp.getRespCode())){
Method method = invocation.getMethod();
Class<?> returnType = method.getReturnType();
return paramCheckService.getCheckResult(returnType, resp);
}
return invocation.proceed();
}
}
- advisor
繼承AbstractBeanFactoryPointcutAdvisor重寫getPointcut()方法。
BeanFactoryAutoAvdDefineAdvisor中的AvdAttributeSource用來解析@AutoAvd註解的內容,
public class BeanFactoryAutoAvdDefineAdvisor extends AbstractBeanFactoryPointcutAdvisor{
private AvdAttributeSource avdAttributeSource;
private AutoAvdDefinePointcut aadPointcut = new AutoAvdDefinePointcut() {
@Override
public AvdAttributeSource getAvdAttributeSource() {
return avdAttributeSource;
}
};
public void setAvdAttributeSource(AvdAttributeSource avdAttributeSource) {
this.avdAttributeSource = avdAttributeSource;
}
@Override
public Pointcut getPointcut() {
return this.aadPointcut;
}
}
AnnotationAvdAttributeSource實現AvdAttributeSource來實現由@AutoAvd表示的內容。
回顧切點的matches()方法,利用了AvdAttributeSource的getAutoAvdDefine()方法來具體判讀是否匹配切點。
public class AnnotationAvdAttributeSource implements AvdAttributeSource{
private AutoAvdAnnotationParser annotationParser;
public AnnotationAvdAttributeSource(){
this.annotationParser = new SpringAutoAvdAnnotationParser();
}
@Override
public AutoAvdDefine getAutoAvdDefine(Method method, Class<?> targetClass) {
/**
* 先這麼寫
*/
return computerAutoAvdDefine(method,targetClass);
}
private AutoAvdDefine computerAutoAvdDefine(Method method, Class<?> targetClass){
/**
* 不支援非public方法
*/
if ( !Modifier.isPublic(method.getModifiers())) {
return null;
}
Class<?> userClass = ClassUtils.getUserClass(targetClass);
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
AutoAvdDefine aad = findAutoAvdAnnotationDefine(specificMethod);
if (aad != null) {
return aad;
}
aad = findAutoAvdAnnotationDefine(specificMethod.getDeclaringClass());
if (aad != null) {
return aad;
}
if (specificMethod != method) {
aad = findAutoAvdAnnotationDefine(method);
if (aad != null) {
return aad;
}
return findAutoAvdAnnotationDefine(method.getDeclaringClass());
}
return null;
}
private AutoAvdDefine findAutoAvdAnnotationDefine(Method method){
return determineAutoAvdDefine(method);
}
private AutoAvdDefine findAutoAvdAnnotationDefine(Class<?> targetClass){
return determineAutoAvdDefine(targetClass);
}
private AutoAvdDefine determineAutoAvdDefine(AnnotatedElement ae){
return this.annotationParser.parseAutoAvdAnnotation(ae);
}
}
關鍵
在第二步中,我們只是定義了切點,環繞增強,切面等。還未介紹如何將這些交給spring託管。並且為符合切點的物件生成代理物件。private void configureAutoProxyCreator(Element element, ParserContext parserContext){
/**
* 如果必要開啟自動建立代理物件
* 此方法可以將targetClass委託給spring讓spring為符合切點的目標類自動建立代理物件
*/
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String avdAdvisorName = AVD_ADVISOR_BEAN_NAME;
if(!parserContext.getRegistry().containsBeanDefinition(avdAdvisorName)){
Object eleSource = parserContext.extractSource(element);
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationAvdAttributeSource.class);
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
RootBeanDefinition interceptorDef = new RootBeanDefinition(AutoAvdInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryAutoAvdDefineAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("avdAttributeSource", new RuntimeBeanReference(sourceName));/**<property name="name" ref=ref/>*/
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);/**<property name="name" value="value"/>*/
parserContext.getRegistry().registerBeanDefinition(avdAdvisorName, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, avdAdvisorName));
parserContext.registerComponent(compositeDef);
}
}
回顧AvdAnnotationDrivenBeanDefinitionParser的configureAutoProxyCreator()方法。在這裡將自動校驗的切點增強等邏輯交給spring託管並生成代理物件。 方法開始呼叫AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);。熟悉spring aop的都清楚,在使用spring aop時,一般都需要在xml中配置<aop:aspectj-autoproxy proxy-target-class="true" />;。spring這對這個進行解析的時候最終就會呼叫AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);方法。目的是為符合切點的容器中的bean自動建立代理物件。接下來就是將BeanFactoryAutoAvdDefineAdvisor等元件註冊到工廠中。
相關推薦
Spring下基於註解的引數校驗
前段時間,為了校驗介面資料方便,實現了一個簡易的引數校驗功能。通過引數上加註解並且配置spring aop實現的。大概配置如下:public Object ArroundParamCheck(ProceedingJoinPoint pj) throws Thr
更加靈活的引數校驗,Spring-boot自定義引數校驗註解
上文[測試開發專題:如何在spring-boot中進行引數校驗](https://www.immortalp.com/articles/2020/05/15/1589509696197.html),我們討論瞭如何使用@Min、@Max等註解進行引數校驗,主要是針對基本資料型別和級聯物件進行引數校驗的演示,但是
關於怎麼解決從ajax傳入的json引數注入到Controller的接收物件 以及如何在Spring Boot專案使用引數校驗
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <tit
Spring Boot專案使用引數校驗
開發web專案有時候我們需要對controller層傳過來的引數進行一些基本的校驗,比如非空,非null,整數值的範圍,字串的個數,日期,郵箱等等。最常見的就是我們直接寫程式碼校驗,這樣以後比較繁瑣,而且不夠靈活。 Bean Validation 1.0(JSR-303
如何在 Spring/Spring Boot 中做引數校驗?你需要了解的都在這裡!
本文為作者原創,如需轉載請在文首著名地址,公眾號轉載請申請開白。 springboot-guide : 適合新手入門以及有經驗的開發人員查閱的 Spring Boot 教程(業餘時間維護中,歡迎一起維護)。 資料的校驗的重要性就不用說了,即使在前端對資料進行校驗的情況下,我們還是要對傳入後端的資料再進行一
測試開發專題:如何在spring-boot中進行引數校驗
上文我們討論了spring-boot如何去獲取前端傳遞過來的引數,那傳遞過來總不能直接使用,需要對這些引數進行校驗,符合程式的要求才會進行下一步的處理,所以本篇文章我們主要討論spring-boot中如何進行引數校驗。 ### lombok使用介紹 在介紹引數校驗之前,先來了解一下lombok的使用,因為
手把手寫一個基於Spring Boot框架下的引數校驗元件(JSR-303)
前言 之前參與的新開放平臺研發的過程中,由於不同的介面需要對不同的入參進行校驗,這就涉及到通用引數的校驗封裝,如果不進行封裝,那麼寫出來的校驗程式碼將會風格不統一、校驗工具類不一致、維護風險高等其它因素,於是我對其公共的校驗做了一個封裝,達到了通過註解的方式即可實現引數統一校驗。 遇到的問
spring註解式引數校驗
一般入參我們都會轉為vo物件。那麼直接在物件的屬性上註解即可。 其實spring用的是hibernate的validator. 步驟 1.配置spring.xml <mvc:annotation-driven /> 2.配置
Spring -- 通過攔截器使用註解方式校驗引數
public class OpenHandlerInterceptor extends HandlerInterceptorAdapter { private static final Logger LOGGER = Logger.getLogger(OpenHandlerInterceptor.c
基於註解的引數校驗框架
近期由於工作的需要,寫了一個簡易的引數校驗框架,雖然市場上有common-validator 和hibernate-validator兩個開源的,但是有些情景他們是無法滿足的,比如引數欄位之間的依賴關係,這在專案中是極其常見的。他們僅僅提供了對欄位的簡單的格式校驗。另外這兩種
Spring Validation-用註解代替程式碼引數校驗
# Spring Validation ## 概念 在原先的編碼中,我們如果要驗證前端傳遞的引數,一般是在接受到傳遞過來的引數後,手動在程式碼中做 if-else 判斷,這種編碼方式會帶來大量冗餘程式碼,十分的不優雅。 因此,推出了用註解的方式,來代替手動判斷的方式,讓編碼更加的簡潔。 ## 使用方式
Spring基礎系列-引數校驗
原創作品,可以轉載,但是請標註出處地址:https://www.cnblogs.com/V1haoge/p/9953744.html Spring中使用引數校驗 概述 JSR 303中提出了Bean Validation,表示JavaBean的校驗,Hibernate Validation是其具體實
SpringMVC自定義註解進行引數校驗
在我的另一篇部落格中(SpringMVC),學習瞭如何使用Spring MVC結合Hibernate的校驗框架validation(它和hibernate沒有任何關係)對引數進行校驗。在實際專案中,引數的校驗邏輯可能比較複雜,這時我們可以自定義註解來實現引數校驗,下面是一個簡單的例子。 po
SpringMVC自定義註解進行引數校驗(以校驗列舉值是否合法為例)
pom引入springMVC依賴,以springboot專案為例 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-
Spring Boot MVC 引數校驗
文章目錄 Spring Boot Validate 常用註解 使用說明 基本使用 實現分組校驗 處理校驗結果 自定義校驗註解 配置校驗提示資訊 手動進行校驗
Spring Boot 使用hibernate validator進行引數校驗
demo 實體類 public class UserBean { @NotBlank(message="使用者名稱不能為空") private String userName; @NotBlank(message="年齡不能為空") @P
自定義註解完成引數校驗
在現在的專案開發中,經常會用到註解,比如Spring的@Autowired,SpringMVC的@Controller,@RequestMapping等等,大部分人只知道用,不知道這些註解的怎麼發揮作用。有沒有想過手動寫一個註解,完成引數校驗呢? 簡介 註解(
Spring:基於註解的 Spring MVC(下)
上一篇文章《Spring:基於註解的Spring MVC(上)》,講了Spring MVC環境搭建、@RequestMapping以及引數繫結,這是Spring MVC中最基礎也是最重要的內容,本篇文章繼續講講Spring MVC中其餘的知識點,先從Model開始。 Mod
Spring請求引數校驗
SpringMVC支援的資料校驗是JSR303的標準,通過在bean的屬性上打上@NotNull、@Max等進行驗證。JSR303提供有很多annotation介面,而SpringMVC對於這些驗證是使用hibernate的實現,所以我們需要新增hibernate的一個validator包: 依賴引用 com
java註解方式引數校驗
1、註解類NotEmpty.java空值校驗 package com.cmbc.umm.core.common.annotation; import java.lang.annotation.Documented; import java.lang.an