[英]Dynamically injecting a JDK dynamic proxy as spring bean - but only if no other implementation is available
我有一種情況,我想查找帶有給定注釋的接口,然后檢查匹配的實現是否可用。 如果沒有,我想提供一個實際上是接口的 JDK 代理的 bean,本質上是什么:
@ConditionalOnMissingBean
會做,除非沒有為每一個編寫工廠方法。
我有“有時”工作的代碼 - 它似乎對類加載器結構非常敏感,特別是,類是從 springboot fat jar 加載(有效)還是某些部分是從單獨的類路徑條目加載的(大多數情況下不起作用) .
這就是我正在做的事情:
@Service
@Order(value = Ordered.LOWEST_PRECEDENCE)
public class RemotingImportService implements BeanFactoryPostProcessor {
private static Log log = LogFactory.getLog(RemotingExportService.class);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// scan classpath with classgraph, then:
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(MyAnnotation.class.getCanonicalName())) {
Class c = Class.forName(classInfo.getName());
if(beanFactory.getBeanNamesForType(c).length > 0) {
implFound = true;
log.info(c.getName()+" already has an implementation ... skipping");
continue;
}
// create proxy, then:
GenericBeanDefinition bdService = new GenericBeanDefinition();
bdService.setBeanClassName(classInfo.getName());
bdService.setInstanceSupplier(new ProxySupplier(proxy));
bdService.setLazyInit(true);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(classInfo.getName(), bdService);
log.info(c.getName()+" has NO implementation ... proxy registerd");
}
在某些情況下,beanfactory 似乎還沒有完成,並且
beanFactory.getBeanNamesForType()
返回一個空列表,盡管它稍后會找到該類型的 bean。 我知道搞砸這可能並不理想——但如果能找到一個與 spring 引導配合得很好的解決方案會很好。
關於如何解決這個問題的任何建議? 將 bean 定義標記為“ConditionalOnMissingBean”的方法也很棒。
您應該使用BeanPostProcessor
而不是BeanFactoryPostProcessor
BeanPostProcessor
由組裝的 bean 操作,而BeanFactoryPostProcessor
使用原始 bean 定義。 在這里閱讀更多
public class ConditionalOnMissingProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware
{
private static final Logger LOG = Logger.getLogger(ConditionalOnMissingProcessor .class);
private ApplicationContext applicationContext;
// Ordering to last in chain.
@Override
public int getOrder()
{
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
/**
* Process each bean and inject proxy objects in fields marked with: {@ConditionalOnMissingBean}
*/
@Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException
{
LOG.debug("Processing bean: " + beanName);
final Class clazz = bean.getClass();
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback()
{
@Override
public void doWith(final Field field)
{
try
{
if(field.isAnnotationPresent(ConditionalOnMissingBean.class))
{
ReflectionUtils.makeAccessible(field);
final String beanFieldName = field.getName();
...
// Find proper implementation in application context
// Or create a proxy.
// Inject proxy or impl into a bean
field.set(bean, <value>));
}
}
catch(IllegalAccessException e)
{
LOG.error("Cannot set " + field.getName() + " in " + beanName, e);
}
}
});
return bean;
}
升級版:
首先,我不得不說一下您的 impl 的缺點(恕我直言):-您正在嘗試掃描類路徑以查找帶有@RemoteEndpoint
注釋的所有接口,並且,如果當前應用程序上下文不包含實現此接口的 bean-創建一個代理bean。 但是,如果我說,並非所有標有@RemoteEndpoint
的接口都應該考慮在內呢? 開發人員應該明確標記這些接口,然后您可以創建所有需要的 bean(例如,開發人員制作了一個公共服務庫)。 - 在使用@RemotingEndpoint(value=RandomService.class)
注釋標記 impl class 時,您可能正在指定冗余信息。 當你實現一個接口時,你已經提到過。
有多種方法可以實現您的想法
在 bean 字段上使用自定義注釋而不是@Autowired
。
優點:
BeanPostProcessor
注入依賴項(代理或 impl)。缺點:
使用常規@Autowired
和@Value
注釋來注入遠程服務代理在這種情況下,您應該使用BeanFactoryPostProcessor
(正如您已經嘗試過的那樣)。 您必須遍歷所有 bean 定義,收集字段名稱的 map 您必須注冊的遠程服務代理的接口(依賴項元信息)。 下一步是使用依賴元信息創建和注冊 bean(僅當上下文中沒有實現 bean 時才創建新的)。 Spring 稍后將自動連接這些字段。 但是,這些依賴項應該是 singleton bean。
優點:
缺點:
仍然使用常規的@Autowired
和@Value
注釋以及BeanFactoryPostProcessor
,但是您應該為每個接口@RemoteEndpoint
注冊一個FactoryBean
,而不是注冊新的 bean。
優點:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.