前書き
その他のURL
説明
すべてのBeanは、BeanPostProcessorインターフェースのpostProcessBeforeInitializationメソッドとpostProcessAfterInitializationメソッドに移動します。
Beanのライフサイクルプロセスを参照してください:Beanライフサイクル-summary_feiying0canglang的博客-CSDN博客
簡単な例
その他のURL
コード
package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class TestProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization==> " + "bean:" + bean + "; " + "beanName:" + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization==> " + "bean:" + bean + "; " + "beanName:" + beanName); return bean; } }
実行結果(長すぎる、その一部のみ)
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0.RELEASE) 2021-03-04 23:40:59.246 INFO 5820 --- [ main] com.example.DemoApplication : Starting DemoApplication on DESKTOP-QI6B9ME with PID 5820 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot) 2021-03-04 23:40:59.248 INFO 5820 --- [ main] com.example.DemoApplication : No active profile set, falling back to default profiles: default postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat@1136b469; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat@1136b469; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat postProcessBeforeInitialization==> bean:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory@512d4583; beanName:tomcatServletWebServerFactory postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@2abc224d; beanName:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@2abc224d; beanName:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.TomcatWebSocketServletWebServerCustomizer@2b97cc1f; beanName:websocketServletWebServerCustomizer postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.TomcatWebSocketServletWebServerCustomizer@2b97cc1f; beanName:websocketServletWebServerCustomizer postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration@64f555e7; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration@64f555e7; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
高度なアプリケーション:プロパティを注入する
その他のURL
春の戦闘シリーズ(3つ)-BeanPostProcessor_jokeHelloのコラムの魔法の使用-CSDN blog_beanpostprocessor
前書き
Springを開発するとき、同じインターフェースの複数の実装クラス、通常は次のメソッドに遭遇します。
- @ Autowired + @Qualifyを介してインターフェースを導入します。@ Autowired @Qualify( "helloServiceImpl2")private HelloService helloService;
- @ Autowired + @ Primary経由:特定の実装クラスで@Primaryをマークします
- 特定の呼び出し場所では、ApplicationContextを介して、ビジネスのニーズに応じてさまざまなインターフェイス実装クラスを選択できます。ファクトリメソッドは抽象化できますが、それでも十分にエレガントではないと感じます。そうしないと、エラーが報告されます。
実際、上記の方法はすでに開発ニーズを満たすことができるため、方法1をお勧めします。ここでは、BeanPostProcessor +カスタムアノテーションを使用して同じ効果を実現しています。もちろん、このように書くことは車輪の再発明であり、本質的に無意味です。ただし、この例から次の点を学ぶことができます。
- カスタムアノテーションを使用して属性に値を割り当てる方法
- プロキシファクトリクラスの使用法
- BeanPostProcessorの使用法
インターフェース
package com.example.service; public interface HelloService { void sayHello(); }
実装クラス
package com.example.service.impl; import com.example.service.HelloService; import org.springframework.stereotype.Service; @Service public class HelloServiceImpl1 implements HelloService { @Override public void sayHello() { System.out.println("我是HelloServiceImpl1"); } }
package com.example.service.impl; import com.example.service.HelloService; import org.springframework.stereotype.Service; @Service public class HelloServiceImpl2 implements HelloService { @Override public void sayHello() { System.out.println("我是HelloServiceImpl2"); } }
カスタムアノテーション
package com.example.annotation; import org.springframework.stereotype.Component; import java.lang.annotation.*; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface RountingInjected { String value() default "helloServiceImpl1"; }
カスタムBeanPostProcessor実装クラス
package com.example.processor; import com.example.annotation.RountingInjected; import com.example.factory.RoutingBeanProxyFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.Map; @Component public class HelloServiceInjectProcessor implements BeanPostProcessor { @Autowired private ApplicationContext applicationContext; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class<?> targetCls = bean.getClass(); Field[] targetFld = targetCls.getDeclaredFields(); for (Field field : targetFld) { //找到指定目标的注解类 if (field.isAnnotationPresent(RountingInjected.class)) { if (!field.getType().isInterface()) { throw new BeanCreationException("RoutingInjected field must be declared as an interface:" + field.getName() + " @Class " + targetCls.getName()); } try { this.handleRoutingInjected(field, bean, field.getType()); } catch (IllegalAccessException e) { e.printStackTrace(); } } } return bean; } private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException { Map<String, Object> candidates = this.applicationContext.getBeansOfType(type); field.setAccessible(true); if (candidates.size() == 1) { field.set(bean, candidates.values().iterator().next()); } else if (candidates.size() == 2) { String injectVal = field.getAnnotation(RountingInjected.class).value(); Object proxy = RoutingBeanProxyFactory.createProxy(injectVal, type, candidates); field.set(bean, proxy); } else { throw new IllegalArgumentException("Find more than 2 beans for type: " + type); } } }
プロキシ実装クラス
package com.example.factory; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; import java.util.Map; public class RoutingBeanProxyFactory { private final static String DEFAULT_BEAN_NAME = "helloServiceImpl1"; public static Object createProxy(String name, Class type, Map<String, Object> candidates) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(type); proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(name, candidates)); return proxyFactory.getProxy(); } static class VersionRoutingMethodInterceptor implements MethodInterceptor { private Object targetObject; public VersionRoutingMethodInterceptor(String name, Map<String, Object> beans) { this.targetObject = beans.get(name); if (this.targetObject == null) { this.targetObject = beans.get(DEFAULT_BEAN_NAME); } } @Override public Object invoke(MethodInvocation invocation) throws Throwable { return invocation.getMethod().invoke(this.targetObject, invocation.getArguments()); } } }
テストクラス
package com.example.controller; import com.example.annotation.RountingInjected; import com.example.service.HelloService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RountingInjected(value = "helloServiceImpl2") private HelloService helloService; @GetMapping("/test1") public String test1() { helloService.sayHello(); return "test1 success"; } }
テスト
訪問:http:// localhost:8080 / test1
バックエンドの結果:
我是HelloServiceImpl2