激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - 淺談BeanPostProcessor加載次序及其對Bean造成的影響分析

淺談BeanPostProcessor加載次序及其對Bean造成的影響分析

2021-07-29 11:40不動明王1984 Java教程

這篇文章主要介紹了淺談BeanPostProcessor加載次序及其對Bean造成的影響分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

beanpostprocessor是一個工廠鉤子,允許spring框架在新創建bean實例時對其進行定制化修改。例如:通過檢查其標注的接口或者使用代理對其進行包裹。應用上下文會從bean定義中自動檢測出beanpostprocessor并將它們應用到隨后創建的任何bean上。

普通bean對象的工廠允許在程序中注冊post-processors,應用到隨后在本工廠中創建的所有bean上。典型的場景如:post-processors使用postprocessbeforeinitialization方法通過特征接口或其他類似的方式來填充bean;而為創建好的bean創建代理則一般使用postprocessafterinitialization方法。

beanpostprocessor本身也是一個bean,一般而言其實例化時機要早過普通的bean,但是beanpostprocessor也會依賴一些bean,這就導致了一些bean的實例化早于beanpostprocessor,由此會導致一些問題。最近在處理shiro和spring cache整合時就碰到了,導致的結果就是spring cache不起作用。現將問題場景、查找歷程及解決方法展現一下。

1 問題場景

打算在項目中將shiro與spring cache整合,使用spring cache統一管理緩存,也包括shiro認證時的用戶信息查詢。項目中將service分層,outter層負責權限和session,inner層主打事務和緩存并與dao交互,兩層之間也可以較容易的擴展為rpc或微服務模式。因此在shiro的authrealm中依賴了inneruserservice,并在inneruserservice中配置了spring cache的標注,使用cache進行緩存。配置如下(摘錄重要部分):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@bean(name="shirofilter")
public shirofilterfactorybean shirofilter(
 @qualifier("securitymanager") securitymanager manager
 ) {
  shirofilterfactorybean bean=new shirofilterfactorybean();
  bean.setsecuritymanager(manager);
  ..............
  return bean;
}
//配置核心安全事務管理器
@bean(name="securitymanager")
public securitymanager securitymanager(@qualifier("authrealm") authorizingrealm authrealm,
 @qualifier("sessionmanager") sessionmanager sessionmanager,
 @qualifier("cookieremembermemanager") remembermemanager remembermemanager,
 @qualifier("cachemanager") cachemanager cachemanager) {
  system.err.println("--------------shiro已經加載----------------");
  defaultwebsecuritymanager manager=new defaultwebsecuritymanager();
  manager.setrealm(authrealm);
  manager.setsessionmanager(sessionmanager);
  manager.setremembermemanager(remembermemanager);
  manager.setcachemanager(cachemanager);
  return manager;
}
//配置自定義權限登錄器
@bean(name="authrealm")
public authorizingrealm authrealm(iinneruserservice userservice) {
 myrealm myrealm = new myrealm(iinneruserservice);
 logger.info("authrealm myrealm initiated!");
  return myrealm;
}
@bean
public lifecyclebeanpostprocessor lifecyclebeanpostprocessor(){
 return new lifecyclebeanpostprocessor(ordered.lowest_precedence);
}

其中myrealm是自定義的shiro authorizingrealm,用于執行認證與授權,其實現依賴inneruserservice從庫中查找用戶信息,示例代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class myrealm extends authorizingrealm {
 iinneruserservice userservice;
 public myrealm(){
 super();
 }
 public myrealm(iinneruserservice userservice){
 this.userservice = userservice;
 }
 public iinneruserservice getuserservice() {
 return userservice;
 }
 public void setuserservice(iinneruserservice userservice) {
 this.userservice = userservice;
 }
 @override
 protected authorizationinfo dogetauthorizationinfo(
  principalcollection principals) {
 //null usernames are invalid
    if (principals == null) {
      throw new authorizationexception("principalcollection method argument cannot be null.");
    }
    set<string> rolenames = new hashset<string>();
    set<string> permissions = new hashset<string>();
 user user = (user)getavailableprincipal(principals);
 rolenames.add("role1");
 rolenames.add("role2");
 permissions.add("user:create");
 permissions.add("user:update");
 permissions.add("user:delete");
 simpleauthorizationinfo info = new simpleauthorizationinfo(rolenames);
    info.setstringpermissions(permissions);
    return info;
 }
 
 @override
 protected authenticationinfo dogetauthenticationinfo(
  authenticationtoken token) throws authenticationexception {
 string username = (string)token.getprincipal(); //得到用戶名
    string password = new string((char[])token.getcredentials()); //得到密碼
    user user = userservice.findbyusernameinner(username);
    if(user==null){
     throw new unknownaccountexception();
    }else if(!password.equals(user.getpassword()))
 {
     throw new incorrectcredentialsexception();
 }
    else{
     return new simpleauthenticationinfo(user, password, getname());
    }
 }
}

而在inneruserservice中配置了spring cache的標注,示例代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@service
public class iinneruserserviceimpl implements iinneruserservice {
 logger logger = loggerfactory.getlogger(iinneruserserviceimpl.class);
 
 @autowired
 iuserdao userdao;
 
 @override
 @cacheable(value = "mycache", key = "#username")
 public user findbyusernameinner(string username) {
 user user = userdao.findbyusername(username);
 logger.info("real execute find from database, username:{}", username);
 return user;
 }
}

并在配置文件上標注了@enablecaching(mode=advicemode.proxy)以啟動spring cache。這里不過多解釋具體shiro和spring cache的使用,有興趣的同學請自行搜索相關資料。

按理說這樣的配置在認證的時候應該可以直接使用到inneruserservice中配置的spring cache緩存。

但,問題出現了,當authrealm中依賴了inneruserservice以后,定義在inneruserservice上的spring cache就神奇的失效了。而authrealm不依賴inneruserservice的時候,cache卻運行的好好的。

接下來是問題查找的路徑。

2 解決問題之旅

2.1 spring cache失效的表象原因

首先要找到spring cache失效的表象/直接原因,我們知道spring cache使用spring aop和攔截器的方式攔截定義了特定標注的方法,然后執行特定邏輯。因此其實現依賴于動態代理機制auto-proxy,而經過初步調試發現,當被authrealm依賴以后,inneruserservice就不會被代理了,因此無從進入aop的pointcut,也就是說aop切面失效了!

2.2 從spring cache的集成機制分析深層次原因

為何沒有被代理呢,我們先來確認一下正常情況下什么時候進行代理封裝,這時關于beanpostprocessor的定義浮現腦海,據文檔記載beanpostprocessor允許在bean實例化的前后對其做一些猥瑣的事情,比如代理。我們在beanpostprocessor的實現類中發現了instantiationawarebeanpostprocessor、smartinstantiationawarebeanpostprocessor、abstractautoproxycreator、infrastructureadvisorautoproxycreator這一脈。而反觀@enablecache標注在啟動的時候會@import cachingconfigurationselector,其selectimports方法會返回autoproxyregistrar和proxycachingconfiguration的全類名(我們定義了mode=advicemode.proxy),也就是加載這兩個類。第一個的作用就是注冊infrastructureadvisorautoproxycreator到beandefinitionregistry中。第二個的作用就是注冊了beanfactorycacheoperationsourceadvisor和cacheinterceptor。

因此,當正常情況下,一個添加了spring cache相關標注的bean會在創建后被infrastructureadvisorautoproxycreator基于advisor進行代理增強,代理后便可在攔截器cacheinterceptor中對其方法進行攔截,然后執行cache相關邏輯。此處省略具體處理邏輯,有興趣請參考相關文檔。

所以第一懷疑就是inneruserservice沒有經過infrastructureadvisorautoproxycreator的代理增強。果然調試發現,被authrealm依賴的情況下在inneruserservice的bean實例化時,用于處理該bean的postbeanprocessor明顯比沒被authrealm依賴時少,并且不含有infrastructureadvisorautoproxycreator。

而且,被依賴時會多打出來一行信息:

...................
bean 'iinneruserserviceimpl' of type [shiro.web.inner.service.impl.iinneruserserviceimpl] is not eligible for getting processed by all beanpostprocessors (for example: not eligible for auto-proxying)
...................

據此推斷,可能是inneruserservice啟動時機過早,導致的后面那些beanpostprocessor們來沒來得及實例化及注冊呢。

2.3 beanpostprocessor啟動階段對其依賴的bean造成的影響

首先確認了authrealm也是受害者,因為shirofilter->securitymanager->authrealm的依賴關系導致其不得不提前實例化。表面上的罪魁禍首是shirofilter,但是到底是誰導致的shirofilter預料之外的提前啟動呢。shirofilter與infrastructureadvisorautoproxycreator的具體啟動時機到底是什么時候呢。

又經過一番混天暗地的調試,終于了解了beanpostprocessor的啟動時機。在abstractbeanfactory中維護了beanpostprocessor的列表:

?
1
private final list<beanpostprocessor> beanpostprocessors = new arraylist<beanpostprocessor>();

 

并實現了configurablebeanfactory定義的方法:

?
1
void addbeanpostprocessor(beanpostprocessor beanpostprocessor);

因此我們首先監控abstractbeanfactory.addbeanpostprocessor(),看看啟動過程中誰調用了該方法來注冊beanpostprocessor。發現實例化及注冊postbeanfactory的階段分為四個: 

第一階段是在啟動時調用過程會調用abstractapplicationcontext.refresh(),其中的preparebeanfactory方法中注冊了

applicationcontextawareprocessor、applicationlistenerdetector:
........
beanfactory.addbeanpostprocessor(new applicationcontextawareprocessor(this));
........
beanfactory.addbeanpostprocessor(new applicationlistenerdetector(this));
........

然后在postprocessbeanfactory方法中注冊了webapplicationcontextservletcontextawareprocessor:

?
1
2
beanfactory.addbeanpostprocessor(
  new webapplicationcontextservletcontextawareprocessor(this));

然后在invokebeanfactorypostprocessors方法中調用

 

復制代碼 代碼如下:
postprocessorregistrationdelegate.invokebeanfactorypostprocessors(beanfactory, getbeanfactorypostprocessors());

 

其中對已經注冊的beanfactorypostprocessors挨個調用其postprocessbeanfactory方法,其中有一個configurationclasspostprocessor,其postprocessbeanfactory方法中注冊了一個importawarebeanpostprocessor:

?
1
beanfactory.addbeanpostprocessor(new importawarebeanpostprocessor(beanfactory));

最后在registerbeanpostprocessors方法中調用

?
1
postprocessorregistrationdelegate.registerbeanpostprocessors(beanfactory, this);

在該方法中,首先注冊beanpostprocessorchecker:

 

復制代碼 代碼如下:
beanfactory.addbeanpostprocessor(new beanpostprocessorchecker(beanfactory, beanprocessortargetcount));

 

該beanpostprocessorchecker就是輸出上面那行信息的真兇,它會在bean創建完后檢查可在當前bean上起作用的beanpostprocessor個數與總的beanpostprocessor個數,如果起作用的個數少于總數,則報出上面那句信息。

然后分成三個階段依次實例化并注冊實現了priorityordered的beanpostprocessor、實現了ordered的beanpostprocessor、沒實現ordered的beanpostprocessor,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// separate between beanpostprocessors that implement priorityordered,
// ordered, and the rest.
list<beanpostprocessor> priorityorderedpostprocessors = new arraylist<beanpostprocessor>();
list<beanpostprocessor> internalpostprocessors = new arraylist<beanpostprocessor>();
list<string> orderedpostprocessornames = new arraylist<string>();
list<string> nonorderedpostprocessornames = new arraylist<string>();
for (string ppname : postprocessornames) {
 if (beanfactory.istypematch(ppname, priorityordered.class)) {
 beanpostprocessor pp = beanfactory.getbean(ppname, beanpostprocessor.class);
 priorityorderedpostprocessors.add(pp);
 if (pp instanceof mergedbeandefinitionpostprocessor) {
  internalpostprocessors.add(pp);
 }
 }
 else if (beanfactory.istypematch(ppname, ordered.class)) {
 orderedpostprocessornames.add(ppname);
 }
 else {
 nonorderedpostprocessornames.add(ppname);
 }
}
 
 
// first, register the beanpostprocessors that implement priorityordered.
sortpostprocessors(priorityorderedpostprocessors, beanfactory);
registerbeanpostprocessors(beanfactory, priorityorderedpostprocessors);
 
 
// next, register the beanpostprocessors that implement ordered.
list<beanpostprocessor> orderedpostprocessors = new arraylist<beanpostprocessor>();
for (string ppname : orderedpostprocessornames) {
 beanpostprocessor pp = beanfactory.getbean(ppname, beanpostprocessor.class);
 orderedpostprocessors.add(pp);
 if (pp instanceof mergedbeandefinitionpostprocessor) {
 internalpostprocessors.add(pp);
 }
}
sortpostprocessors(orderedpostprocessors, beanfactory);
registerbeanpostprocessors(beanfactory, orderedpostprocessors);
 
 
// now, register all regular beanpostprocessors.
list<beanpostprocessor> nonorderedpostprocessors = new arraylist<beanpostprocessor>();
for (string ppname : nonorderedpostprocessornames) {
 beanpostprocessor pp = beanfactory.getbean(ppname, beanpostprocessor.class);
 nonorderedpostprocessors.add(pp);
 if (pp instanceof mergedbeandefinitionpostprocessor) {
 internalpostprocessors.add(pp);
 }
}
registerbeanpostprocessors(beanfactory, nonorderedpostprocessors);
 
 
// finally, re-register all internal beanpostprocessors.
sortpostprocessors(internalpostprocessors, beanfactory);
registerbeanpostprocessors(beanfactory, internalpostprocessors);
 
 
// re-register post-processor for detecting inner beans as applicationlisteners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanfactory.addbeanpostprocessor(new applicationlistenerdetector(applicationcontext));

需要注意的是,除了第一個階段,其他階段同一個階段的beanpostprocessor是在全部實例化完成以后才會統一注冊到beanfactory的,因此,同一個階段的beanpostprocessor及其依賴的bean在實例化的時候是無法享受到相同階段但是先實例化的beanpostprocessor的“服務”的,因為它們還沒有注冊。

從上面調試與源代碼分析,beanpostprocessor的實例化與注冊分為四個階段,第一階段applicationcontext內置階段、第二階段priorityordered階段、第三階段ordered階段、第四階段nonordered階段。而beanpostprocessor同時也是bean,其注冊之前一定先實例化。而且是分批實例化和注冊,也就是屬于同一批的beanpostprocesser全部實例化完成后,再全部注冊,不存在先實例化先注冊的問題。而在實例化的時候其依賴的bean同樣要先實例化。 

因此導致一個結果就是,被priorityorderedbeanpostprocessor所依賴的bean其初始化時無法享受到priorityordered、ordered、和nonordered的beanpostprocessor的服務。而被orderedbeanpostprocessor所依賴的bean無法享受ordered、和nonordered的beanpostprocessor的服務。最后被nonorderedbeanpostprocessor所依賴的bean無法享受到nonorderedbeanpostprocessor的服務。

由于infrastructureadvisorautoproxycreator的啟動階段是ordered,因此我們需要確保沒有任何priorityordered和ordered的beanpostprocessor直接或間接的依賴到shirofilter,也就是依賴到我們的inneruserservice。

同時,在priorityordered接口的注解中也提到了該情況:

note: {@code priorityordered} post-processor beans are initialized in
  * a special phase, ahead of other post-processor beans. this subtly
  * affects their autowiring behavior: they will only be autowired against
  * beans which do not require eager initialization for type matching.

2.4 beanpostprocessor在進行依賴的bean注入時,根據bean名稱進行類型檢查時導致的“誤傷”

ok,問題貌似已查明,修改configuration中所有priorityordered和ordered類型的postbeanprocessor的bean配置,使其不再依賴shirofilter。再次啟動,卻發現仍然提前啟動了shirofilter->securitymanager->authrealm->inneruserservice。

百思不得其解,又是一輪昏天暗地的調試,查找shirofilter具體的啟動時機。發現在一個叫做datasourceinitializerpostprocessor的beanpostprocessor實例化的時候,在根據類型獲得其依賴的參數時,對shirofilter執行了初始化。導致后續securitymanager->authrealm->inneruserservice統統提前初始化。但是在datasourceinitializerpostprocessor之前的beanpostprocessor卻沒有。經調試它們是否會導致shirofilter初始化的區別在調用abstractbeanfactory.istypematch方法時出現:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean istypematch(string name, resolvabletype typetomatch) throws nosuchbeandefinitionexception{
.....................
// check bean class whether we're dealing with a factorybean.
if (factorybean.class.isassignablefrom(beantype)) { //(1)判斷名稱對應的bean是否是一個factorybean,若是factorybean才執行本句
 if (!beanfactoryutils.isfactorydereference(name)) {
 // if it's a factorybean, we want to look at what it creates, not the factory class.
 beantype = gettypeforfactorybean(beanname, mbd);
 if (beantype == null) {
  return false;
 }
 }
}
.....................
}

然后進入abstractautowirecapablebeanfactory.gettypeforfactorybean方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@override
protected class<?> gettypeforfactorybean(string beanname, rootbeandefinition mbd) {
string factorybeanname = mbd.getfactorybeanname();
string factorymethodname = mbd.getfactorymethodname();
 
 
if (factorybeanname != null) {
 if (factorymethodname != null) {
 // try to obtain the factorybean's object type from its factory method declaration
 // without instantiating the containing bean at all.
 beandefinition fbdef = getbeandefinition(factorybeanname);
 if (fbdef instanceof abstractbeandefinition) {
  abstractbeandefinition afbdef = (abstractbeandefinition) fbdef;
  if (afbdef.hasbeanclass()) {
  class<?> result = gettypeforfactorybeanfrommethod(afbdef.getbeanclass(), factorymethodname);
  if (result != null) {
   return result;
  }
  }
 }
 }
 // if not resolvable above and the referenced factory bean doesn't exist yet,
 // exit here - we don't want to force the creation of another bean just to
 // obtain a factorybean's object type...
 if (!isbeaneligibleformetadatacaching(factorybeanname)) {  //(2)判斷該bean對應的factorybeanname是否已經初始化了,如果沒有,就返回。如果有,則繼續
 return null;
 }
}
 
 
// let's obtain a shortcut instance for an early getobjecttype() call...
factorybean<?> fb = (mbd.issingleton() ?
 getsingletonfactorybeanfortypecheck(beanname, mbd) :
 getnonsingletonfactorybeanfortypecheck(beanname, mbd));
 
 
......................
}

其中,有一個重要的判斷:

?
1
2
3
4
5
6
   // if not resolvable above and the referenced factory bean doesn't exist yet,
// exit here - we don't want to force the creation of another bean just to
// obtain a factorybean's object type...
if (!isbeaneligibleformetadatacaching(factorybeanname)) {
return null;
}

注解說的很明確,如果名字對應的factorybean所在的factorybean工廠尚未解析并實例化,那就直接退出,不會強制創建該facotrybean工廠,也就是configuration對應的bean。再次調試,果然發現,在先前的beanpostprocessor和datasourceinitializerpostprocessor之間,存在一個lifecyclebeanpostprocessor,而lifecyclebeanpostprocessor是在我們的configuration中顯示定義的,因此,當lifecyclebeanpostprocessor啟動時會導致configuration實例化。 

datasourceinitializerpostprocessor和在它之前的beanpostprocessor對shirofilter行為的不同在這里得到了完美的解釋。本質上說datasourceinitializerpostprocessor并不重要,重要的是lifecyclebeanpostprocessor將configuration初始化了。就算不是datasourceinitializerpostprocessor,那另一個beanpostprocessor實例化時同樣會將shirofilter初始化。

最終隱藏大boss查明,解決方案就簡單了,將lifecyclebeanpostprocessor移出到一個單獨的configuration就好了。

3. 總結

3.1 beanpostprocessor啟動順序,以及其對于依賴的bean的影響

beanpostprocessor的啟動時機。分為四個階段,第一階段context內置階段、第二階段priorityordered階段、第三階段ordered階段、第四階段nonordered階段。

而beanpostprocessor同時也是bean,其注冊之前一定先實例化。而且是分批實例化和注冊,也就是屬于同一批的beanpostprocesser全部實例化完成后,再全部注冊,不存在先實例化先注冊的問題。而在實例化的時候其依賴的bean同樣要先實例化。

因此導致一個結果就是,被priorityorderedbeanpostprocessor所依賴的bean其初始化以后無法享受到priorityordered、ordered、和nonordered的beanpostprocessor的服務。而被orderedbeanpostprocessor所依賴的bean無法享受ordered、和nonordered的beanpostprocessor的服務。最后被nonorderedbeanpostprocessor所依賴的bean無法享受到nonorderedbeanpostprocessor的服務。

3.2 注意避免beanpostprocessor啟動時的“誤傷”陷阱

beanpostprocessor實例化時,自動依賴注入根據類型獲得需要注入的bean時,會將某些符合條件的bean(factorybean并且其factorybeanfactory已經實例化的)先實例化,如果此facotrybean又依賴其他普通bean,會導致該bean提前啟動,造成誤傷(無法享受部分beanpostprocessor的后處理,例如典型的auto-proxy)。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/m0_37962779/article/details/78605478

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: a一级黄| 国产一级伦理片 | 欧美成人一区二区三区电影 | 久久久青 | 久久午夜神器 | 久草资源在线观看 | 国产91九色在线播放 | 性aaa| 免费看成人av | 亚洲字幕av| 91成人在线免费观看 | 一级网站 | 成人亚洲一区二区 | 海外中文字幕在线观看 | 国产黄色毛片 | 欧美亚洲一区二区三区四区 | 成人免费毛片片v | 欧产日产国产精品乱噜噜 | 国产三级国产精品国产普男人 | 成人三区四区 | 免费日本一区二区 | 成人福利视频在线观看 | 欧美黄成人免费网站大全 | 亚洲91精品 | 成人免费一区二区三区在线观看 | 国产午夜精品久久久久 | 国产精品视频中文字幕 | 精品偷拍久久 | 国产伦久视频免费观看视频 | 99亚洲视频 | 亚洲精品成人在线视频 | 亚洲成人精品一区二区 | 精国品产一区二区三区有限公司 | 久久精品一区二区三 | 免费看成人av | 91视频网| 久久精品免费网站 | 九九色网站| www.9191.com| 狠狠干最新网址 | 欧美成年人在线视频 |