我正在使用 Spring-Boot,使用类名字符串动态初始化一个类并获取返回值

Posted

技术标签:

【中文标题】我正在使用 Spring-Boot,使用类名字符串动态初始化一个类并获取返回值【英文标题】:I am using Spring-Boot, initialize a class dynamically using string of class name and get the returned values 【发布时间】:2019-08-02 10:44:47 【问题描述】:

我在 Spring Boot 中工作,在 MyService class 中,我得到了一个作为 String 的类名,我想初始化该对象并取回返回值。

但是我对它没有更多的想法,我认为它是通过依赖注入实现的。但是,如何?

假设我有课程A.java, B.java, C.java 和服务课程MyService.java

@Component
public class A
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello A"+" good morning";



@Component
public class B
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello B"+" good morning";



@Component
public class C
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello C"+" good morning";



@Service
public class MyService
   // i get here A class name as String like,
   String classNameString = "A"; // or "B", or "C"
   int timrHr =  new Date().getHours();
   //how to here i will initialize above class and get method wist(param) returned wish msg.
   //like, a.wish(timeHr); and it gives "Hello A good morning"

我希望wish()返回的输出。

谁能建议我如何实现它?

谢谢。

【问题讨论】:

为什么不在MyService 中使用@Autowire?喜欢@Autowire A a; @arnonuem 但是当我们有类名的字符串时,我们如何决定自动装配,例如`String classNameString = "A" or "B" or "C"`。感谢回复。 可能会有所帮助***.com/questions/5725222/… 【参考方案1】:

我可以在这里想到两种方法。第一种方法是命名类(在@Component 之后)并使用上下文,尝试获取 bean。您还可以使用 @Qualifier 命名 bean

@Component("A")
public class A
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello A"+" good morning";



@Component("B")
public class B
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello B"+" good morning";



@Component("C")
public class C
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello C"+" good morning";




@Service
public class MyService
   @Autowired
   private ApplicationContext context;

  void myMethod() 
    A a = (A) context.getBean("A");


     System.out.println(a.wish(123));
    
    

第二种方法是让你所有的wisher类都实现一个接口,并遍历这个接口的所有实现bean并找到正确的bean

@Component
public class A implements Wisher
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello A"+" good morning";



@Component
public class B implements Wisher
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello B"+" good morning";



@Component
public class C implements Wisher
    public String wish(int timeHr)
       //some logic of time based wish
    return "Hello C"+" good morning";



public interface Wisher public String wish(int time); 

@Service
public class MyService
   @Autowired
   private List<Wisher> wishers;

void myMethod() 
    String requiredClassName = "A";
    Wisher requiredWisher = null;
    for (int i = 0; i < wishers.length(); i++) 
        Wisher w = wishers.get(i);
        if (w.getClass().getName().equals(requiredClassName)) 
            requiredWisher = w;
            break;
        

        System.out.println(w.wish(123));
    




    

【讨论】:

感谢您的第一种方式对我有用。但我有一个疑问,在服务类中使用 Autowired ApplicationContext 是一个好习惯吗?它有什么缺点吗? @programmer420 我没有看到任何使用它的建议/反对意见。我们在生产代码本身中使用它。您可以尝试使用 ***.com/questions/4914012/… 中所述的 ApplicationContextAware 接口【参考方案2】:

String 不是 Spring IoC 框架可管理的 bean,但类 ABC 的实例是您的 bean。

你应该做的是声明类ABC作为公共接口的实现,并通过这种类型注入对应的bean:

interface Wisher 
   String wish(int timeHr);


@Component
public class A implements Wisher 
  public String wish(int timeHr)
     //some logic of time based wish
    return "Hello A"+" good morning";
  


@Component
public class B implements Wisher 
  public String wish(int timeHr)
     //some logic of time based wish
  return "Hello B"+" good morning";
  


@Component
public class C implements Wisher 
  public String wish(int timeHr)
     //some logic of time based wish
  return "Hello C"+" good morning";
   


@Service
public class MyService
   @Autowired
   private Wisher a; // instance of "A" or "B", or "C"

   void myMethod() 
        int timrHr =  new Date().getHours();
        wisher.wish(timrHr);
    
 

【讨论】:

感谢在此回复,但我们这里以 A、B 或 C 为例。当我有组件类的字符串时,例如 String s =“A”或“B”或“C”。 你可以将你的字符串声明为 bean:@Bean String stringA() return "Hello A"+" good morning"; 在一个用 @Configuration 注释的类中并注入它们,但这有什么意义吗?))) 我从对控制器和服务的请求中获取类名作为字符串,它必须在这里初始化这个类并返回特定的wish()。 MyService 中有一个Map&lt;String, Wisher&gt; wishers,其中名称映射到ABC 怎么样?当您收到带有名称的请求时,您可以从地图中检索相应的Wisher 并调用wisher.wish() @star67 ,您的 Wisher 界面自动装配将不起作用。 Spring 不会理解 A 或 B 或 C 中的哪个 bean 到 Autowire。在这种情况下,您必须使用 Qualifier 注释。请在下面查看我的答案。【参考方案3】:

考虑到您使用 Spring 进行实现,所有 bean 都将是 Singleton,并且这些 bean 将在 App 启动时初始化(加载 ApplicationContext 时),然后应用程序无法检查要注入的实现。

因此,您无法在运行时使用依赖注入有条件地注入 bean

相反,您可以使用以下其他设计 -

@Service
public class MyService

  private Wisher wisher;

  public Wisher setupWisher(String class)

   if (class.equals("A")) 
        wisher = new A();
    else if(class.equals("B"))
        wisher = new B();
    else if(class.equals("C"))
        wisher = new C();
    
 

 void myMethod(String requestString) 
    int timrHr =  new Date().getHours();
    setupWisher(requestString).wish(timrHr);       
 


【讨论】:

感谢您的回答是个好主意。我有一个问题,当我们与 ApplicationContext 一起去这里时有什么缺点(Praveen E 的回答以上)? 没有这样的缺点.. 现在开发人员通过 autowired 使用 applicationcontext,如下所示。这一切都归结为您认为应该选择实施的设计。我会更喜欢我建议的那个,因为我不希望 A、B、C 成为 applicationcontext 中的 spring bean,因为它没有做任何额外的事情来获得 spring bean 的资格

以上是关于我正在使用 Spring-Boot,使用类名字符串动态初始化一个类并获取返回值的主要内容,如果未能解决你的问题,请参考以下文章

Spring-boot 使用环境变量使用破折号/连字符配置属性

如何使用 Selenium 和 VBA 识别包含特殊字符的元素类名

“类名必须是有效的对象或字符串”在没有 Composer 的情况下安装 PHPSecLib

使用 Spring-Boot 2.1 从 .yml 读取对象列表 [重复]

Spring-boot & hibernate,使用事务

使用spring-boot在多个数据库之间进行数据迁移