Что нового в весне 4.3?
1. обзор
Релиз Spring 4.3 внес несколько приятных улучшений в основной контейнер, кеширование, JMS, Web MVC и тестовые подмодули платформы.
В этом посте мы обсудим некоторые из этих улучшений, в том числе:
-
Неявная инъекция конструктора
-
Поддержка методов интерфейса Java 8 по умолчанию
-
Улучшенное разрешение зависимостей
-
Улучшения Cache Abstraction
-
Составлено@RequestMapping Варианты
-
@Requestscope, @Sessionscope, @Applicationscope Аннотации
-
Аннотации@RequestAttribute и@SessionAttribute
-
Libraries/Application Servers Versions Support
-
класс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 умнее и автоматически связывает конструктор:
Точно так же вы могли заметить, что классы@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 по умолчанию
До весны 4.3 стандартные методы интерфейса не поддерживались.
Это было нелегко реализовать, потому что даже интроспектор JavaBean JDK не обнаруживал методы по умолчанию как средства доступа. Начиная с 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:
То же самое касается использования тестовых аннотаций, таких как@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 с удобными сигнатурами, такими какgetIfAvailable иgetIfUnique, для получения bean-компонента, только если он существует или может быть определен единственный кандидат (в частности: основной кандидат в случае нескольких совпадающих bean-компонентов).
@Service
public class FooService {
private final FooRepository repository;
public FooService(ObjectProvider repositoryProvider) {
this.repository = repositoryProvider.getIfUnique();
}
}
Вы можете использовать такой дескрипторObjectProvider для целей настраиваемого разрешения во время инициализации, как показано выше, или сохранить дескриптор в поле для позднего разрешения по запросу (как вы обычно делаете сObjectFactory).
5. Улучшения Cache Abstraction
Абстракция кэша в основном используется для кэширования значений, которые потребляют ресурсы процессора и ввода-вывода. В конкретных случаях использования данный ключ может быть запрошен несколькими потоками (т.е. клиенты) параллельно, особенно при запуске. Поддержка синхронизированного кэша - давно востребованная функция, которая теперь реализована. Предположим следующее:
@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 get() {
return appointmentBook.getAppointmentsForToday();
}
}
7. Аннотации@RequestScope,@SessionScope,@ApplicationScope
При использовании компонентов, управляемых аннотациями, или Java Config, аннотации@RequestScope,@SessionScope и@ApplicationScope могут использоваться для назначения компонента требуемой области. Эти аннотации не только устанавливают область действия bean-компонента, но также устанавливают режим прокси с областью действия наScopedProxyMode.TARGET_CLASS.
РежимTARGET_CLASS означает, что прокси-сервер CGLIB будет использоваться для проксирования этого bean-компонента и обеспечения возможности его внедрения в любой другой bean-компонент, даже с более широкой областью действия. РежимTARGET_CLASS позволяет проксировать не только интерфейсы, но и классы.
@RequestScope
@Component
public class LoginAction {
// ...
}
@SessionScope
@Component
public class UserPreferences {
// ...
}
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
8. Аннотации@RequestAttribute и@SessionAttribute
Появились еще две аннотации для внедрения параметров HTTP-запроса в методыController, а именно@RequestAttribute и@SessionAttribute. Они позволяют вам получить доступ к некоторым уже существующим атрибутам, управляемым на глобальном уровне (т.е. внеController). Значения для этих атрибутов могут предоставляться, например, зарегистрированными экземплярамиjavax.servlet.Filter илиorg.springframework.web.servlet.HandlerInterceptor.
Предположим, мы зарегистрировали следующую реализациюHandlerInterceptor, которая анализирует запрос и добавляет параметрlogin к сеансу и еще один параметрquery к запросу:
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. Libraries/Application Servers Versions Support
Spring 4.3 поддерживает следующие версии библиотеки и поколения серверов:
-
Hibernate ORM 5.2 (по-прежнему поддерживает 4.2 / 4.3 и 5.0 / 5.1, а 3.6 уже не рекомендуется)
-
Джексон 2.8 (минимум поднят до Джексона 2.6+ начиная с весны 4.3)
-
OkHttp 3.x (все еще поддерживает OkHttp 2.x бок о бок)
-
Нетти 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с
КлассInjectionPoint - это новый класс, представленный в Spring 4.3, которыйprovides information about places where a particular bean gets injected, будь то параметр метода / конструктора или поле.
Типы информации, которую вы можете найти с помощью этого класса:
-
ОбъектField - вы можете получить точку инъекции, обернутую как объектField, используя методgetField(), если bean-компонент вводится в поле
-
MethodParameter - вы можете вызвать методgetMethodParameter(), чтобы получить точку инъекции, обернутую как объектMethodParameter, если bean-компонент введен в параметр
-
Member - вызов методаgetMember() вернет сущность, содержащую внедренный bean-компонент, завернутый в объектMember
-
Class<?> - получить объявленный тип параметра или поля, в которое внедрен bean-компонент, используяgetDeclaredType()
-
Annotation[] - используя методgetAnnotations(), вы можете получить массив объектов Annotation, которые представляют аннотации, связанные с полем или параметром
-
AnnotatedElement - вызовgetAnnotatedElement(), чтобы точка инъекции была обернута как объектAnnotatedElement
Случай, в котором этот класс очень полезен, - это когда мы хотим создать bean-компонентыLogger на основе класса, к которому они принадлежат:
@Bean
@Scope("prototype")
public Logger logger(InjectionPoint injectionPoint) {
return Logger.getLogger(
injectionPoint.getMethodParameter().getContainingClass());
}
Компонент должен быть определен с областью действияprototype, чтобы для каждого класса создавался отдельный регистратор. Если вы создаете bean-компонентsingleton и вводите его в нескольких местах, Spring вернет первую встреченную точку внедрения.
Затем мы можем внедрить bean в нашAppointmentsController:
@Autowired
private Logger logger;
11. Заключение
В этой статье мы обсудили некоторые новые функции, появившиеся в Spring 4.3.
Мы рассмотрели полезные аннотации, которые исключают шаблон, новые полезные методы поиска и внедрения зависимостей, а также несколько существенных улучшений в Интернете и средствах кэширования.
Вы можете найти исходный код статьиon GitHub.