我有一个用例,在注入点基于注释参数动态实例化bean(使用某种工厂方法)会非常好。具体来说,我需要能够为创建bean的工厂指定类型参数。
一个非常相关的示例是JSON反序列化器,它需要将其反序列化为的类型。
我设想:
@Inject
@DeserializeQualifier(Car.class)
private Deserializer<Car> _carDeserializer;
@Inject
@DeserializeQualifier(Bus.class)
private Deserializer<Bus> _busDeserializer;
..或简单地,如果可以从泛型类型参数中嗅探类型:
@Inject
private Deserializer<Car> _carDeserializer;
@Inject
private Deserializer<Bus> _busDeserializer;
这里的重点是,我事先不知道项目中需要哪种类型,因为这将是许多项目所包含的通用工具。因此,您可以使用@EnableDeserializer注释@Configuration类,然后可以注入任何类型的反序列化器(制造这些反序列化器的工厂可以处理任何类型,但要创建一个类型,就需要知道反序列化对象的所需类型-普通的泛型不会削减它,因为Java并未使用标准化的泛型。)
因此,我需要能够注入到Spring上下文中,或使用其他任何Spring魔术技巧,使用某种带有类型参数的DeserializerFactory。
基本上,我需要让Spring调用基于以下方法的方法,如第一个示例中的qualifier参数(或该问题的整个DeserializeQualifier-instance),或如第二个示例中的通用类型参数:
DeserializerFactory {
<T> Deserializer<T> createDeserializer(Class<T> type) { ... }
}
答案 0 :(得分:1)
您可以创建一个BeanFactoryPostProcessor
来设置带有自定义注释的属性。我已经设置了一个小的Spring Boot项目来玩:
// Custom annotation
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectSomeClassHere {
Class value();
}
// Demo bean
@Component
public class SomeBean {
@InjectSomeClassHere(String.class)
private Class someValue;
public Class getInjectedClass() {
return someValue;
}
}
// The BeanFactoryPostProcessor
@Component
public class SomeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Arrays
.stream(beanFactory.getBeanDefinitionNames())
.filter(beanName -> hasAnnotatedField(beanFactory, beanName))
.forEach(beanName -> {
Object bean = beanFactory.getBean(beanName);
Stream.of(bean.getClass().getDeclaredFields()).forEach(field -> setFieldValue(bean, field));
});
}
private boolean hasAnnotatedField(ConfigurableListableBeanFactory beanFactory, String beanName) {
try {
String className = beanFactory.getBeanDefinition(beanName).getBeanClassName();
if (className == null) {
return false;
}
return Arrays.stream(Class.forName(className).getDeclaredFields())
.anyMatch(field -> field.isAnnotationPresent(InjectSomeClassHere.class));
} catch (ClassNotFoundException e) {
// Error handling here
return false;
}
}
private void setFieldValue(Object filteredBean, Field field) {
try {
// Note: field.isAccessible() is deprecated
if (!field.isAccessible()) {
field.setAccessible(true);
}
// Retrieve the value from the annotation and set the field
// In your case, you could call `createDeserializer(fieldValue);` and set the field using the return value.
// Note that you should change the type of `SomeBean#someValue` accordingly.
Class fieldValue = field.getAnnotation(InjectSomeClassHere.class).value();
field.set(filteredBean, fieldValue);
} catch (IllegalAccessException e) {
// Error handling here
e.printStackTrace();
}
}
}
// A small test to verify the outcome of the BeanFactoryPostProcessor
@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeBeanTests {
@Autowired
private SomeBean someBean;
@Test
public void getInjectedClass_shouldHaveStringClassInjected() {
Assert.assertEquals(String.class, someBean.getInjectedClass());
}
}
请注意,这是一个非常幼稚的实现,需要进行进一步的微调。例如,它会扫描所有弹簧组件中的 all 属性以查看是否存在注释。
祝您的项目好运!