Какво е новото през пролет 4.3?

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

През пролет 4.2 и по-долу, следната конфигурация за този компонент няма да работи, защото 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, getters и setters, внедрени като интерфейсни методи по подразбиране, се идентифицират по време на инжектирането, което позволява да се използват например като общи препроцесори за достъпни свойства, както в този пример:

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

Този боб вече може да има инжектирано свойство 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 е облекчаването на изискването анотираните методи да бъдат публични - сега те могат да имат всяко ниво на видимост.

4. Подобрено разрешаване на зависимости

Най-новата версия също така въвежда ObjectProvider , разширение на съществуващия интерфейс ObjectFactory с удобни подписи като getIfAvailable и getIfUnique за извличане на боб само ако той съществува или ако може да бъде определен един кандидат (по-специално: първичен кандидат в случай на множество съответстващи зърна).

@Service public class FooService { private final FooRepository repository; public FooService(ObjectProvider repositoryProvider) { this.repository = repositoryProvider.getIfUnique(); } }

Можете да използвате такъв манипулатор ObjectProvider за целите на персонализираната резолюция по време на инициализацията, както е показано по-горе, или да съхранявате манипулатора в поле за късно разрешаване при поискване (както обикновено правите с ObjectFactory ).

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

Абстракцията на кеша се използва главно за кеширане на стойности, които консумират CPU и IO. В конкретни случаи на използване, даден ключ може да бъде поискан от няколко нишки (т.е. клиенти) успоредно, особено при стартиране. Поддръжката на синхронизиран кеш е отдавна поискана функция, която вече е внедрена. Да приемем следното:

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

Забележете атрибута sync = true , който казва на рамката да блокира всички едновременни нишки, докато стойността се изчислява. Това ще гарантира, че тази интензивна операция се извиква само веднъж в случай на едновременен достъп.

Spring 4.3 също подобрява абстракцията за кеширане, както следва:

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

6. Съставени варианти на @RequestMapping

Spring Framework 4.3 въвежда следните варианти на анотация @RequestMapping, съставени на ниво метод, които помагат да се опростят съпоставянията за често срещани методи на HTTP и да се изрази по-добре семантиката на метода на анотирания манипулатор.

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

For example, @GetMapping is a shorter form of saying @RequestMapping(method = RequestMethod.GET). The following example shows an MVC controller that has been simplified with a composed @GetMapping annotation.

@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 Annotations

When using annotation-driven components or Java Config, the @RequestScope, @SessionScope and @ApplicationScope annotations can be used to assign a component to the required scope. These annotations not only set the scope of the bean but also set the scoped proxy mode to ScopedProxyMode.TARGET_CLASS.

TARGET_CLASS mode means that CGLIB proxy will be used for proxying of this bean and ensuring that it can be injected in any other bean, even with a broader scope. TARGET_CLASS mode allows proxying not only for interfaces but classes too.

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

8. @RequestAttribute and @SessionAttribute Annotations

Two more annotations for injecting parameters of the HTTP request into Controller methods appeared, namely @RequestAttribute and @SessionAttribute. They allow you to access some pre-existing attributes, managed globally (i.e. outside the Controller). The values for these attributes may be provided, for instance, by registered instances of javax.servlet.Filter or org.springframework.web.servlet.HandlerInterceptor.

Suppose we have registered the following HandlerInterceptor implementation that parses the request and adds login parameter to the session and another query parameter to a request:

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

Such parameters may be injected into a Controller instance with corresponding annotations on method arguments:

@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 supports the following library versions and server generations:

  • Hibernate ORM 5.2 (still supporting 4.2/4.3 and 5.0/5.1 as well, with 3.6 deprecated now)
  • Jackson 2.8 (minimum raised to Jackson 2.6+ as of Spring 4.3)
  • OkHttp 3.x (still supporting OkHttp 2.x side by side)
  • Netty 4.1
  • Undertow 1.4
  • Tomcat 8.5.2 as well as 9.0 M6

Furthermore, Spring 4.3 embeds the updated ASM 5.1 and Objenesis 2.4 in spring-core.jar.

10. InjectionPoint

The InjectionPoint class is a new class introduced in Spring 4.3 which provides information about places where a particular bean gets injected, whether it is a method/constructor parameter or a field.

The types of information you can find using this class are:

  • Field object – you can obtain the point of injection wrapped as a Field object by using the getField() method if the bean is injected into a field
  • MethodParameter – you can call getMethodParameter() method to obtain the injection point wrapped as a MethodParameter object if the bean is injected into a parameter
  • Member – calling getMember() method will return the entity containing the injected bean wrapped into a Member object
  • Class – obtain the declared type of the parameter or field where the bean in injected, using getDeclaredType()
  • Annotation[] – by using the getAnnotations() method, you can retrieve an array of Annotation objects which represent the annotations associated with the field or parameter
  • AnnotatedElement – call getAnnotatedElement() to get the injection point wrapped as an AnnotatedElement object

A case in which this class is very useful is when we want to create Logger beans based on the class to which they belong:

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

The bean has to be defined with a prototype scope so that a different logger is created for each class. If you create a singleton bean and inject in multiple places, the Spring will return the first encountered injection point.

Then, we can inject the bean into our AppointmentsController:

@Autowired private Logger logger;

11. Conclusion

In this article, we discussed some of the new features introduced with Spring 4.3.

We've covered useful annotations that eliminate boilerplate, new helpful methods of dependency lookup and injection and several substantial improvements within the web and caching facilities.

Можете да намерите изходния код за статията на GitHub.