📑
scrapbook
  • Introduction
  • 1. Concept
  • 1.1 EAI
  • 1.2 ESB
  • 1.3 EAI v.s. ESB
  • 1.4 SOA
  • 1.5 RESTful
  • 1.6 Microservices
  • 1.7 Microservice v.s. SOA
  • 1.8 Maintain HTTP State
  • 1.8.1 Cookie
  • 1.8.2 Session
  • 1.9 Put, Add, and Set
  • 1.10 Public Key & Private Key
  • 1.11 Message Digest & Hash Function
  • 1.12 Diffie - Hellman Key Exchange
  • 1.13 About IoC
  • 1.14 About AOP
  • 1.15 Spring - Pros and Cons
  • 1.16 Spring - Prototype in Singleton
  • 1.17 HTTP v.s. SPDY
  • 1.18 HTTP/2
  • 1.19 Securing REST Services
  • 1.20 Conway's Law
  • 1.21 大型網站架構演化發展歷程
  • 1.22 Java Generics
  • 1.23 MySQL HA經驗談
  • 2. Questions & Solutions
  • 2.1 海底撈幾根針
  • 2.2 塞不進去
  • 3. Relational Database
  • 3.1 SQL Join Type
  • 3.2 SQL Injection
  • 3.3 MySQL CHAR v.s. VARCHAR
  • 4. NoSQL
  • 4.1 CAP Theorem, ACID v.s. BASE
  • 4.2 Two-Phase-Commit
  • 4.3 RDB v.s. NoSQL
  • 4.4 Structured, Unstructured and Semi-structured Data
  • 4.5 Shard v.s. Replica
  • 4.6 ArrayList v.s. LinkedList
  • 4.7 HashSet v.s. TreeSet
  • 4.8 HashMap v.s. TreeMap
  • 4.9 ArrayList v.s. Vector
  • 4.10 HashMap v.s. HashTable
  • 4.11 Statement, PreparedStatement and CallableStatement
  • 4.12 Overflow of Digits
  • X. JVM
  • X.1 JVM System Threads
  • X.2 Garbage Collection
Powered by GitBook
On this page

Was this helpful?

1.16 Spring - Prototype in Singleton

問題: 若今天有個@Autowired bean (此稱bean A)存在於某個Singleton bean (此稱bean B)中, 且bean A必須為prototype bean, 請問該如何實現此種配置呢?

思路: 這裡可以考慮以下情境, 假設有個singleton bean (bean B), 此bean的角色是個controller, 然後在bean B中注入一個bean A (@Autowired), 而我們希望每次有request進到這個controller的時候, 這個bean A都會是一個新的bean (即prototype bean).

以下先定義Request:

/**
 * @author Carl Lu
 */
public class Request {

    Long id;
    String content;

    public static Request getInstance(Long id, String content) {
        Request request = new Request();
        request.setId(id);
        request.setContent(content);
        return request;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

再來是要被設定成prototype的bean A, 此處為validator:

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

/**
 * @author Carl Lu
 */
@Component
@Scope(value = "prototype")
public class Validator {

    private List<String> validationMessages = new ArrayList<>();

    public Validator() {}

    public List<String> validate(Request request) {
        if (request.getContent() == null) {
            validationMessages.add("Request: " + request.getId() + " failed (no content).");
        }
        return validationMessages;
    }

}

然後是身為controller的singleton bean (bean B)

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author Carl Lu
 */
@Component
public class RequestHandler {

    @Autowired
    private Validator validator;

    public void handle(Request request) {
        List<String> validationMsgs = validator.validate(request);
        if (!validationMsgs.isEmpty()) {
            validationMsgs.stream().forEach(msg -> System.out.println(msg));
        } else {
            System.out.println("Validation passed for request: " + request.getId());
        }
        System.out.println("Validator: " + validator.toString());
    }

}

-> 就只是一個要處理request的class而已, 不過其中會注入bean A (prototype scope)

寫個test case來驗證看看:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.rga.RgaCustomerModuleApplication;

/**
 * @author Carl Lu
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = RgaCustomerModuleApplication.class)
public class RequestHandlerTest {

    @Autowired
    private RequestHandler requestHandler;

    @Test
    public void testPrototype() {
        Request req1 = Request.getInstance(1l, "R1");
        Request req2 = Request.getInstance(2l, "R2");
        Request req3 = Request.getInstance(3l, null);
        Request req4 = Request.getInstance(4l, "R4");
        Request req5 = Request.getInstance(5l, null);

        requestHandler.handle(req1);
        requestHandler.handle(req2);
        requestHandler.handle(req3);
        requestHandler.handle(req4);
        requestHandler.handle(req5);
    }

}

所以現在來修改一下, 以下是改良過後的validator:

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

/**
 * @author Carl Lu
 */
@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public class Validator {

    private List<String> validationMessages = new ArrayList<>();

    public Validator() {}

    public List<String> validate(Request request) {
        if (request.getContent() == null) {
            validationMessages.add("Request: " + request.getId() + " failed (no content).");
        }
        return validationMessages;
    }

}

-> 我後來查了一些資料, 我想這個解法是我覺得比較好看的, 首先要include cglib, 這邊的原理是利用AOP scoped proxies在每次RequestHandler被呼叫的時候都注入一個新的validator bean

Previous1.15 Spring - Pros and ConsNext1.17 HTTP v.s. SPDY

Last updated 5 years ago

Was this helpful?

結果:-> 看起來光是把Validator設定成prototype scope是沒有用的, 因為都是同一個instance. 原因我想應該是因為在create RequestHandler的時候, container已經把RequestHandler默認為一個singleton bean了, 所以它也只會把validator bean注入一次並且重用validator bean.

重新測試:-> 這樣看起來是有達到效果了, 每個validator看來都是一個新的instance.