Java 如何使用singleton类实现依赖注入

Java 如何使用singleton类实现依赖注入,java,junit,dependency-injection,Java,Junit,Dependency Injection,我正在努力学习用Java编写可测试代码。我看到很多链接说,如果我想进行单元测试,单例类不适合实现。这就是我想要解决的问题。我有一个发送HTTP请求的类(HTTPRequest类)。我想让这个类考虑使用哪个协议(TLSv1.2等)。我有多个其他类,它们只是用body、URL等调用HTTPRequest类 我的方法是HTTPRequest可以是一个单例类,因为不需要创建同一事物的多个实例。整个项目的协议将是相同的,只需设置一次 这就是我的HTTPRequest的样子 public class HTT

我正在努力学习用Java编写可测试代码。我看到很多链接说,如果我想进行单元测试,单例类不适合实现。这就是我想要解决的问题。我有一个发送HTTP请求的类(HTTPRequest类)。我想让这个类考虑使用哪个协议(TLSv1.2等)。我有多个其他类,它们只是用body、URL等调用HTTPRequest类

我的方法是HTTPRequest可以是一个单例类,因为不需要创建同一事物的多个实例。整个项目的协议将是相同的,只需设置一次

这就是我的HTTPRequest的样子

public class HTTPRequest {
    private CloseableHttpClient httpClient;
    private HttpGet httpGet;
    private HttpPut httpPut;
    private HttpPost httpPost;

    HTTPRequest(CloseableHttpClient a, HttpGet b, HttpPut c, HttpPost d) {
        httpClient = a;
        httpGet = b;
        httpPut = c;
        httpPost = d;
    }

    public HTTPResponse getRequest(url) {
        //Do some processing with httpClient & httpGet to send the request
    }
}
现在,对于需要HTTPRequest的类,有没有一种方法可以调用getRequest,但只使用一个对象?我读了一些关于依赖注入的书,但我真的不知道该怎么做


我应该如何修改代码以使其也可测试?

可以使用setter实现依赖注入的简单实现(没有注入框架):

class SomeOtherClass {

    private HTTPRequest httpRequest;
    public void setHttpRequest(HTTPRequest httpRequest){

    }

    public void someAction(){
        httpRequest.getRequest(...);
    }
}

...
SomeOtherClass someOtherClass = new SomeOtherClass(httpRequest);
在上面的示例中,
HttpRequest
(依赖项)被注入到
SomeOtherClass
。通过这种方式,单元测试能够创建
HttpRequest
的模拟,将其提供给
SomeOtherClass
并测试
SomeOtherClass
,而无需外部依赖项

这有意义吗


现在,关于singleton:使HttpRequest成为singleton会使模拟变得更加困难(使用类似Mockito的模拟框架)。另外,像其他类一样的类只能执行HttpRequest.getInstance(),所以您的测试对此无能为力。

  • 将名为
    beans.xml
    的空文件放入
    WEB-INF
    目录
  • @ApplicationScoped
    注释您的
    HttpRequest
  • 将本地字段添加到使用它的所有类中,并用
    @Inject
    注释它们

您可以在
HTTPRequest
类中删除构造函数,然后创建
受保护的
方法,称为
getHttpGet()
getHttpPut
,等等

public class HTTPRequest {

    public HTTPResponse getRequest(url) {
        //Do some processing with httpClient & httpGet to send the request
      getHttpGet(url).call();
    }

    protected CloseableHttpClient getHttpClient() {
      return new CloseableHttpClient();
    }

    protected HttpGet getHttpGet(String url) {
      return new HttpGet(url);
    }

    protected HttpPost getHttpPost(String url) {
      return new HttpPost(url);
    }

    ...
}
然后,您可以创建一个继承自
HttpRequest
HttpRequestMock
类,并重写
getHttp*
方法

public class HTTPRequestMock extends HttpRequest {

    @Override
    protected CloseableHttpClient getHttpClient() {
      //return mock
      return new CloseableHttpClientMock();
    }

    @Override
    protected HttpGet getHttpGet(String url) {
      //return mock
      return new HttpGetMock(url);
    }

    @Override
    protected HttpPost getHttpPost(String url) {
      //return mock
      return new HttpPostMock(url);
    }

    ...
}
最后,您可以在单元测试中相应地注入
HttpRequest
HttpRequestMock

class HTTPRequestClient {

    private HTTPRequest httpRequest = HttpRequestFactory.getHttpRequest();

    public void callServer(){
        httpRequest.getRequest("http://someurl.com/");
    }

    void setHttpRequest(HTTPRequest httpRequest){
        this.httpRequest = httpRequest;
     }
  }

class HTTPRequestFactory {
   private static final HttpRequest httpRequest = new HttpRequest();

   public static HTTPRequest getHttpRequest() {
     return httpRequest;
   }
}

class HTTPRequestClientTest {
     HTTPRequestClient  httpRequestClient;

       @Before
       public void setUp(){
           httpRequestClient = new HTTPRequestClient();
           httpRequestClient.setHttpRequest(new HttpRequestMock());
        }

        @Test
        ...
      }

是的,我想过这个。但是我不希望创建HttpRequest的责任由调用其他类的人来完成。在这种情况下我该如何处理呢?然后,您可以使用Spring或Guice之类的注入框架。该框架将实例化这些对象并将它们注入到您的bean中。那么在这种情况下,绑定应该在哪里进行呢?其他类将进行绑定吗?你能举个例子吗?老实说,注入框架一开始不是一个容易理解的概念,我觉得你最好先看一下教程。看看StackOverflow的文档:它显示了一个简单的工作解决方案。为什么要将
HttpGet
HttpPut
等作为构造函数中的参数传递?@alayor我想模拟HttpGet的响应。因为我不希望它实际上是我的服务器。但是你能在
getRequest
方法中传递
HttpGet
吗?@alayor是的,我可以。谢谢你的详细解释。然而,我将有多个HTTPRequestClients做各种各样的事情。我认为没有必要为每个类创建多个HTTPRequest对象。您可以创建一个
HTTPRequestFactory
。而且您只能控制一个
HTTPRequest
对象的创建。这很有意义。我会试试看。谢谢你的帮助:)