ピット1:依存BeanはBeanPostProcessorを使用しません
その他のURL
前書き
BeanPostProcessorの実装クラスで、他のBeanが依存している場合、依存Beanが作成されると、BeanPostProcessorの実装クラスによって実装されたメソッドは実行されません。
BeanPostProcessorの実装クラスは他のBeanに依存しているため、このBeanはPostBeanの前に作成する必要があります。つまり、このBeanが作成されたとき、BeanPostProcessorの実装クラスはまだ初期化されておらず、そのメソッドは呼び出されません。
検証
BeanPostProcessor実装クラス
package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyProcessor implements BeanPostProcessor { @Autowired MyBean myBean; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyBean) { System.out.println("postProcessBeforeInitialization==> " + "This is MyBean"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyBean) { System.out.println("postProcessAfterInitialization==> " + "This is MyBean"); } return bean; } }
依存豆
package com.example.processor; import org.springframework.stereotype.Component; @Component public class MyBean { }
テスト
起動後、関連する印刷はありません。
@Autowired MyBean myBeanが削除されると、起動後に次のように出力されます。
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0.RELEASE) 2021-03-05 21:39:47.861 INFO 16100 --- [ main] com.example.DemoApplication : Starting DemoApplication on DESKTOP-QI6B9ME with PID 16100 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot) 2021-03-05 21:39:47.864 INFO 16100 --- [ main] com.example.DemoApplication : No active profile set, falling back to default profiles: default 2021-03-05 21:39:48.645 INFO 16100 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2021-03-05 21:39:48.652 INFO 16100 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-03-05 21:39:48.652 INFO 16100 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35] 2021-03-05 21:39:48.730 INFO 16100 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-03-05 21:39:48.731 INFO 16100 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 825 ms postProcessBeforeInitialization==> This is MyBean postProcessAfterInitialization==> This is MyBean 2021-03-05 21:39:48.865 INFO 16100 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-03-05 21:39:48.998 INFO 16100 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-03-05 21:39:49.007 INFO 16100 --- [ main] com.example.DemoApplication : Started DemoApplication in 1.557 seconds (JVM running for 2.638)
ピット2:AOPを使用できません
その他のURL
SpringBeanPostProcessorインターフェースで話す-Angによるエージェント-自動配線されたブログ
BeanPostProcessorは失敗につながるAOP | Big BoxBeanPostProcessorが信頼を開始するとき「フレンドリーファイア」トラップのBean(すべての人が処理する資格はありません...)_ YourBatman-CSDNブログ
は春の構成事故を覚えています-フライアウンスノー-ブログパーク
ビジネスクラスはAOPプロキシの問題にはなり得ません
理由
簡単な説明
BeanPostProcessorおよび依存BeanはAOPを使用できない場合があります。
現時点では、次のような出力メッセージが表示されます。trationDelegate$ BeanPostProcessorChecker:タイプ[com.example.processor.MyBean]のBean'myBean 'は、すべてのBeanPostProcessorsによる処理の対象ではありません(例:auto-の対象外)プロキシ)
ソースの場所
これはBeanPostProcessorCheckerクラスによって出力されます。ソースコードで検索すると、このクラスはPostProcessorRegistrationDelegateの静的内部クラスであることがわかります。
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { if (logger.isInfoEnabled()) { logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() + "] is not eligible for getting processed by all BeanPostProcessors " + "(for example: not eligible for auto-proxying)"); } } return bean; }
このログを印刷する理由
beanFactoryに登録されているBeanPostProcessorsの数がBeanPostProcessorsの総数より少ない場合。つまり、準備ができていない他のポストプロセッサがある場合、このBean(ここではmyBean)がインスタンス化されます。このBeanがインスタンス化される理由は、一般的に次のとおりです。BeanPostProcessorの実装クラスがこのBeanを参照し、これがこのBeanのインスタンス化につながります。
春の公式文書の原文:
BeanPostProcessorインスタンスとAOP自動プロキシ
BeanPostProcessorインターフェースを実装するクラスは特別であり、コンテナーによって異なる方法で処理されます。直接参照するすべてのBeanPostProcessorインスタンスとBeanは、ApplicationContextの特別な起動フェーズの一部として、起動時にインスタンス化されます。次に、すべてのBeanPostProcessorインスタンスがソートされた方法で登録され、コンテナー内の以降のすべてのBeanに適用されます。AOP自動プロキシはBeanPostProcessor自体として実装されているため、BeanPostProcessorインスタンスも、それらが直接参照するBeanも自動プロキシの対象にはならず、したがって、アスペクトが組み込まれていません。
このようなBeanについては、情報ログメッセージが表示されます。BeansomeBeanは、すべてのBeanPostProcessorインターフェースで処理される資格がありません(たとえば、自動プロキシの資格がありません)。
翻訳:
BeanPostProcessorインターフェースを実装するクラスは特別であり、コンテナーによって異なる方法で処理されます。すべてのBeanPostProcessorインスタンスとそれらの直接参照されるBeanは、ApplicationContextの特別な起動フェーズの一部として、起動時にインスタンス化されます。次に、すべてのBeanPostProcessorインスタンスがソートされた方法で登録され、次のBeanに適用されます。SpringのAOP自動プロキシはBeanPostProcessorインターフェースを実装することによって行われるため、BeanPostProcessorの実装クラスとそれらが直接参照するBeanはAOP自動プロキシの条件を満たしていないため、AOPに組み込むことはできません。
これらのBeanについては、いくつかのINFOレベルの情報が表示されます。BeansomeBeanは、すべてのBeanPostProcessorインターフェースによって処理される資格がありません(たとえば、自動プロキシの資格がありません)。
インスタンス
再発
BeanPostProcessor実装クラス
Orderedインターフェースを意図的に実装して、他のBeanPostProcessor実装クラスよりも早くMyProcessorの初期化をシミュレートします。そうしないと、再現が容易になりません。ここでテストしたところ、最高優先または最低優先に指定しましたが、効果は同じでした。優先度については、以下を参照してください。Beanライフサイクル-BeanPostProcessorの概要_feiying0canglangのブログ-CSDNブログ
さらに、順序を指定するには、ここにOrderedインターフェイスを実装する必要があります。@ Orderを使用すると、無効になります。推測:@OrderもBeanPostProcessorインターフェースを実装することによって行われるため、これも無効です。package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; @Component public class MyProcessor implements BeanPostProcessor, Ordered { @Autowired private MyBean myBean; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyBean) { System.out.println("postProcessBeforeInitialization==> " + "This is MyBean"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyBean) { System.out.println("postProcessAfterInitialization==> " + "This is MyBean"); } return bean; } // 此方法用来测试AOP,作为切点 public void testAOP() { System.out.println("testAOP方法(MyProcessor)"); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
豆
package com.example.processor; import org.springframework.stereotype.Component; @Component public class MyBean { // 此方法用来测试AOP,用作切点 public void testAOP() { System.out.println("testAOP方法(MyBean)"); } }
セクション
package com.example.processor; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class BeanPostProcessorAspect { // 此方法织入PostBean的testAOP方法 @Before("execution(* com.example.processor.MyProcessor.testAOP(..))") public void before() { System.out.println("before MyProcessor#testAOP"); } // 此方法织入MyBean的testAOP方法 @Before("execution(* com.example.processor.MyBean.testAOP(..))") public void before2() { System.out.println("before MyBean#testAOP"); } }
ApplicationContextHolderツールクラス
package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { ApplicationContextHolder.context = context; } public static ApplicationContext getContext() { return context; } }
テストクラス
package com.example.controller; import com.example.processor.MyBean; import com.example.processor.MyProcessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @Autowired MyProcessor myProcessor; @Autowired MyBean myBean; @GetMapping("/test1") public String test1() { myProcessor.testAOP(); myBean.testAOP(); return "test1 success"; } // @GetMapping("/test1") // public String test1() { // MyProcessor myProcessor = ApplicationContextHolder.getContext().getBean(MyProcessor.class); // myProcessor.testAOP(); // MyBean myBean = ApplicationContextHolder.getContext().getBean(MyBean.class); // myBean.testAOP(); // return "test1 success"; // } }
テスト
1.AOPができないという現象を再現します
起動:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0.RELEASE) 2021-03-06 12:23:36.622 INFO 14368 --- [ main] com.example.DemoApplication : Starting DemoApplication on DESKTOP-QI6B9ME with PID 14368 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot) 2021-03-06 12:23:36.625 INFO 14368 --- [ main] com.example.DemoApplication : No active profile set, falling back to default profiles: default 2021-03-06 12:23:37.237 INFO 14368 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'myBean' of type [com.example.processor.MyBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2021-03-06 12:23:37.492 INFO 14368 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2021-03-06 12:23:37.500 INFO 14368 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-03-06 12:23:37.500 INFO 14368 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35] 2021-03-06 12:23:37.590 INFO 14368 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-03-06 12:23:37.590 INFO 14368 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 927 ms 2021-03-06 12:23:37.718 INFO 14368 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-03-06 12:23:37.847 INFO 14368 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-03-06 12:23:37.855 INFO 14368 --- [ main] com.example.DemoApplication : Started DemoApplication in 1.553 seconds (JVM running for 2.454)
訪問:http:// localhost:8080 / test1
結果:(BeanPostProcessor実装クラスによって注入された通常のBeanもBeanPostProcessor実装クラスもAOPではありません)説明:コントローラーの2番目のメソッドも同じ結果です。
testAOP方法(MyProcessor) testAOP方法(MyBean)
2.BeanPostProcessorをAOPに削除します
BeanPostProcessor実装クラスの実装BeanPostProcessorを削除し、オーバーライドされたメソッドを削除します。(その他は同じままです。たとえば、次のようになります。引き続きOrderedインターフェイスを実装します)
結果は次のとおりです(BeanPostProcessor実装クラスとBeanPostProcessor実装クラスによって注入される通常のBeanは両方ともAOPです)
before MyProcessor#testAOP testAOP方法(MyProcessor) before MyBean#testAOP testAOP方法(MyBean)
追跡
このオフィスは上記のコードをトレースします「1.AOPが再現できない現象を再現します」、入り口:インスタンス化されているMyBeanのソースコード
Beanのインスタンス化エントリポイントは次のとおりです。AbstractBeanFactory#doGetBean。次の場所に条件付きブレークポイントを作成します。
プロジェクトを開始し、このブレークポイントに3回到達したことを確認します。つまり、MyBeanをインスタンス化する場所が3つあります。
初めて:MyBeanはBeanPostProcessor実装クラスによって参照されます
2回目:HelloControllerはMyBeanを参照します
3回目:MyBean自体がコンテナに参加します。もちろん、インスタンス化する必要があります
解決する
方法1:遅延初期化を使用する
コード
BeanPostProcessorの実装クラスは、次のようにMyBeanを導入します。
@Lazy @Autowired private MyBean myBean;
テスト
起動時:
postProcessBeforeInitialization==> This is MyBean postProcessAfterInitialization==> This is MyBean
訪問:http:// localhost:8080 / test1
結果(BeanPostProcessor実装クラスによって注入された通常のBeanはAOPになる可能性がありますが、BeanPostProcessor実装クラスはAOPではありません)
testAOP方法(MyProcessor) before MyBean#testAOP testAOP方法(MyBean)
方法2:ApplicationContextを使用する
もちろん、この方法をここで適用するのは簡単ではありません。Shiroを構成するときにサービスを注入するために使用できます。
private UserService getUserService() { return (UserService) applicationContext.getBean(UserService.class); }
ピット3:登録方法と制限
その他のURL
前書き
SpringコンテナにBeanPostProcessorを登録する方法は?主に2つの方法があります。
- 通常のBeanとして、Spring構成クラスまたはxmlファイルで宣言し、ApplicationContextオブジェクトにロードさせて、コンテナーに自動的に登録されるようにします。また、Springコンテナは、BeanPostProcessorの実装クラスに対して特別な処理を行います。つまり、それらが選択され、BeanPostProcessorの実装クラスが最初にロードされてから、他のBeanがロードされます。
- 手動で追加するには、ConfigurableBeanFactoryインターフェースのaddBeanPostProcessorメソッドを使用します。ConfigurableBeanFactoryの実装クラスオブジェクトは、ApplicationContextオブジェクトに結合されます。ただし、このようにBeanPostProcessorを追加すると、次のようないくつかの欠点があります。
- Springコンテナーが作成されると、構成ファイル内のシングルトンBeanがロードされます。この時点では、addBeanPostProcessorメソッドは実行されておらず、手動で追加されたBeanPostProcessorはこれらのBeanに作用できないため、手動で追加されたBeanPostProcessorはにのみ作用します。遅延ロードされたBean。、または非シングルトンBean。
- Orderedインターフェースの役割は無効になりますが、登録順に実行されます。前述のように、Orderedインターフェースは、複数のBeanPostProcessorsによって実装されるメソッドの実行順序を指定するために使用されます。これは、Springの公式ドキュメントに記載されています。BeanPostProcessor登録の推奨アプローチは(上記のように)ApplicationContext自動検出によるものですが、addBeanPostProcessorメソッドを使用してConfigurableBeanFactoryに対してプログラムで登録することもできます。これは必要な場合に役立ちます。登録前に条件付きロジックを評価するため、または階層内のコンテキスト間でBeanポストプロセッサをコピーする場合。ただし、プログラムで追加されたBeanPostProcessorは、Orderedインターフェイスを尊重しないことに注意してください。ここでは、実行の順序を決定するのは登録の順序です。プログラムで登録されたBeanPostProcessorは、明示的な順序に関係なく、自動検出によって登録されたものよりも常に前に処理されます。
ピット4:@Bean構成の使用に関する制限
その他のURL
前書き
JavaクラスでSpringを構成し、@ Beanを使用してBeanインスタンスを返すファクトリメソッドを宣言する場合、戻り値はBeanPostProcessor型、またはBeanPostProcessorよりも小さい型である必要があります。
デモ
package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; @Component public class MyProcessor implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization==> " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization==> " + beanName); return bean; } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
構成クラスでは、MyProcessorを宣言する方法がいくつかあります。
@Configuration public class BeanConfig { // 方式1:MyProcessor @Bean public MyProcessor myProcessor() { return new MyProcessor(); } // 方式2:返回值为BeanPostProcessor @Bean public BeanPostProcessor myProcessor() { return new MyProcessor(); } // 方式3:返回值为Ordered @Bean public Ordered postBean() { return new MyProcessor(); } }
上記の3つのメソッドを使用すると、SpringコンテナでMyProcessorインスタンスオブジェクトを作成できます。MyProcessorはBeanPostProcessorインターフェイスとOrderedインターフェイスを実装しているため、そのオブジェクトもこれら2つのタイプのオブジェクトです。ただし、上記の3つのメソッドのうち、1番目と2番目のメソッドのみがSpringコンテナにMyProcessorをBeanPostProcessorとして処理させ、3番目のメソッドはBeanPostProcessorを実装するための通常のBeanとして処理されることに注意してください。どちらのメソッドも呼び出されません。 。MyProcessorの継承システムでは、OrderedとBeanPostProcessorが同じレベルにあるため、SpringはこのOrderedオブジェクト(これもBeanPostProcessorオブジェクト)を認識できませんが、MyProcessorタイプはBeanPostProcessorのサブタイプであるため、MyProcessorを使用できます。したがって、@ Bean宣言ファクトリメソッドを使用してBeanPostProcessor実装クラスオブジェクトを返す場合、戻り値はBeanPostProcessor型または下位レベルの型である必要があります。Springの公式ドキュメントでは、この部分の内容は次のとおりです。
構成クラスで@Beanファクトリメソッドを使用してBeanPostProcessorを宣言する場合、ファクトリメソッドの戻り値の型は実装クラス自体または少なくともorg.springframework.beans.factory.config.BeanPostProcessorインターフェイスである必要があります。そのBeanのポストプロセッサの性質。そうしないと、ApplicationContextは、完全に作成する前にタイプごとに自動検出できません。コンテキスト内の他のBeanの初期化に適用するには、BeanPostProcessorを早期にインスタンス化する必要があるため、この早期の型検出は重要です。