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 .