1. Pengenalan
Ringkasnya, dalam corak reka bentuk Pengawal Depan , pengawal tunggal bertanggungjawab untuk mengarahkan HttpRequests masuk ke semua pengawal dan pengendali aplikasi lain .
Spring's DispatcherServlet menerapkan corak ini dan, oleh itu, bertanggungjawab untuk mengkoordinasikan HttpRequests dengan betul kepada pengendali kanan mereka.
Dalam artikel ini, kami akan memeriksa aliran kerja pemprosesan permintaan Spring DispatcherServlet dan cara melaksanakan beberapa antara muka yang mengambil bahagian dalam aliran kerja ini.
2. Pemprosesan Permintaan DispatcherServlet
Pada dasarnya, DispatcherServlet menangani HttpRequest yang masuk , mendelegasikan permintaan, dan memproses permintaan yang sesuai dengan antara muka HandlerAdapter yang dikonfigurasi yang telah dilaksanakan dalam aplikasi Spring bersama dengan anotasi yang menyertakan penangan, titik akhir pengawal, dan objek respons.
Mari dapatkan lebih mendalam mengenai bagaimana DispatcherServlet memproses komponen:
- yang WebApplicationContext dikaitkan kepada DispatcherServlet bawah kekunci DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE dicari dan disediakan kepada semua unsur-unsur proses
- The DispatcherServlet mendapati semua pelaksanaan yang HandlerAdapter muka dikonfigurasikan untuk penghantar anda menggunakan getHandler () - setiap ditemui dan mengendalikan pelaksanaan dikonfigurasikan permintaan melalui pemegang () melalui baki proses
- yang LocaleResolver adalah pilihan terikat kepada permintaan untuk membolehkan elemen dalam proses untuk menyelesaikan penempatan
- yang ThemeResolver adalah pilihan terikat untuk permintaan untuk membiarkan elemen, seperti pandangan, menentukan tema untuk penggunaan
- jika MultipartResolver ditentukan, permintaan diperiksa untuk MultipartFile s - mana-mana yang dijumpai dibungkus dalam MultipartHttpServletRequest untuk pemprosesan selanjutnya
- HandlerExceptionResolver implementasi yang dinyatakan dalam WebApplicationContext mengambil pengecualian yang dilemparkan semasa memproses permintaan
Anda boleh mengetahui lebih lanjut mengenai semua cara untuk mendaftar dan menyediakan DispatcherServlet di sini.
3. Antara muka HandlerAdapter
The HandlerAdapter muka memudahkan penggunaan pengawal, Servlets, HttpRequests , dan HTTP laluan melalui beberapa antara muka tertentu. The HandlerAdapter muka itu memainkan peranan penting melalui banyak peringkat daripada DispatcherServlet aliran kerja permintaan pemprosesan .
Pertama, setiap pelaksanaan HandlerAdapter dimasukkan ke dalam HandlerExecutionChain dari kaedah getHandler () penghantar anda . Kemudian, setiap daripada mereka pelaksanaan mengendalikan () yang HttpServletRequest objek sebagai hasil rantaian pelaksanaan.
Dalam bahagian berikut, kami akan meneroka beberapa HandlerAdapters yang paling penting dan biasa digunakan dengan lebih terperinci.
3.1. Pemetaan
Untuk memahami pemetaan, kita perlu melihat terlebih dahulu cara memberi penjelasan pengawal kerana pengawal sangat penting untuk antara muka Pengendali Pemetaan .
The SimpleControllerHandlerAdapter membolehkan pelaksanaan pengawal jelas tanpa @Controller anotasi.
The RequestMappingHandlerAdapter menyokong kaedah dijelaskan dengan @RequestMapping anotasi .
Kami akan memberi tumpuan kepada penjelasan @Controller di sini, tetapi sumber yang berguna dengan beberapa contoh menggunakan SimpleControllerHandlerAdapter juga tersedia.
The @RequestMapping anotasi menetapkan titik akhir tertentu di mana pengendali yang boleh didapati dalam WebApplicationContext yang berkaitan dengannya.
Mari lihat contoh Pengawal yang memperlihatkan dan menangani titik akhir '/ pengguna / contoh' :
@Controller @RequestMapping("/user") @ResponseBody public class UserController { @GetMapping("/example") public User fetchUserExample() { // ... } }
Laluan yang ditentukan oleh anotasi @RequestMapping diuruskan secara dalaman melalui antara muka HandlerMapping .
Struktur URL secara semula jadi relatif terhadap DispatcherServlet itu sendiri - dan ditentukan oleh pemetaan servlet.
Oleh itu, jika DispatcherServlet dipetakan ke '/', maka semua pemetaan akan dilindungi oleh pemetaan tersebut.
Namun, jika pemetaan servlet adalah ' / dispatcher ', maka setiap anotasi @ RequestMapping akan relatif dengan URL akar itu.
Ingat bahawa '/' tidak sama dengan '/ *' untuk pemetaan servlet! '/' adalah pemetaan lalai dan memperlihatkan semua URL ke wilayah tanggungjawab penghantar.
‘/*' is confusing to a lot of newer Spring developers. It does not specify that all paths with the same URL context are under the dispatcher's area of responsibility. Instead, it overrides and ignores the other dispatcher mappings. So, ‘/example' will come up as a 404!
For that reason, ‘/*' shouldn't be used except in very limited circumstances (like configuring a filter).
3.2. HTTP Request Handling
The core responsibility of a DispatcherServlet is to dispatch incoming HttpRequests to the correct handlers specified with the @Controller or @RestController annotations.
As a side note, the main difference between @Controller and @RestController is how the response is generated – the @RestController also defines @ResponseBody by default.
A writeup where we go into much greater depth regarding Spring's controllers can be found here.
3.3. The ViewResolver Interface
A ViewResolver is attached to a DispatcherServlet as a configuration setting on an ApplicationContext object.
A ViewResolver determines both what kind of views are served by the dispatcher and from where they are served.
Here's an example configuration which we'll place into our AppConfig for rendering JSP pages:
@Configuration @EnableWebMvc @ComponentScan("com.baeldung.springdispatcherservlet") public class AppConfig implements WebMvcConfigurer { @Bean public UrlBasedViewResolver viewResolver() { UrlBasedViewResolver resolver = new UrlBasedViewResolver(); resolver.setPrefix("/WEB-INF/view/"); resolver.setSuffix(".jsp"); resolver.setViewClass(JstlView.class); return resolver; } }
Very straight-forward! There are three main parts to this:
- setting the prefix, which sets the default URL path to find the set views within
- the default view type which is set via the suffix
- setting a view class on the resolver which allows technologies like JSTL or Tiles to be associated with the rendered views
One common question involves how precisely a dispatcher's ViewResolverand the overall project directory structure are related. Let's take a look at the basics.
Here's an example path configuration for an InternalViewResolver using Spring's XML configuration:
For the sake of our example, we'll assume that our application is being hosted on:
//localhost:8080/
This is the default address and port for a locally hosted Apache Tomcat server.
Assuming that our application is called dispatcherexample-1.0.0, our JSP views will be accessible from:
//localhost:8080/dispatcherexample-1.0.0/jsp/
The path for these views within an ordinary Spring project with Maven is this:
src -| main -| java resources webapp -| jsp WEB-INF
The default location for views is within WEB-INF. The path specified for our InternalViewResolver in the snippet above determines the subdirectory of ‘src/main/webapp' in which your views will be available.
3.4. The LocaleResolver Interface
The primary way to customize session, request, or cookie information for our dispatcher is through the LocaleResolver interface.
CookieLocaleResolver is an implementation allowing the configuration of stateless application properties using cookies. Let's add it to AppConfig.
@Bean public CookieLocaleResolver cookieLocaleResolverExample() { CookieLocaleResolver localeResolver = new CookieLocaleResolver(); localeResolver.setDefaultLocale(Locale.ENGLISH); localeResolver.setCookieName("locale-cookie-resolver-example"); localeResolver.setCookieMaxAge(3600); return localeResolver; } @Bean public LocaleResolver sessionLocaleResolver() { SessionLocaleResolver localeResolver = new SessionLocaleResolver(); localeResolver.setDefaultLocale(Locale.US); localResolver.setDefaultTimeZone(TimeZone.getTimeZone("UTC")); return localeResolver; }
SessionLocaleResolver allows for session-specific configuration in a stateful application.
The setDefaultLocale() method represents a geographical, political, or cultural region, whereas setDefaultTimeZone( ) determines the relevant TimeZone object for the application Bean in question.
Both methods are available on each of the above implementations of LocaleResolver.
3.5. The ThemeResolver Interface
Spring provides stylistic theming for our views.
Let's take a look at how to configure our dispatcher to handle themes.
First, let's set up all the configuration necessary to find and use our static theme files. We need to set a static resource location for our ThemeSource to configure the actual Themes themselves (Theme objects contain all of the configuration information stipulated in those files). Add this to AppConfig:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/", "/resources/") .setCachePeriod(3600) .resourceChain(true) .addResolver(new PathResourceResolver()); } @Bean public ResourceBundleThemeSource themeSource() { ResourceBundleThemeSource themeSource = new ResourceBundleThemeSource(); themeSource.setDefaultEncoding("UTF-8"); themeSource.setBasenamePrefix("themes."); return themeSource; }
Requests managed by the DispatcherServlet can modify the theme through a specified parameter passed into setParamName() available on the ThemeChangeInterceptor object. Add to AppConfig:
@Bean public CookieThemeResolver themeResolver() { CookieThemeResolver resolver = new CookieThemeResolver(); resolver.setDefaultThemeName("example"); resolver.setCookieName("example-theme-cookie"); return resolver; } @Bean public ThemeChangeInterceptor themeChangeInterceptor() { ThemeChangeInterceptor interceptor = new ThemeChangeInterceptor(); interceptor.setParamName("theme"); return interceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(themeChangeInterceptor()); }
The following JSP tag is added to our view to make the correct styling appear:
The following URL request renders the example theme using the ‘theme' parameter passed into our configured ThemeChangeIntercepter:
//localhost:8080/dispatcherexample-1.0.0/?theme=example
3.6. The MultipartResolver Interface
A MultipartResolver implementation inspects a request for multiparts and wraps them in a MultipartHttpServletRequest for further processing by other elements in the process if at least one multipart is found. Add to AppConfig:
@Bean public CommonsMultipartResolver multipartResolver() throws IOException { CommonsMultipartResolver resolver = new CommonsMultipartResolver(); resolver.setMaxUploadSize(10000000); return resolver; }
Now that we've configured our MultipartResolver bean, let's set up a controller to process MultipartFile requests:
@Controller public class MultipartController { @Autowired ServletContext context; @PostMapping("/upload") public ModelAndView FileuploadController( @RequestParam("file") MultipartFile file) throws IOException { ModelAndView modelAndView = new ModelAndView("index"); InputStream in = file.getInputStream(); String path = new File(".").getAbsolutePath(); FileOutputStream f = new FileOutputStream( path.substring(0, path.length()-1) + "/uploads/" + file.getOriginalFilename()); int ch; while ((ch = in.read()) != -1) { f.write(ch); } f.flush(); f.close(); in.close(); modelAndView.getModel() .put("message", "File uploaded successfully!"); return modelAndView; } }
We can use a normal form to submit a file to the specified endpoint. Uploaded files will be available in ‘CATALINA_HOME/bin/uploads'.
3.7. The HandlerExceptionResolver Interface
Spring's HandlerExceptionResolver provides uniform error handling for an entire web application, a single controller, or a set of controllers.
To provide application-wide custom exception handling, create a class annotated with @ControllerAdvice:
@ControllerAdvice public class ExampleGlobalExceptionHandler { @ExceptionHandler @ResponseBody public String handleExampleException(Exception e) { // ... } }
Any methods within that class annotated with @ExceptionHandler will be available on every controller within dispatcher's area of responsibility.
Implementations of the HandlerExceptionResolver interface in the DispatcherServlet's ApplicationContext are available to intercept a specific controller under that dispatcher's area of responsibility whenever @ExceptionHandler is used as an annotation, and the correct class is passed in as a parameter:
@Controller public class FooController{ @ExceptionHandler({ CustomException1.class, CustomException2.class }) public void handleException() { // ... } // ... }
The handleException() method will now serve as an exception handler for FooController in our example above if either exception CustomException1 or CustomException2 occurs.
Here's an article that goes more in-depth about exception handling in a Spring web application.
4. Conclusion
In this tutorial, we've reviewed Spring's DispatcherServlet and several ways to configure it.
As always, the source code used in this tutorial is available over on Github.