Перейти к основному содержимому

Что нового в Spring 4.3?

· 8 мин. чтения

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.foreach.spring43.ctor.FooRepository" />
<bean class="com.foreach.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 геттеры и сеттеры, реализованные как методы интерфейса по умолчанию, идентифицируются во время внедрения, что позволяет использовать их, например, в качестве общих препроцессоров для свойств, к которым осуществляется доступ, как в этом примере:

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.foreach.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 , — ослабление требования о том, что аннотируемые методы должны быть общедоступными — теперь они могут иметь любой уровень видимости.

4. Улучшенное разрешение зависимостей

В новейшей версии также представлен ObjectProvider , расширение существующего интерфейса ObjectFactory с удобными сигнатурами, такими как getIfAvailable и getIfUnique , для извлечения bean-компонента, только если он существует или если может быть определен единственный кандидат (в частности, первичный кандидат в случае нескольких подходящие бобы).

@Service
public class FooService {

private final FooRepository repository;

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

Вы можете использовать такой дескриптор ObjectProvider для целей пользовательского разрешения во время инициализации, как показано выше, или сохранить дескриптор в поле для позднего разрешения по запросу (как вы обычно делаете с ObjectFactory ).

5. Уточнения абстракции кэша

Абстракция кэша в основном используется для кэширования значений, потребляющих ресурсы ЦП и ввода-вывода. В особых случаях использования данный ключ может быть запрошен несколькими потоками (т. е. клиентами) параллельно, особенно при запуске. Поддержка синхронизированного кэша — это долгожданная функция, которая теперь реализована. Предположим следующее:

@Service
public class FooService {

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

}

Обратите внимание на атрибут sync = true , который указывает платформе блокировать любые параллельные потоки, пока вычисляется значение. Это гарантирует, что эта интенсивная операция будет вызвана только один раз в случае параллельного доступа.

Spring 4.3 также улучшает абстракцию кэширования следующим образом:

  • Выражения SpEL в аннотациях, связанных с кэшем, теперь могут ссылаться на bean-компоненты (например, @beanName.method() ).
  • ConcurrentMapCacheManager и ConcurrentMapCache теперь поддерживают сериализацию записей кэша с помощью нового атрибута storeByValue .
  • @Cacheable , @CacheEvict , @CachePut и @Caching теперь можно использовать в качестве мета-аннотаций для создания пользовательских составных аннотаций с переопределением атрибутов.

6. Составленные варианты @RequestMapping

Spring Framework 4.3 представляет следующие варианты аннотации @RequestMapping , составленные на уровне методов , которые помогают упростить сопоставления для распространенных методов HTTP и лучше выражают семантику аннотированного метода обработчика.

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

Например, @GetMapping — это более короткая форма выражения @RequestMapping(method = RequestMethod.GET) . В следующем примере показан контроллер MVC, упрощенный с помощью составной аннотации @GetMapping .

@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. Аннотации @RequestScope , @SessionScope , @ApplicationScope

При использовании компонентов, управляемых аннотациями, или Java Config аннотации @RequestScope , @SessionScope и @ApplicationScope можно использовать для назначения компонента требуемой области. Эти аннотации не только устанавливают область действия bean-компонента, но также устанавливают режим прокси-сервера с ограниченной областью действия на ScopedProxyMode.TARGET_CLASS.

Режим TARGET_CLASS означает, что прокси-сервер CGLIB будет использоваться для проксирования этого компонента и обеспечения возможности его внедрения в любой другой компонент, даже с более широкой областью действия. Режим TARGET_CLASS позволяет проксировать не только интерфейсы, но и классы .

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

8. Аннотации @RequestAttribute и @SessionAttribute

Появились еще две аннотации для внедрения параметров HTTP-запроса в методы Controller , а именно @RequestAttribute и @SessionAttribute . Они позволяют вам получить доступ к некоторым ранее существовавшим атрибутам, управляемым глобально (т.е. вне контроллера ). Значения для этих атрибутов могут быть предоставлены, например, зарегистрированными экземплярами javax.servlet.Filter или org.springframework.web.servlet.HandlerInterceptor .

Предположим, мы зарегистрировали следующую реализацию HandlerInterceptor , которая анализирует запрос и добавляет параметр входа в сеанс и еще один параметр запроса в запрос:

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

9. Поддержка версий библиотек/серверов приложений

Spring 4.3 поддерживает следующие версии библиотеки и поколения серверов:

  • Hibernate ORM 5.2 (по-прежнему поддерживает 4.2/4.3 и 5.0/5.1, а 3.6 устарела)
  • Jackson 2.8 (минимум повышен до Jackson 2.6+ по состоянию на Spring 4.3)
  • OkHttp 3.x (по-прежнему поддерживает OkHttp 2.x рядом)
  • Нетти 4.1
  • Отлив 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, который предоставляет информацию о местах, куда вводится конкретный компонент , будь то параметр метода/конструктора или поле.

Типы информации, которую вы можете найти с помощью этого класса:

  • Объект Field — вы можете получить точку внедрения, обернутую как объект Field , используя метод getField() , если bean-компонент вводится в поле.
  • MethodParameter — вы можете вызвать метод getMethodParameter() , чтобы получить точку внедрения, обернутую как объект MethodParameter , если bean-компонент вводится в параметр.
  • Member — вызов метода getMember() вернет сущность, содержащую внедренный bean-компонент, завернутый в объект Member .
  • Class<?> — получить объявленный тип параметра или поля, куда был введен бин, используя getDeclaredType()
  • Annotation[] — используя метод getAnnotations() , вы можете получить массив объектов Annotation, которые представляют аннотации, связанные с полем или параметром.
  • AnnotatedElement — вызовите getAnnotatedElement() , чтобы получить точку внедрения, обернутую как объект AnnotatedElement .

Случай, в котором этот класс очень полезен, — это когда мы хотим создать bean-компоненты Logger на основе класса, к которому они принадлежат:

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

Компонент должен быть определен с областью действия прототипа , чтобы для каждого класса создавался отдельный регистратор. Если вы создаете одноэлементный компонент и внедряете его в несколько мест, Spring вернет первую обнаруженную точку внедрения.

Затем мы можем внедрить bean-компонент в наш AppointmentsController :

@Autowired
private Logger logger;

11. Заключение

В этой статье мы обсудили некоторые новые функции, представленные в Spring 4.3.

Мы рассмотрели полезные аннотации, которые устраняют шаблоны, новые полезные методы поиска и внедрения зависимостей, а также несколько существенных улучшений в веб-средствах и средствах кэширования.

Исходный код статьи можно найти на GitHub .