Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/390.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 何时使用依赖项注入在Spring中的控制器调用的服务中初始化映射?_Java_Spring_Hashmap_Autowired - Fatal编程技术网

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();
    }
}