知識庫 / Spring RSS 訂閱

Spring 4.3 新功能介紹

Spring
HongKong
6
02:49 PM · Dec 06 ,2025

1. 概述

Spring 4.3 發佈版本對核心容器、緩存、JMS、Web MVC 和測試子模塊等進行了優化改進。

在本篇博文中,我們將討論其中一些改進,包括:

  • 隱式構造函數注入
  • Java 8 默認接口方法支持
  • 依賴解析改進
  • 緩存抽象優化
  • 組合的 @RequestMapping 變體
  • @Requestscope、@Sessionscope、@Applicationscope 註解
  • @RequestAttribute@SessionAttribute 註解
  • 對庫/應用服務器版本支持
  • InjectionPoint

2. 隱式構造器注入

請考慮以下服務類:

@Service
public class FooService {

    private final FooRepository repository;

    @Autowired
    public FooService(FooRepository repository) {
        this.repository = repository
    }
}

這是一個相當常見的用例,但如果忘記在構造函數上添加 @Autowired 註解,容器將嘗試查找默認構造函數,除非您明確進行依賴注入。

從 4.3 版本開始,在單構造函數場景中,您不再需要顯式指定注入註解。這對於不攜帶任何註解的類尤其優雅:

public class FooService {

    private final FooRepository repository;

    public FooService(FooRepository repository) {
        this.repository = repository
    }
}

在 Spring 4.2 及以下版本中,對於此 Bean 的以下配置將不起作用,因為 Spring 將無法找到 FooService 的默認構造函數。Spring 4.3 更智能,它會自動注入構造函數:

<beans>
    <bean class="com.baeldung.spring43.ctor.FooRepository" />
    <bean class="com.baeldung.spring43.ctor.FooService" />
</beans>

同樣,你可能注意到,@Configuration 類歷史上不支持構造函數注入。從 4.3 版本開始,它們支持了構造函數注入,並且在單構造函數場景中,它們也允許省略 @Autowired

@Configuration
public class FooConfiguration {

    private final FooRepository repository;

    public FooConfiguration(FooRepository repository) {
        this.repository = repository;
    }

    @Bean
    public FooService fooService() {
        return new FooService(this.repository);
    }
}

3. Java 8 默認接口方法支持

在 Spring 4.3 之前,默認接口方法未得到支持。

這主要是因為即使 JDK 的 JavaBean 反射器也無法將默認接口方法識別為訪問器。自 Spring 4.3 版本起,Spring 能夠識別作為默認接口方法實現的 getter 和 setter,從而允許將其用於實例的預處理,例如在以下示例中:

public interface IDateHolder {

    void setLocalDate(LocalDate localDate);

    LocalDate getLocalDate();

    default void setStringDate(String stringDate) {
        setLocalDate(LocalDate.parse(stringDate, 
          DateTimeFormatter.ofPattern("dd.MM.yyyy")));
    }

}

這個 Bean 現在可能已經注入了 stringDate 屬性:

<bean id="dateHolder" 
  class="com.baeldung.spring43.defaultmethods.DateHolder">
    <property name="stringDate" value="15.10.1982"/>
</bean>

同樣適用於使用測試註解,例如在默認接口方法上使用 @BeforeTransaction@AfterTransaction。JUnit 5 已經支持在默認接口方法上使用測試註解,Spring 4.3 緊隨其後。現在,您可以將常見的測試邏輯抽象到一個接口中,並在測試類中實現它。這是一個用於測試用例的接口,它會在測試中的事務前後記錄消息:

public interface ITransactionalTest {

    Logger log = LoggerFactory.getLogger(ITransactionalTest.class);

    @BeforeTransaction
    default void beforeTransaction() {
        log.info("Before opening transaction");
    }

    @AfterTransaction
    default void afterTransaction() {
        log.info("After closing transaction");
    }

}

另一個改進是關於註解 @BeforeTransaction, @AfterTransaction@Transactional 的,即取消了註解方法必須是 public 的要求——現在它們可以具有任何可見性級別。

4. 依賴解析改進

最新版本還引入了 ObjectProvider,它是現有 ObjectFactory 接口的擴展,並提供了方便的簽名,例如 getIfAvailablegetIfUnique,用於僅在對象存在或確定唯一候選對象時檢索 Bean(特別是,在多個匹配 Bean 的情況下,優先選擇主 Bean)。

@Service
public class FooService {

    private final FooRepository repository;

    public FooService(ObjectProvider<FooRepository> repositoryProvider) {
        this.repository = repositoryProvider.getIfUnique();
    }
}

您可以使用這樣的 ObjectProvider 在初始化過程中進行自定義解析,如上所示,或者將句柄存儲在字段中,以便在需要時按需進行解析(通常與 ObjectFactory 類似)。

5. 緩存抽象優化

緩存抽象主要用於緩存 CPU 和 IO 消耗的值。 在特定用例中,相同的鍵可能會由多個線程(即客户端)並行請求,尤其是在啓動時。 同步緩存支持是長期 richiest 的功能,現在已被實現。 假設如下:

@Service
public class FooService {

    @Cacheable(cacheNames = "foos", sync = true)
    public Foo getFoo(String id) { ... }

}

請注意 sync = true 屬性,它指示框架在值計算過程中阻止任何併發線程。這確保了在併發訪問的情況下,這個耗時操作僅在一次執行。

Spring 4.3 還改進了緩存抽象,如下所示:

  • SpEL 表達式現在可以在與緩存相關的註解中引用 Bean(例如:@beanName.method())。
  • ConcurrentMapCacheManagerConcurrentMapCache 現在通過新的 storeByValue 屬性支持緩存條目的序列化。
  • @Cacheable@CacheEvict@CachePut@Caching 現在可以作為元註解,用於創建具有屬性覆蓋的自定義組合註解。

6. 組合 @RequestMapping 變體

Spring Framework 4.3 引入了 @RequestMapping 註解的組合變體,這些變體可以簡化對常用 HTTP 方法的映射,並更好地表達註解的處理器方法的語義。

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

例如,@GetMapping 是説 @RequestMapping(method = RequestMethod.GET) 的簡短形式。 下面的示例展示了一個使用組合 @GetMapping 註解簡化後的 MVC 控制器。

@Controller
@RequestMapping("/appointments")
public class AppointmentsController {

    private final AppointmentBook appointmentBook;

    @Autowired
    public AppointmentsController(AppointmentBook appointmentBook) {
        this.appointmentBook = appointmentBook;
    }

    @GetMapping
    public Map<String, Appointment> get() {
        return appointmentBook.getAppointmentsForToday();
    }
}

7. `<em @RequestScope>, <em @SessionScope>, <em @ApplicationScope> 註解

當使用註解驅動的組件或 Java Config 時,<em @RequestScope</em>>, <em @SessionScope</em>><em @ApplicationScope</em>> 註解可用於將組件分配到所需的範圍。這些註解不僅設置了 Bean 的範圍,還設置了 scoped proxy 模式為 <em ScopedProxyMode.TARGET_CLASS</em>>

<em ScopedProxyMode.TARGET_CLASS</em>> 模式意味着 CGLIB 代理將被用於該 Bean 的代理,並確保它可以被注入到任何其他 Bean 中,即使具有更廣泛的範圍。<em ScopedProxyMode.TARGET_CLASS</em>> 模式允許代理不僅用於接口,還用於類。

@RequestScope
@Component
public class LoginAction {
    // ...
}
@SessionScope
@Component
public class UserPreferences {
    // ...
}
@ApplicationScope
@Component
public class AppPreferences {
    // ...
}

8. <em @RequestAttribute</em>><em @SessionAttribute</em>> 註解

兩個註解用於將 HTTP 請求的參數注入到 <em Controller</em>> 方法中,即 <em @RequestAttribute</em>><em @SessionAttribute</em>>. 它們允許您訪問全局管理的一些預先存在的屬性(即不在 <em Controller</em>> 內部管理的屬性)。 這些屬性的值可以由註冊的 <em javax.servlet.Filter</em>><em org.springframework.web.servlet.HandlerInterceptor</em>> 實例提供。

假設我們註冊了以下 <em HandlerInterceptor</em>> 實現,該實現解析請求並向會話中添加 <em login</em>> 參數以及向請求中的 <em query</em>> 參數:

public class ParamInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, 
      HttpServletResponse response, Object handler) throws Exception {
        request.getSession().setAttribute("login", "john");
        request.setAttribute("query", "invoices");
        return super.preHandle(request, response, handler);
    }

}

這些參數可以注入到 Controller 實例中,並通過方法參數上的對應註解進行標記:

@GetMapping
public String get(@SessionAttribute String login, 
  @RequestAttribute String query) {
    return String.format("login = %s, query = %s", login, query);
}

9. 支持的庫/應用服務器版本

Spring 4.3 支持以下庫版本和服務器生成:

  • Hibernate ORM 5.2 (同時支持 4.2/4.3 和 5.0/5.1 版本,現在 3.6 版本已棄用)
  • Jackson 2.8 (Spring 4.3 之後,最低版本要求 Jackson 2.6+)
  • OkHttp 3.x (與 OkHttp 2.x 並存,仍提供支持)
  • Netty 4.1
  • Undertow 1.4
  • Tomcat 8.5.2 以及 9.0 M6

此外,Spring 4.3 內嵌了更新的 ASM 5.1 和 Objenesis 2.4 在 spring-core.jar 中。

10.

InjectionPoint 類是 Spring 4.3 引入的一個新類,它提供有關特定 Bean 被注入的位置的信息,無論這些位置是方法/構造函數參數還是字段。

您可以使用此類找到的信息類型如下:

  • 字段對象 – 通過使用 getField() 方法,如果 Bean 被注入到字段中,可以獲取被注入的點包裝為 Field 對象
  • 方法參數 – 通過調用 getMethodParameter() 方法,如果 Bean 被注入到參數中,可以獲取被注入的點包裝為 MethodParameter 對象
  • 成員 – 調用 getMember() 方法將返回包含注入的 Bean 的實體,並將其包裝在 Member 對象中
  • 類<?> – 使用 getDeclaredType() 方法獲取參數或字段中 Bean 被注入的聲明類型
  • Annotation[] – 通過使用 getAnnotations() 方法,可以檢索一個 Annotation 對象數組,這些對象代表與字段或參數關聯的 Annotation
  • AnnotatedElement – 調用 getAnnotatedElement() 以獲取被注入的點包裝為 AnnotatedElement 對象

此類在我們需要根據其所屬的類創建 Logger Bean 時非常有用:

@Bean
@Scope("prototype")
public Logger logger(InjectionPoint injectionPoint) {
    return Logger.getLogger(
      injectionPoint.getMethodParameter().getContainingClass());
}

豆子必須使用 prototype作用域進行定義,以便為每個類創建一個不同的日誌記錄器。如果創建了 singleton 豆子並將其注入到多個位置,Spring 將返回遇到的第一個注入點。

然後,我們可以將豆子注入到 AppointmentsController 中:

@Autowired
private Logger logger;

11. 結論

在本文中,我們探討了 Spring 4.3 中引入的一些新功能。

我們介紹了有用的註解,這些註解消除了冗餘代碼,以及新的依賴查找和注入方法,以及在 Web 和緩存功能方面的諸多重大改進。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.