Java 使用依赖项注入创建的动态类型化对象
我有一段使用反射的现有代码,但是如果可能的话,我想开始使用依赖项注入和Guice创建对象 以下是它目前的工作原理:Java 使用依赖项注入创建的动态类型化对象,java,reflection,guice,Java,Reflection,Guice,我有一段使用反射的现有代码,但是如果可能的话,我想开始使用依赖项注入和Guice创建对象 以下是它目前的工作原理: 已加载配置(.properties)文件,文件中的字符串如下 objects=Foo,^ab..$;巴,卑诗省^元;Baz,i* 注意:Foo、Bar和Baz是实现MyInterface 每对都有一个与之配对的正则表达式 输入数据从另一个来源输入。假设本例中的数据为: String[]{“abab”、“abcd”、“dbca”、“fghi”、“jklm”} 然后,我想创建G
.properties
)文件,文件中的字符串如下
objects=Foo,^ab..$;巴,卑诗省^元;Baz,i*
- 注意:
、Foo
和Bar
是实现Baz
MyInterface
- 每对都有一个与之配对的正则表达式
String[]{“abab”、“abcd”、“dbca”、“fghi”、“jklm”}
Foo
、Bar
和Baz
的新实例。
- 在这种情况下,创建的实例将是:
newfoo(“abab”)代码>
newfoo(“abcd”)代码>
newbar(“abcd”)代码>
新条形图(“dbca”)代码>
newbaz(“fghi”)代码>
不会创建任何新实例,因为它没有匹配的模式“jklm”
public class MyInterfaceBuilder {
private Classloader tcl = Thread.currentThread().getContextClassLoader();
private Pattern p;
private Class<? extends MyInterface> klass;
public InterfaceBuilder(String className, String pattern) {
this.pattern = Pattern.compile(pattern);
this.klass = makeClass(className);
}
private static Class<? extends Interface> makeClass(String className) {
String fullClassName = classPrefix + className;
Class<?> myClass;
try {
myClass = tcl.loadClass(fullClassName);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Class not found: " + fullClassName, e);
}
if(MyInterface.class.isAssignableFrom(myClass)) {
return (Class<? extends MyInterface>) myClass;
} else {
throw new IllegalArgumentException(fullClassName + " is not a MyInterface!");
}
}
public MyInterface makeInstance(String type) {
if (pattern == null || pattern.matcher(type).find()) {
MyInterface newInstance = null;
try {
newInstance = klass.getConstructor(String.class).newInstance(type);
} catch (Exception e) {
// Handle exceptions
}
return newInstance;
} else {
return null;
}
}
}
公共类MyInterfaceBuilder{
私有类加载器tcl=Thread.currentThread().getContextClassLoader();
私有模式p;
私有类myClass;
试一试{
myClass=tcl.loadClass(fullClassName);
}catch(classnotfounde异常){
抛出新的IllegalArgumentException(“未找到类:“+fullClassName,e”);
}
if(MyInterface.class.isAssignableFrom(myClass)){
return(Class我敢肯定,如果没有任何反射,并且只使用Guice,您就无法做到这一点。这是因为Guice不是为这些事情而设计的。Guice的任务是帮助进行依赖关系管理,而不是使用不同的创建对象的策略(在某种程度上是这样的,但没有您需要的那么多)
但是,如果您需要使用使用文件中的信息创建的对象作为其他对象的依赖项,您可以这样做。只需将对象预加载到某种映射中,我想这样做可以:
Map<String, MyInterface> myInterfaceMap;
// You fill it with pairs "abcd" -> new Foo("abcd"), "abab" -> new Foo("abab") etc
在此之后,可以按如下方式注入这些对象:
class SomeOtherClass {
// previous 'new Foo("abcd")' object will be injected here
@Inject
SomeOtherClass(@Named("abcd") MyInterface interface) {
// whatever
}
}
如果您的字符串键集是动态的,那么您可能希望在运行时将这些对象作为集合进行检查。在这种情况下,您可以像往常一样绑定它:
bind(new TypeLiteral<Map<String, MyInterface>>() {}).toInstance(myInterfaceMap);
这是可能的最简单解决方案,但它要求通过方法而不是构造函数提供对象的所有依赖项
如果必须通过构造函数提供依赖项,则情况会变得更复杂。您必须手动为类编写工厂,并将其与Guice集成。工厂可以如下所示:
Map<String, MyInterface> myInterfaceMap = ...;
Injector injector = ...; // create the injector
for (MyInterface myInterface : myInterfaceMap.values()) {
injector.injectMembers(myInterface);
}
public interface MyInterfaceFactory {
MyInterface create(String name);
}
public class ReflectiveFromFileMyInterfaceFactory implements MyInterfaceFactory {
// You have to inject providers for all dependencies you classes need
private final Provider<Dependency1> provider1;
private final Provider<Dependency2> provider2;
private final Provider<Dependency3> provider3;
@Inject
ReflectiveFromFileMyInterfaceFactory(Provider<Dependency1> provider1,
Provider<Dependency2> provider2,
Provider<Dependency3> provider3) {
this.provider1 = provider1;
this.provider2 = provider2;
this.provider3 = provider3;
}
@Override
public MyInterface create(String name) {
// Here you query the file and create an instance of your classes
// reflectively using the information from file and using providers
// to get required dependencies
// You can inject the information from file in this factory too,
// I have omitted it for simplicity
}
}
然后像往常一样注射
然而,这种方法要求您提前知道类具有哪些依赖关系
如果您事先不知道您的类具有哪些依赖项,那么我认为您可以使用这些功能实现您想要的功能,但在您的情况下,这很快就会变得很难处理。但是如果您使用私有模块,您可能不需要使用反射。进一步思考后,我开始怀疑我是否应该不太关心将运行时参数传递给构造函数,而更关心使用以下示例没有错误检查,但是实际的实现版本会抛出大量的NullPointerException
s和IllegalArgumentException
s来检查坏数据。但这里的想法是:
基本上是这样的:
// This could be done a number of different ways
public static void main() {
Injector inj = Guice.createInjector(new MyOuterModule());
Injector child = inj.createChildInjector(new MyPluginModule(/* interfaceFileName? */));
MyApp app = child.getInstance(MyApp.class);
app.run();
}
public class MyPluginModule extends AbstractModule {
@Override
protected void configure() {
MapBinder<String, MyInterface> mapBinder
= newMapBinder(binder(), String.class, MyInterface.class);
// These could probably be read from a file with reflection
mapBinder.addBinding("Foo").to(Foo.class);
mapBinder.addBinding("Bar").to(Bar.class);
}
}
public class InterfaceFactory {
private Pattern p;
@Inject private Map<Provider<MyInterface>> providerMap;
private Provider<MyInterface> selectedProvider;
public void configure(String type, String pattern) {
p = Pattern.compile(pattern);
selectedProvider = providerMap.get(type);
}
public MyInterface create(String data) {
if(pattern.matcher(data).find()) {
MyInterface intf = selectedProvider.get();
intf.configure(data);
}
}
}
//这可以通过多种不同的方式实现
公共静态void main(){
inj=Guice.createInjector(新模块());
Injector child=inj.createChildInjector(新的MyPluginModule(/*interfaceFileName?*/);
MyApp-app=child.getInstance(MyApp.class);
app.run();
}
公共类MyPluginModule扩展了AbstractModule{
@凌驾
受保护的void configure(){
MapBinder MapBinder
=newMapBinder(binder(),String.class,MyInterface.class);
//这些可能是从带有反射的文件中读取的
mapBinder.addBinding(“Foo”).to(Foo.class);
mapBinder.addBinding(“Bar”)到(Bar.class);
}
}
公共类接口{
私有模式p;
@注入私有映射providerMap;
私人提供者选择提供者;
公共void配置(字符串类型、字符串模式){
p=模式。编译(模式);
selectedProvider=providerMap.get(类型);
}
公共MyInterface创建(字符串数据){
if(pattern.matcher(data.find()){
MyInterface intf=selectedProvider.get();
配置(数据);
}
}
}
这看起来比我现在的干净多了
优点:
使用Guice创建对象
反射被最小化和划分
我不需要任何依赖性知识
缺点:
我必须编写我的类,以便能够知道如果它们是在没有配置的情况下创建的,该怎么办
我需要能够在添加插件绑定之前读取配置文件,或者在代码中定义它们
我正在添加另一个答案,因为第一个答案已经太大了
我使用multibinder和专用模块似乎能够实现您所需的功能
首先,这些链接帮助了我:
基本思想如下。首先,我们创建从名称到类的映射。无论如何,这应该通过手动反射来完成,因为您的类名是由配置文件中的字符串定义的,但Guice需要class
对象(至少)来建立绑定
接下来,我们迭代这个映射,并针对每个对应的name->class
ins
public interface MyInterfaceFactory {
MyInterface create(String name);
}
public class ReflectiveFromFileMyInterfaceFactory implements MyInterfaceFactory {
// You have to inject providers for all dependencies you classes need
private final Provider<Dependency1> provider1;
private final Provider<Dependency2> provider2;
private final Provider<Dependency3> provider3;
@Inject
ReflectiveFromFileMyInterfaceFactory(Provider<Dependency1> provider1,
Provider<Dependency2> provider2,
Provider<Dependency3> provider3) {
this.provider1 = provider1;
this.provider2 = provider2;
this.provider3 = provider3;
}
@Override
public MyInterface create(String name) {
// Here you query the file and create an instance of your classes
// reflectively using the information from file and using providers
// to get required dependencies
// You can inject the information from file in this factory too,
// I have omitted it for simplicity
}
}
bind(MyInterfaceFactory.class).to(ReflectiveFromFileMyInterfaceFactory.class);
// This could be done a number of different ways
public static void main() {
Injector inj = Guice.createInjector(new MyOuterModule());
Injector child = inj.createChildInjector(new MyPluginModule(/* interfaceFileName? */));
MyApp app = child.getInstance(MyApp.class);
app.run();
}
public class MyPluginModule extends AbstractModule {
@Override
protected void configure() {
MapBinder<String, MyInterface> mapBinder
= newMapBinder(binder(), String.class, MyInterface.class);
// These could probably be read from a file with reflection
mapBinder.addBinding("Foo").to(Foo.class);
mapBinder.addBinding("Bar").to(Bar.class);
}
}
public class InterfaceFactory {
private Pattern p;
@Inject private Map<Provider<MyInterface>> providerMap;
private Provider<MyInterface> selectedProvider;
public void configure(String type, String pattern) {
p = Pattern.compile(pattern);
selectedProvider = providerMap.get(type);
}
public MyInterface create(String data) {
if(pattern.matcher(data).find()) {
MyInterface intf = selectedProvider.get();
intf.configure(data);
}
}
}