Apa yang Baru di Spring 4.3?

1. Ikhtisar

Rilis Spring 4.3 membawa beberapa perbaikan bagus ke dalam wadah inti, caching, JMS, MVC Web, dan pengujian submodul kerangka kerja.

Pada postingan kali ini, kami akan membahas beberapa perbaikan tersebut diantaranya:

  • Injeksi Konstruktor Implisit
  • Dukungan Metode Antarmuka Default Java 8
  • Resolusi Ketergantungan yang Lebih Baik
  • Perbaikan Abstraksi Cache
  • Terdiri @RequestMapping Varian
  • @Requestscope, @Sessionscope, @Applicationscope Anotasi
  • @RequestAttribute dan @SessionAttribute anotasi
  • Dukungan Versi Perpustakaan / Server Aplikasi
  • yang InjectionPoint kelas

2. Injeksi Konstruktor Implisit

Pertimbangkan kelas layanan berikut:

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

Kasus penggunaan yang cukup umum, tetapi jika Anda lupa anotasi @Autowired pada konstruktor, container akan mengeluarkan pengecualian untuk mencari konstruktor default, kecuali Anda secara eksplisit melakukan wiring.

Jadi mulai 4.3, Anda tidak perlu lagi menentukan anotasi injeksi eksplisit dalam skenario konstruktor tunggal seperti itu. Ini sangat elegan untuk kelas yang tidak membawa anotasi sama sekali:

public class FooService { private final FooRepository repository; public FooService(FooRepository repository) { this.repository = repository } }

Di Spring 4.2 dan yang lebih lama, konfigurasi berikut untuk kacang ini tidak akan berfungsi, karena Spring tidak akan dapat menemukan konstruktor default untuk FooService . Spring 4.3 lebih pintar dan akan secara otomatis memasang konstruktor:

Demikian pula, Anda mungkin memperhatikan bahwa kelas @Configuration secara historis tidak mendukung injeksi konstruktor. Mulai 4.3, mereka melakukannya, dan mereka secara alami mengizinkan penghapusan @Autowired dalam skenario konstruktor tunggal juga:

@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. Dukungan Metode Antarmuka Default Java 8

Sebelum Spring 4.3, metode antarmuka default tidak didukung.

Ini tidak mudah untuk diterapkan karena bahkan introspektor JavaBean JDK tidak mendeteksi metode default sebagai pengakses. Sejak Spring 4.3, getter dan setter yang diimplementasikan sebagai metode antarmuka default diidentifikasi selama injeksi, yang memungkinkan untuk menggunakannya misalnya sebagai preprosesor umum untuk properti yang diakses, seperti dalam contoh ini:

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

Kacang ini sekarang mungkin memiliki properti stringDate yang diinjeksi:

Hal yang sama berlaku untuk penggunaan anotasi pengujian seperti @BeforeTransaction dan @AfterTransaction pada metode antarmuka default. JUnit 5 sudah mendukung anotasi pengujiannya pada metode antarmuka default, dan Spring 4.3 mengikuti petunjuknya. Sekarang Anda dapat mengabstraksi logika pengujian umum dalam sebuah antarmuka dan mengimplementasikannya di kelas pengujian. Berikut adalah antarmuka untuk kasus pengujian yang mencatat pesan sebelum dan sesudah transaksi dalam pengujian:

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

Perbaikan lain mengenai penjelasan @BeforeTransaction, @AfterTransaction dan @Transactional adalah relaksasi persyaratan bahwa metode dijelaskan harus publik - sekarang mereka mungkin memiliki tingkat visibilitas.

4. Peningkatan Resolusi Ketergantungan

Versi terbaru juga memperkenalkan ObjectProvider , sebuah ekstensi dari antarmuka ObjectFactory yang sudah ada dengan tanda tangan praktis seperti getIfAvailable dan getIfUnique untuk mengambil kacang hanya jika ada atau jika satu kandidat dapat ditentukan (khususnya: kandidat utama dalam kasus beberapa kacang yang cocok).

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

Anda dapat menggunakan pegangan ObjectProvider tersebut untuk tujuan resolusi kustom selama inisialisasi seperti yang ditunjukkan di atas, atau menyimpan pegangan di bidang untuk resolusi akhir sesuai permintaan (seperti yang biasanya Anda lakukan dengan ObjectFactory ).

5. Perbaikan Abstraksi Cache

Abstraksi cache terutama digunakan untuk nilai cache yang memakan CPU dan IO. Dalam kasus penggunaan tertentu, kunci tertentu dapat diminta oleh beberapa thread (yaitu klien) secara paralel, terutama saat startup. Dukungan cache tersinkronisasi adalah fitur yang telah lama diminta yang sekarang telah diterapkan. Asumsikan sebagai berikut:

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

Perhatikan atribut sync = true yang memberi tahu kerangka kerja untuk memblokir semua utas bersamaan saat nilai sedang dihitung. Ini akan memastikan bahwa operasi intensif ini dipanggil hanya sekali dalam kasus akses bersamaan.

Spring 4.3 juga meningkatkan abstraksi caching sebagai berikut:

  • Ekspresi SpEL dalam anotasi yang berhubungan dengan cache sekarang bisa merujuk ke kacang (yaitu @ beanName.method () ).
  • ConcurrentMapCacheManager dan ConcurrentMapCache sekarang mendukung serialisasi entri cache melalui atribut storeByValue baru .
  • @Cacheable , @CacheEvict , @CachePut , dan @Caching sekarang dapat digunakan sebagai meta-anotasi untuk membuat anotasi tersusun khusus dengan penggantian atribut.

6. Menyusun Varian @RequestMapping

Spring Framework 4.3 memperkenalkan varian tersusun tingkat metode berikut dari anotasi @RequestMapping yang membantu menyederhanakan pemetaan untuk metode HTTP umum dan mengekspresikan semantik metode penangan beranotasi dengan lebih baik.

  • @Tokopedia
  • @Postingan
  • @Putih
  • @Hapus
  • @Catatan

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.

Anda dapat menemukan kode sumber untuk artikel tersebut di GitHub.