如果您可以帮助我以下内容,将不胜感激。 假设我有以下类和接口:
public interface BaseType {
public void method();
}
@Component
@Scope(SCOPE_PROTOTYPE)
public class BaseTypeImpl implements BaseType {
private int num;
public BaseTypeImpl(int num) {
this.num = num;
}
@Override
public void method() {
System.out.println(num);
}
}
@Component
@Scope(SCOPE_PROTOTYPE)
public class ChildBaseTypeImpl extends BaseTypeImpl {
String mes;
public ChildBaseTypeImpl(int num, String mes) {
super(num);
this.mes = mes;
}
@Override
public void method() {
super.method();
System.out.println(mes);
}
}
@Component
@Scope(SCOPE_PROTOTYPE)
public class SecondaryTypeImpl implements BaseType {
private String str;
public SecondaryTypeImpl(String str) {
this.str = str;
}
@Override
public void method() {
System.out.println(str);
}
}
结果,我有3个类实现1个接口。
所有类都具有非默认构造函数,具有不同的参数。
有没有办法根据构造函数参数通过BaseType
接口使Spring查找正确的bean?
我想这样做:
ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
ctx.getBean(BaseType.class, 11); //Should return instance of BaseTypeImpl class object, since it has constructor taking int
或者像这样:
ctx.getBean(BaseType.class, 11, "hello!"); //Should return instance of ChildBaseTypeImpl
尝试这样做会导致异常:
线程中的异常" main" org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有 属于' springtest.BaseType'的限定bean可用:预期 单匹配bean但发现3: baseTypeImpl,childBaseTypeImpl,secondaryTypeImpl
似乎没有直接的方法来做到这一点。
但也许可以从BaseType
中获取可分配的所有类,在其中找到适当的构造函数,然后使用getBean
类作为第一个参数调用*Impl
方法?
已更新
感谢大家的回答! 正如pvpkiran所提到的,有一种方法可以通过bean名来获取bean类。 这打开了反射的大门,解决了问题,这里是示例代码:
public class App {
static ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
public static void main(String[] args) {
BaseType beanByInterface = getBeanByInterface(BaseType.class, "Hello!");
System.out.println(beanByInterface.getClass());
}
public static <T> T getBeanByInterface(Class<T> interf, Object... params) {
BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);
TypeFilter tf = new AssignableTypeFilter(interf);
s.addIncludeFilter(tf);
s.scan("springtest"); //Sample project package name
String[] beans = bdr.getBeanDefinitionNames();
for(String b : beans) {
Class<?> type = ctx.getType(b);
MAIN: for(Constructor cons : type.getConstructors()) {
if (cons.getParameterCount() == params.length) {
for (int i = 0; i < cons.getParameterCount(); i++) {
if (!params[i].getClass().equals(cons.getParameterTypes()[i])) { //Will fail comparing primitive and boxed types, just leaving like this for simplicity
continue MAIN;
}
}
return (T) ctx.getBean(type, params);
}
}
}
return null;
}
}
代码有问题,但这只是为了展示这个概念。 拥有bean类我可以获得正确的bean,它具有所需的构造函数。
此示例输出&#34;类springtest.SecondaryTypeImpl&#34;
但我相信这应该是某些工具中涉及的常见问题。 我不想发明自行车,但仍无法找到解决方案。
更新2
似乎现有的库中没有这样的解决方案,因为它不是最佳实践。 无论如何,这里有更新的方法,也许有人会发现它很有用。
public static <T> T getBeanByInterface(Class<T> interf, Object... params) {
String[] beans = ctx.getBeanNamesForType(interf);
for(String beanName : ctx.getBeanNamesForType(interf)) {
Class<?> type = ctx.getType(beanName);
Class<?>[] paramTypes = new Class[params.length];
//Getting params types
for (int i = 0; i < params.length; i++) {
paramTypes[i] = params[i].getClass();
}
if (ConstructorUtils.getMatchingAccessibleConstructor(type, paramTypes) != null) {
return (T) ctx.getBean(type, params);
}
}
return null;
}
答案 0 :(得分:0)
BeanFactory具有以下getBean()
声明
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException
正如你在这里看到的,没有关于类类型,bean名称和参数的声明(这是你正在寻找的)。
但是你可以使用bean名称和参数来获取bean。
ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
ChildBaseTypeImpl childBaseTypeImpl = (ChildBaseTypeImpl) ctx.getBean("childBaseTypeImpl", 11, "hello");
BaseTypeImpl basetypeImpl = (BaseTypeImpl) ctx.getBean("baseTypeImpl", 11, );
答案 1 :(得分:0)
嗯,你不能像@alexlys那样使用构造函数参数来区分你的bean。但是你可以用相同的结果做一些不同的事情。这两种方法都假定您可以在运行时定义不需要的bean,但是在编写代码时。
您可以创建具有唯一名称的bean,然后获取所需的bean 名称,而不是界面
您可以添加一些标记界面,例如
interface SecondaryType extends BaseType {//empty}
。每个bean只实现一个特定的接口,因此您可以使用这样的接口来实现bean。