@validated注解實(shí)現(xiàn)

用法


public interface UserService {
    public  UserModel get2( Integer uuid) ;
}
@Validated      //① 告訴MethodValidationPostProcessor此Bean需要開啟方法級(jí)別驗(yàn)證支持
@Component
public class UserServiceImpl implement UserService  {
    public @NotNull UserModel get2(@NotNull @Min(value = 1) Integer uuid) { //②聲明前置條件/后置條件
        //獲取 User Model
        UserModel user = new UserModel(); //此處應(yīng)該從數(shù)據(jù)庫(kù)獲取
        if(uuid > 100) {//方便后置添加的判斷(此處假設(shè)傳入的uuid>100 則返回null)
            return null;
        }
        return user;
    }
}

<!--注冊(cè)方法驗(yàn)證的后處理器-->
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

@validated和@valid不同點(diǎn)

在spring項(xiàng)目中唉工,@validated和@valid功能很類似,都可以在controller層開啟數(shù)據(jù)校驗(yàn)功能乏盐。
但是@validated和@valid又不盡相同佳窑。有以下不同點(diǎn):

  1. 分組
  2. 注解地方,@Valid可以注解在成員屬性(字段)上,但是@Validated不行
  3. 由于第2點(diǎn)的不同,將導(dǎo)致@Validated不能做嵌套校驗(yàn)
  4. @valid只能用在controller。@Validated可以用在其他被spring管理的類上父能。
    對(duì)于第4點(diǎn)的不同神凑,體現(xiàn)了@validated注解其實(shí)又更實(shí)用的功能。那就是@validated可以用在普通bean的方法校驗(yàn)上法竞。

@validated的使用注意點(diǎn)

1 @validated和@valid都可以用在controller層的參數(shù)前面耙厚,但這只能在controller層生效强挫。
2 @validated如果要開啟方法驗(yàn)證。注解應(yīng)該打在類上薛躬,而不是方法參數(shù)上俯渤。
3 方法驗(yàn)證模式下,被jsr303標(biāo)準(zhǔn)的注解修飾的可以是方法參數(shù)也可以是返回值型宝,類似如下
public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
4 @validated不支持嵌套驗(yàn)證八匠。所以jsr303標(biāo)準(zhǔn)的注解修飾的對(duì)象只能基本類型和包裝類型。其他類型只能做到檢測(cè)是否為空趴酣,
對(duì)于對(duì)象里面的jsr303標(biāo)準(zhǔn)的注解修飾的屬性梨树,不支持驗(yàn)證。

如何實(shí)現(xiàn)

看MethodValidationPostProcessor類繼承圖譜


clipboard.png

實(shí)現(xiàn)了InitializingBean和BeanPostProcessor岖寞。所以我們重點(diǎn)看生命周期的2個(gè)節(jié)點(diǎn)方法抡四。
BeanPostProcessor.postProcessAfterInitialization
InitializingBean.afterPropertiesSet
根據(jù)生命周期的順序,先執(zhí)行afterPropertiesSet方法仗谆。

MethodValidationPostProcessor.afterPropertiesSet
    ->Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);//創(chuàng)建了切入點(diǎn)
      this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));//創(chuàng)建了切面指巡。

然后再看postProcessAfterInitialization。實(shí)現(xiàn)是在MethodValidationPostProcessor的祖父類AbstractAdvisingBeanPostProcessor上

AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization
    ->if (bean instanceof Advised)//如果被攔截的bean已經(jīng)是代理類了隶垮。
        if (this.beforeExistingAdvisors)//并且beforeExistingAdvisors=true的時(shí)候藻雪,
            advised.addAdvisor(0, this.advisor);//把當(dāng)前切面放到代理類切面的第一位±晖蹋可以想到beforeExistingAdvisors參數(shù)的作用是為了保證驗(yàn)證切面優(yōu)先于其他的切面勉耀。
                                                //并且這里我們也能得知,代理類的切面可能不止1個(gè)蹋偏,相當(dāng)于代理類里面還存在攔截器鏈一樣便斥。//后文會(huì)去看這塊的源碼
        ->if (isEligible(bean, beanName))//如果被攔截的bean不是一個(gè)代理類。先校驗(yàn)一下這個(gè)類是否有資格添加上 validated的切面暖侨。原理就是掃描這個(gè)類上面是否有@validated注解
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            proxyFactory.addAdvisor(this.advisor);
            return proxyFactory.getProxy(getProxyClassLoader());//創(chuàng)建代理椭住。
                ->createAopProxy().getProxy(classLoader);
                    ->ProxyCreatorSupport.createAopProxy
                        ->getAopProxyFactory().createAopProxy(this)
                            ->DefaultAopProxyFactory.createAopProxy
                                ->if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {//如果該類有接口或者是代理類了,則直接使用jdk動(dòng)態(tài)代理
                                       return new JdkDynamicAopProxy(config);}
                                   return new ObjenesisCglibAopProxy(config);//否則使用cglib動(dòng)態(tài)代理
                    ->JdkDynamicAopProxy.getProxy
                        ->Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);//這一步找到目標(biāo)類的所有接口字逗。
                                                //另外根據(jù)配置決定是否把Advised接口也加進(jìn)去京郑。加進(jìn)去就意味著代理類也是Advised的實(shí)現(xiàn)類。那這一步顯示是加上了Advised接口
                        ->Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);//看到調(diào)用jdk動(dòng)態(tài)代理的api了葫掉。

小結(jié):

  • 1 什么時(shí)候創(chuàng)建的切面
    在afterPropertiesSet方法中創(chuàng)建切面些举。
    這里我就有一個(gè)疑問了,是不是實(shí)現(xiàn)InitializingBean的類都優(yōu)先被加載呢俭厚?不然怎么提前把切面創(chuàng)建出來呢户魏?

  • 2 怎么掃描注解的
    實(shí)現(xiàn)BeanPostProcessor的方法,可以攔截到所有bean

  • 3 什么時(shí)候給目標(biāo)類做代理的
    也是在BeanPostProcessor.postProcessAfterInitialization方法中做的,攔截了bean之后叼丑,就檢測(cè)是否類似打上了@validated注解,
    如果有就創(chuàng)建代理关翎,

  • 4 如果目標(biāo)類已經(jīng)代理了,怎么辦
    如果已經(jīng)是代理了鸠信,就直接添加切面纵寝。如果想優(yōu)先使用驗(yàn)證切面,則需要設(shè)置優(yōu)先級(jí)為0.那么怎么設(shè)置優(yōu)先級(jí)呢星立?
    直接把beforeExistingAdvisors屬性設(shè)置為true即可爽茴。

  • 5 代理類的攔截器鏈?zhǔn)窃趺磳?shí)現(xiàn)的呢?
    根據(jù)jdk動(dòng)態(tài)代理的知識(shí)绰垂,會(huì)動(dòng)態(tài)的給UserService 類創(chuàng)建一個(gè)實(shí)現(xiàn)類(代理類)并實(shí)現(xiàn)了接口中的所有方法室奏,當(dāng)調(diào)用接口中的方法其實(shí)先經(jīng)過代理類。
    代理類在把請(qǐng)求傳遞給InvocationHandler實(shí)例劲装。估計(jì)InvocationHandler實(shí)現(xiàn)里面有維護(hù)著一個(gè)攔截器鏈胧沫,那么InvocationHandler是怎么設(shè)置的呢?
    接著看源碼

->JdkDynamicAopProxy.getProxy
    ->Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);//看到調(diào)用jdk動(dòng)態(tài)代理的api了占业。
                                                                //通過this可以看到InvocationHandler實(shí)例其實(shí)就是JdkDynamicAopProxy類琳袄。
JdkDynamicAopProxy.invoke(Object proxy, Method method, Object[] args)
    ->List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//原來InvocationHandler
        //保存著切面配置信息AdvisedSupport advised。advised里面存儲(chǔ)了切面集合纺酸,下標(biāo)代表了優(yōu)先級(jí)。這個(gè)是什么時(shí)候傳遞進(jìn)來的呢址否?
        //是在創(chuàng)建proxyFactory的時(shí)候餐蔬,把配置信息傳遞給proxyFactory的 proxyFactory.addAdvisor(this.advisor)。這個(gè)時(shí)候驗(yàn)證切面就進(jìn)到了proxyFactory中去了佑附。
        //創(chuàng)建代理InvocationHandler樊诺,又把切面配置信息傳遞過去的。
        ->AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice
            ->List<Object> chain = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);//把a(bǔ)dvised對(duì)象向下傳遞
                ->DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class<?> targetClass)//這一步主要是遍歷AdvisedSupport
                                                                                                    //對(duì)象的切面集合音同,即AdvisedSupport.advisors词爬。把所有可以攔截這個(gè)方法的切面都裝到一個(gè)集合中去。
           ->MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);//組裝成一個(gè)帶有鏈的執(zhí)行器
               ->retVal = invocation.proceed();//方法內(nèi)部攔截器數(shù)組下標(biāo)為0的攔截器調(diào)用invoke权均。
               |    ->Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);//currentInterceptorIndex初始默認(rèn)是-1,下標(biāo)+1
               |      InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
               ^      return dm.interceptor.invoke(this);//如果驗(yàn)證攔截器優(yōu)先調(diào)用的話顿膨,則這一步一定會(huì)進(jìn)到MethodValidationInterceptor
               |          ->MethodValidationInterceptor.invoke(MethodInvocation invocation)
               |              ->invocation.proceed()---|
               |---------<--------<-----------<--------|
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叽赊,隨后出現(xiàn)的幾起案子恋沃,更是在濱河造成了極大的恐慌,老刑警劉巖必指,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囊咏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)梅割,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門霜第,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人户辞,你說我怎么就攤上這事泌类。” “怎么了咆课?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵末誓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我书蚪,道長(zhǎng)喇澡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任殊校,我火速辦了婚禮晴玖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘为流。我一直安慰自己呕屎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布敬察。 她就那樣靜靜地躺著秀睛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莲祸。 梳的紋絲不亂的頭發(fā)上蹂安,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音锐帜,去河邊找鬼田盈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缴阎,可吹牛的內(nèi)容都是我干的允瞧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛮拔,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼述暂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起语泽,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤贸典,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后踱卵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廊驼,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡据过,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妒挎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳锅。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖酝掩,靈堂內(nèi)的尸體忽然破棺而出鳞芙,到底是詐尸還是另有隱情,我是刑警寧澤期虾,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布原朝,位于F島的核電站,受9級(jí)特大地震影響镶苞,放射性物質(zhì)發(fā)生泄漏喳坠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一茂蚓、第九天 我趴在偏房一處隱蔽的房頂上張望壕鹉。 院中可真熱鬧,春花似錦聋涨、人聲如沸晾浴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脊凰。三九已至,卻和暖如春茂腥,著一層夾襖步出監(jiān)牢的瞬間笙各,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工础芍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人数尿。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓仑性,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親右蹦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诊杆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 注意LifecycleProcessor接口繼承了Lifcycle接口。同時(shí)何陆,增加了2個(gè)方法晨汹,用于處理容器的ref...
    google666s閱讀 1,110評(píng)論 0 51
  • IOC和DI是什么? Spring IOC 的理解贷盲,其初始化過程淘这? BeanFactory 和 FactoryBe...
    justlpf閱讀 3,474評(píng)論 1 21
  • 本文主要涉及下面10個(gè)【SSM】問題 1剥扣、說一下Session的工作原理?如果客戶端禁用cookie铝穷,sessio...
    一只小星_閱讀 962評(píng)論 0 0
  • 概述 Spring是什么钠怯? Spring是一個(gè)開源框架,為了解決企業(yè)應(yīng)用開發(fā)的復(fù)雜性而創(chuàng)建的曙聂,但是現(xiàn)在已經(jīng)不止于企...
    瑯筑閱讀 1,168評(píng)論 2 8
  • 攝影:無塵 無塵:堅(jiān)持快走鍛煉宁脊,練習(xí)正念冥想断国,愛好手機(jī)攝影,學(xué)習(xí)時(shí)間管理榆苞,崇尚簡(jiǎn)法人生稳衬。若把人生旅途比作一本書,我...
    心若了無塵閱讀 696評(píng)論 3 16