我必须编写一个需要支持多租户/多客户端的应用程序(即对于每个客户端上下文另一个数据库,不同的业务逻辑bean等)。必须能够从一个客户端环境切换到#34;因此,在运行时,应根据实际客户端上下文从spring上下文返回正确的bean。一些信息/ bean与客户端无关(即一些基本业务逻辑,默认实现)。
我想知道如何在不关闭应用程序并为每个客户端上下文使用不同的bean定义文件的情况下在春天实现这一目标?
我通过使用线程本地上下文看到了具有"Routed DataSource"的解决方案。但要写一个" Bean路由器"对于每个Bean类型,使其成为客户端上下文"具体看起来对我来说太多开销,因此我寻求对基础弹簧框架的一些支持。
XML:
<bean name="silverClientDataSource" class="ch.megloff.common.datasource.DataSource"/>
<bean name="goldClientDataSource" class="ch.megloff.common.datasource.DataSource"/>
的java:
ClientContext clientContext = applicationContext.getBean(ClientContext.class);
clientContext.switchContext("silverClient");
...
// get the datasource for silver Client
DataSource ds = applicationContext.getBean(DataSource.class);
ClientContext clientContext = applicationContext.getBean(ClientContext.class);
clientContext.switchContext("goldClient");
...
// get the datasource for gold Client
DataSource ds = applicationContext.getBean(DataSource.class);
// get oter beans for gold Client or return default implementation in case no context specific bean has been implemented
XXX bean = applicationContext.getBean(XXX.class);
答案 0 :(得分:0)
我从第一个虚拟实现开始,这可能有助于说明我正在寻找的行为。这个例子还没有使用Spring的BeanFactory,只是根据给定的上下文显示了工厂应该做什么,请参阅我的虚拟类ContextAwareBeanFactory。工厂正在寻找实现ContextAware接口的特定类型的bean,并使用给定的上下文来比较值。使用注释而不是使用接口可以实现相同的方法。
下一个级别是从Spring覆盖ApplicationContext和BeanFactory,因此这种方法也可以用于bean注入和自动装配的bean。如果有人能指出我正确的方向并避免让我监督任何陷阱,我将不胜感激。如果你看到我不知道的其他方法或框架技巧,请随意发表评论。谢谢。
@Log4j2
public class TestContextAware extends TestBase {
@Test
public void testContextAware() {
Context<String> clientContext = new ThreadLocalContext<String>();
ContextAwareBeanFactory beanFactory = context.getBean(ContextAwareBeanFactory.class);
beanFactory.setContext(clientContext);
clientContext.setValue("silver");
AbstractTestBean bean = beanFactory.getBean(AbstractTestBean.class);
log.info(bean);
clientContext.setValue("gold");
bean = beanFactory.getBean(AbstractTestBean.class);
log.info(bean);
}
}
控制台输出
23:47:37.393 INFO - TestBean1@566e142
23:47:37.589 INFO - TestBean2@3b24087d
进一步的课程:
public static abstract class AbstractTestBean implements ContextAware {
Object contextValue;
@Override
public void setContextValue(Object value) {
contextValue = value;
}
@Override
public Object getContextValue() {
return contextValue;
}
}
@Component
public static class TestBean1 extends AbstractTestBean {
public TestBean1() {
setContextValue("silver");
}
}
@Component
public static class TestBean2 extends AbstractTestBean {
public TestBean2() {
setContextValue("gold");
}
}
@Component
public class ContextAwareBeanFactory {
@Autowired
private ApplicationContext applicationContext;
@Setter
private Context<?> context;
public <T> T getBean(Class<T> type) {
Object contextValue = context.getValue();
// check if bean implements "ContextAware" interface and if it matches the value
if (contextValue != null) {
Map<String,T> beans = applicationContext.getBeansOfType(type);
for(T bean : beans.values()) {
if (bean instanceof ContextAware) {
if (contextValue.equals(((ContextAware)bean).getContextValue())) {
return bean;
}
}
}
}
return applicationContext.getBean(type);
}
}
@Component
public class ThreadLocalContext<T> implements Context<T> {
private static ThreadLocal<Object> holder = new InheritableThreadLocal<Object>();
@SuppressWarnings("unchecked")
public T getValue() {
return (T)holder.get();
}
public void setValue(T value) {
holder.set(value);
}
public void clear() {
holder.remove();
}
@Override
public String toString() {
return String.valueOf(getValue());
}
}
答案 1 :(得分:0)
在研究了几种方法之后,我得出的结论是,对我的问题最简单的解决方案是使用Spring中的"Priority" / "Order"功能来影响您希望在运行时为您的bean检索哪些实现&#34; getBean (班级类型)&#34;。
不建议覆盖ApplicationContext,因为此类存在多种类型的实现,并且它们不提供设置BeanFactory。这意味着你需要覆盖所有类型的ApplicationContext类以保证安全,如果spring的核心框架正在改变或得到更新,这将是危险的。
我唯一复杂且最灵活的方法是使用自己实现的&#34; BeanFactoryPostProcessor&#34;。这允许在&#34; DefaultListableBeanFactory&#34;上设置"DependencyComparator"。用于评估&#34;优先级&#34; /&#34;订单&#34;候选人豆类。
请在此处找到一个示例:
spring.xml
<bean id="tenantContext" class="ThreadLocalContext"></bean>
<bean class="ContextAwareBeanFactoryPostProcessor">
<property name="context" ref="tenantContext"></property>
</bean>
类
public class ContextAwareBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
ContextAwareComparator ContextAwareComparator = new ContextAwareComparator();
public void setContext(Context<?> context) {
ContextAwareComparator.setContext(context);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setDependencyComparator(ContextAwareComparator);
}
}
}
public class ContextAwareComparator extends AnnotationAwareOrderComparator {
@Setter
private Context<?> context;
public Integer getPriority(Object obj) {
if (context != null && obj != null) {
Object value = context.getValue();
if (value != null) {
if (obj instanceof ContextAware) {
if (value.equals(((ContextAware)obj).getContextValue())) {
return Ordered.HIGHEST_PRECEDENCE;
}
}
}
}
return super.getPriority(obj);
}
}
我希望将来Spring&#34; DefaultListableBeanFactory&#34;获得extendend以获得租赁支持。