Java 何时使用依赖项注入在Spring中的控制器调用的服务中初始化映射?
我有一个控制器类,它首先在我的应用程序中被调用。在那里,我计划从服务类的映射中检索一个值 这是控制器:Java 何时使用依赖项注入在Spring中的控制器调用的服务中初始化映射?,java,spring,hashmap,autowired,Java,Spring,Hashmap,Autowired,我有一个控制器类,它首先在我的应用程序中被调用。在那里,我计划从服务类的映射中检索一个值 这是控制器: @Controller public class AppController { public Service doSomethingWithTheMap(String key) { return ServiceImpl.getMapValueFor(key).exec(); } } 我会遇到问题,因为在初始化期间,更准确地说,在将值放入服务映射的过程中,我
@Controller
public class AppController {
public Service doSomethingWithTheMap(String key) {
return ServiceImpl.getMapValueFor(key).exec();
}
}
我会遇到问题,因为在初始化期间,更准确地说,在将值放入服务映射的过程中,我需要BeanFactory,因为映射中的值是服务实现。
在静态块中执行此操作将导致BeanFactory为null,因为我猜它尚未被注入
因此,以这个initMap()调用结束让我感觉有点像。。。应该有更好的解决办法
有人暗示吗
我必须承认,我是春天的新手,也许我把这里搞得一团糟。仅供参考,在根据字符串输入进行了无休止的if-else检查以决定调用哪个服务之后,映射出现在我的脑海中。因此,我将其替换为地图和控制器中的简单一行
ServiceImpl.getMapValueFor(key).exec();
以下是服务类别:
@Service
public class ServiceImpl {
private static Map<String, Service> map;
private static ApplicationContext context;
@Autowired
public void setApplicationContext(ApplicationContext factory) {
this.context = factory;
}
public static Service getMapValueFor(String key) {
if (map == null) {
initMap();
}
return map.get(key);
}
private static void initMap() {
/*
* FIXME: We can not init the map in a static block or directly
* initialize it since the factory is not injected until execution of a
* static block and will be null.
*/
BeanFactory factory = context;
map = new HashMap<String, Service>();
map.put("key", factory.getBean(SomeService.class));
}
}
@服务
公共类ServiceImpl{
私有静态地图;
私有静态应用上下文上下文;
@自动连线
public void setApplicationContext(ApplicationContext工厂){
this.context=工厂;
}
公共静态服务getMapValueFor(字符串键){
if(map==null){
initMap();
}
返回map.get(key);
}
私有静态void initMap(){
/*
*FIXME:我们不能在静态块中初始化映射,也不能直接初始化映射
*初始化它,因为在执行
*静态块和将为空。
*/
BeanFactory工厂=上下文;
map=新的HashMap();
map.put(“key”,factory.getBean(SomeService.class));
}
}
我想说的第一件事是您有一个bug,因为您使用的是没有同步的HashMap!-不要担心,许多(如果不是大多数的话)java开发人员也会犯同样的错误
除非您将代码过于简化,否则您的服务看起来更像是命令而不是服务;一个服务就是一个单身汉。服务拥有没有参数的方法并非不可能,但我认为这是不常见的。您确定不应该使用原型bean而不是单例吗
通常,服务的数量是有限的,如果您有多个相同类型的服务,则在自动连接它们时将使用@Qualifier。在任何情况下,我觉得这段代码都不可靠,所以也许你应该试着从更高的层次解释这个问题,因为可能有比当前代码路径更好的方法。您的服务类ServiceImpl必须实现接口
org.springframework.context.ApplicationContextAware
,以获取Spring的应用程序上下文实例。这里是一个非常基本的解决方案,它使用的事实是@Bean的名称是创建它的方法的名称,您可能需要更好的策略。其思想是将getBean隐藏在一个提供者类中,然后该类可以自动连接(并测试)
看看:谢谢@Dave。我懂了。看起来我的问题真的是初始化顺序。现在尝试使用构造函数方式初始化(或连接)包含映射的类,但仍然是空指针,因为在放入映射期间上下文/工厂不存在。您的措辞“command”完全正确。我只是为了演示而简化了代码。我在这里称之为服务,因为我谈到了spring的服务和组件注释。然而,我真正想做的是根据控制器中接收到的字符串值调用一个类。我将有大约200对字符串到类的关系。那么ApplicationContextAware上的注释就是您所需要的,请给我10分钟时间制作一个示例。您从哪里获得密钥,它是用户输入的一部分还是?您建议改为什么?在配置类中定义bean,比如maybe?密钥完全来自用户。谢谢。我不知道。我想我在这里把事情复杂化了,现在完全在使用spring。@init5,spring将调用你的setApplicationContext,只有当你的类实现了
ApplicationContextAware
。尝试一下,如果您遇到任何其他问题,请告诉我。在initMap()期间仍然保持NPE。检查provider/simplebean方法另一个观察结果是,如果您发现自己在Springbean中编写静态方法,那么您通常会犯错误。静态应该用于实用方法、常量引用和不可变对象。好的,我明白你的意思了。因此,基本上,我将使用Bean注释定义所有命令,并让提供程序实现ApplicationContextAware以使连接正常工作。我会检查一下,然后告诉你。当然,我必须在某个地方初始化映射,因为我的用户输入与bean名称不同。如果您计划在控制器中使用映射,它将被多个线程访问。通常,您会使用ConcurrentHashMap进行此操作,因为它是线程安全的,如果您使用HashMap,则必须同步初始化和每次访问。太好了,它成功了。还有地图。您使用bean名称和方法相等的想法让我想到了。我删除了config类中的映射和bean定义。相反,当Spring使用@Component(“A”)
扫描bean时,我直接命名bean。Spring告诉我们:创建单例bean“A”的共享实例。之后,我可以直接调用控制器中的ctx.getBean(“A”,Command.class).exec()
。你怎么想?还是我误用了注释?使用@Component命名很好,但是它要求您的组件是线程安全的,因为默认情况下组件/服务/存储库是Signleton,所以如果两个用户同时想要执行命令A,他们将在同一个实例上调用execute,在这种情况下,必须以threadsaft方式访问中的字段。
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
CallableProvider provider = ctx.getBean(CallableProvider.class);
System.out.println(provider.getCommand("aCommand").call());
System.out.println(provider.getCommand("bCommand").call());
}
public static class Config {
@Bean
public ACommand aCommand() {
return new ACommand();
}
@Bean
public BCommand bCommand() {
return new BCommand();
}
@Bean
public CallableProvider callableProvider() {
return new CallableProvider();
}
}
public static class CallableProvider implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public Command getCommand(String name) {
return context.getBean(name, Command.class);
}
}
public static class ACommand implements Command {
// autowire stuff
@Override
public String call() {
return "A";
}
}
public static class BCommand implements Command {
// autowire stuff
@Override
public String call() {
return "B";
}
}
public interface Command {
String call();
}
}