隐式构造函数注入

考虑以下服务类:

@Service
public class FooService {

    private final FooRepository repository;

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

这是常见场景,但忘记在构造函数上加 @Autowired 会导致容器抛出异常(除非显式配置)。Spring 4.3 开始,单构造函数场景下无需显式注入注解:

public class FooService {

    private final FooRepository repository;

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

在 Spring 4.2 及以下版本,以下 XML 配置会因找不到默认构造函数而失败。Spring 4.3 则能自动注入:

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

同理,@Configuration 类现在也支持构造函数注入,单构造函数时可省略 @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);
    }
}

Java 8 默认接口方法支持

Spring 4.3 前不支持默认接口方法(因 JDK 的 JavaBean 内省器不识别默认方法)。现在注入时能识别默认方法实现的 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")));
    }
}

现在可通过 stringDate 属性注入:

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

测试注解(如 @BeforeTransaction)同样支持默认接口方法。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,任意可见性均可。

依赖解析增强

Spring 4.3 引入 ObjectProvider(扩展 ObjectFactory),提供便捷方法:

  • getIfAvailable - 仅当 Bean 存在时获取
  • getIfUnique - 仅当存在唯一候选 Bean 时获取(多匹配时取 primary)
@Service
public class FooService {

    private final FooRepository repository;

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

可用于初始化时自定义解析,或存储句柄用于延迟按需解析(类似 ObjectFactory 用法)。

缓存抽象优化

缓存抽象常用于 CPU/IO 密集型操作。某些场景(如启动)多个线程可能并行请求同一 key。Spring 4.3 新增同步缓存支持:

@Service
public class FooService {

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

sync = true 确保值计算时阻塞并发线程,避免重复计算。其他优化包括:

  • 缓存注解元数据改进
  • 缓存解析器增强
  • 缓存管理器配置简化

组合式 @RequestMapping 变体

Spring 4.3 新增方法级组合注解,简化常见 HTTP 方法映射:

  • @GetMapping = @RequestMapping(method = GET)
  • @PostMapping = @RequestMapping(method = POST)
  • @PutMapping = @RequestMapping(method = PUT)
  • @DeleteMapping = @RequestMapping(method = DELETE)
  • @PatchMapping = @RequestMapping(method = PATCH)

示例:

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

@RequestScope, @SessionScope, @ApplicationScope 注解

使用注解驱动或 Java Config 时,可通过这些注解指定组件作用域:

  • @RequestScope - 请求作用域
  • @SessionScope - 会话作用域
  • @ApplicationScope - 应用作用域

这些注解不仅设置作用域,还自动设置 ScopedProxyMode.TARGET_CLASS(CGLIB 代理),确保 Bean 可注入到更广作用域的 Bean 中:

@RequestScope
@Component
public class LoginAction { ... }

@SessionScope
@Component
public class UserPreferences { ... }

@ApplicationScope
@Component
public class AppPreferences { ... }

@RequestAttribute 和 @SessionAttribute 注解

新增两个注解用于注入 HTTP 请求参数:

  • @RequestAttribute - 访问请求属性
  • @SessionAttribute - 访问会话属性

这些属性通常由全局组件(如 FilterHandlerInterceptor)设置。示例拦截器:

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

控制器中直接注入:

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

库与应用服务器版本支持

Spring 4.3 支持以下版本:

  • Hibernate ORM 5.0+
  • Jackson 2.6+
  • OkHttp 3.x
  • Netty 4.1
  • Tomcat 8.5+
  • WildFly 10

同时 spring-core.jar 内置升级版 ASM 5.1 和 Objenesis 2.4。

InjectionPoint 类

InjectionPoint 是 Spring 4.3 新增类,提供 Bean 注入点的详细信息(字段/方法参数/构造函数参数)。可获取:

  • 注入字段对象(getField()
  • 方法参数对象(getMethodParameter()
  • 包含成员(getMember()
  • 声明类型(getDeclaredType()
  • 关联注解(getAnnotations()
  • 注解元素(getAnnotatedElement()

典型场景:按注入类创建 Logger:

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

注意:Bean 必须是 prototype 作用域,否则多注入点会返回首次遇到的注入点。使用示例:

@Autowired
private Logger logger;

总结

Spring 4.3 带来诸多实用改进:

  • 注解简化 - 隐式构造注入、组合式映射注解、作用域注解
  • 依赖注入增强 - ObjectProviderInjectionPoint
  • Web 层优化 - 属性注入注解、接口默认方法支持
  • 缓存改进 - 同步缓存支持

这些特性大幅减少样板代码,提升开发效率。完整示例代码见 GitHub


原始标题:What's New in Spring 4.3?