【玩轉Spring】spring mvc

M:Model 模型

V:View 視圖

C:Controller 控制器

Spring MVC是基於模型-視圖-控制器模式實現的。不管你是struts,還是Spring MVC,只要是基於Java的WEB框架,都會通過一個前端控制器器。在Spring MVC中DispatcherServlet就是它的前端控制器,那麼這個前端控制器做了什麼呢?

DispatcherServlet

 

明白了DispatcherServlet是Spring MVC的前端控制器,那麼我們又是怎麼將請求全部先發給前端控制器,然後由前端控制器來控制跳轉到相應的組件呢?

注意,如果要使用註解的方式啓動MVC,你的項目必須部署在支持servlet3.0的容器當中,如tomcat7或者更好的版本;這是由於在Servlet 3.0環境中, 容器會在類路徑中查找實現javax.servlet.ServletContainerInitializer接口的類, 如果能發現的話, 就會用它來配置Servlet容器。而Spring提供了這個接口的實現, 名爲SpringServletContainerInitializer, 這個類反過來又會查找實現WebApplicationInitializer的類並將配置的任務交給它們來完成。

程序一:配置前端控制器


/***********************DispatcherServlet*******************/
public class ServletInit extends AbstractAnnotationConfigDispatcherServletInitializer{
    public ServletInit() {
        System.out.println("dispatcherservlet啓動了");
    }

    //指定非WEB相關的配置類
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] {RootConfig.class};
    }

    //指定WEB啓動的配置類
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] {WebConfig.class};
    }

     //將DispatcherServlet映射到 "/"
    @Override
    protected String[] getServletMappings() {
        return  new String[] {"/"};
    }
}

/*************************RooConfig**********************/
@Configuration
@ComponentScan(basePackages= {"mvc.logic"})
public class RootConfig {
    public RootConfig() {
        System.out.println("RootConfig啓動");
    }
}

/*************************WebConfig**********************/
@Configuration
@ComponentScan(basePackages= {"mvc.web"})
@EnableWebMvc
public class WebConfig {
    public WebConfig() {
        System.out.println("WebConfig啓動");
    }
}

 

上面的代碼有兩個問題

1、沒有配置視圖解析器

2、對靜態資源也進行了攔截

視圖解析器

視圖解析器的作用是將邏輯視圖轉爲物理視圖,所有的視圖解析器都必須實現ViewResolver接口。Spring MVC將按照你配置的不同的視圖解析器來對模板進行渲染。Spring爲提供了對多種視圖的支持,常用的是以下三種:

  • InternalResourceViewResolver  將視圖解析爲JSP

  • FreeMarketViewResolver 將視圖解析爲FreeMarker模板

  • ThymeleafViewResolver 將視圖解析爲Thymeleaf模板

程序二:JSP視圖解析器


@Configuration
@ComponentScan(basePackages= {"mvc.web"})
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter{
    public WebConfig() {
        System.out.println("WebConfig啓動");
    }

    //配置JSP視圖解析器
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver  viewreResolver = new InternalResourceViewResolver("/WEB-INF/views/",".jsp");
        return viewreResolver;
    }

    //配置靜態資源過濾
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

}

 

控制器

控制器是真正對數據進行處理的地方,主要包括了參數的接收和view的返回。

程序三:簡單的控制器


@Controller
public class UserController {
    public UserController() {
        System.out.println("UserController被啓動了");
    }

    /*************表示接收處理路徑爲index的,GET方式的請求********/
    @RequestMapping(value="/index",method=RequestMethod.GET)
    public void index() {
        System.out.println("調用了index");
    }
}

 

控制器其實就是一個類,只不過使用@RequestMaaping將方法與路徑進行了綁定。

參數的接收

@ReqeustParame == request.getParameter

@RequestMapping(value="/index/",method=RequestMethod.GET)
public void index(@RequestParam("userName") String userName ) {
    System.out.println("調用了index");
}

@PathVariable

@RequestMapping(value="/index/{userName}",method=RequestMethod.GET)
public void index(@PathVariable("userName") String userName ) {
    System.out.println("調用了index");
}

視圖返回

返回一個視圖名

@RequestMapping(value="/index/",method=RequestMethod.GET)
public String index(@RequestParam("userName") String userName ) {
    return "index";
}

返回ModelAndView

@RequestMapping(value="/index/",method=RequestMethod.GET)
public ModelAndView index(@RequestParam("userName") String userName ) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("", "");
    modelAndView.setViewName("");
    return modelAndView;
}

 

文件上傳

首先我們想一想文件上傳我們在服務端知道些什麼?

  • 文件名

  • 文件類型

  • 文件大小

  • 文件

那麼Spring MVC是怎麼處理這些問題的呢?

上一節我們已經實現自定義DispatcherServlet,但是在DispatcherServlet中並未實現任何解析multipart請求數據的功能。它將該任務委託給了Spring中MultipartResolver策略接口的實現, 通過這個實現類來解析multipart請求中的內容。 從Spring 3.1開始, Spring內置了兩個MultipartResolver的實現供我們選擇:CommonsMultipartResolver( 使用Jakarta Commons FileUpload解析multipart請求);StandardServletMultipartResolver(依賴於Servlet 3.0對multipart請求的支持)。一般來講, 在這兩者之間, StandardServletMultipartResolver可能會是優選的方案(不依賴於外部組件)。

程序一:在WebConfig中配置MultipartResolver


@Bean
public MultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
}

同時在DispatcherServlet中,你還需要重寫customizeRegistration函數。

程序二:配置文件上傳的相關參數


@Override
protected void customizeRegistration(Dynamic registration) {
    String location = "E:/spring-mvc/tmp/uploads";
    File file = new File(location);
    if(!file.exists()) {
        file.mkdirs();
    }
        //每一個文件爲3M
    long maxFileSize = 1024 * 1024 * 3;
        //一共上傳15M的內容 
    long requestFileSzie = maxFileSize * 5; 
        //當緩存中有好大的時候,寫入磁盤
    int fileSizeThreshold = 0; 
    registration.setMultipartConfig(
                new MultipartConfigElement(
                        location, 
                        maxFileSize,
                        requestFileSzie ,
                        fileSizeThreshold));
}

程序三:在controller中獲取MultipartFile數據


@RequestMapping(value="/upload")
public String upload(@RequestPart("myFile") MultipartFile myFile) {
    System.out.println("文件名稱:"+myFile.getOriginalFilename());
    return "";
}

至此,Spring MVC文件上傳就完了,是不是非常簡單,非常感謝Spring爲我們帶來如此簡便的文件上傳方法。

異常處理

在Http中,大家經常會碰到404、500等常見異常錯誤碼,但是我們不可以直接將錯誤碼返回給用戶,那麼我們應該怎麼做?在前面的Spring AOP中講過,可以將所有的異常進行統一處理,但是又怎麼返回到指定界面呢?

Spring提供了多種方式將異常轉換爲響應:

  • 特定的Spring異常將會自動映射爲指定的HTTP狀態碼;

  • 異常上可以添加@ResponseStatus註解, 從而將其映射爲某一個HTTP的狀態碼

  • 在方法上可以添加@ExceptionHandler註解, 使其用來處理異常。

第一種和第二種方式是指將特定情況下的異常轉換爲HTTP狀態碼。第三種是對異常的處理。

程序四:@ResponseStatus


@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
public class MyExcetion extends RuntimeException{
}

需要注意的的是,@ResponseStatus是註解在異常類上的


當得到我們需要的異常之後,我們需要對異常進行處理,Spring MVC利用了AOP的原理,加入了@ControllerAdvice註解,此註解能夠攔截所有我們定義的異常

程序五:@ControllerAdvice + @ExceptionHandler


@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(MyExcetion.class)
    public String exception() {
        return "error/500";
    }
}

 

Spring中的過濾器

程序一:自定義Filter


/////實現Filter類,自定義Filter
public class SessionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("過濾器啓動");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("攔截到了訪問請求 ");
    }
    @Override
    public void destroy() {
        System.out.println("過濾器銷燬");
    }
}
/////web.xml中定義Filter
<filter>
   <filter-name>sessionFilter</filter-name>
   <filter-class>myfilter.SessionFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>sessionFilter</filter-name>
   <url-pattern>*</url-pattern>
</filter-mapping>

1、Filter在啓動時會被初始化,調用一次init方法,且只會初始化一次
2、按照XML定義順序進行攔截


Spring 攔截器

spring攔截器要實現的功能從名稱就看出,那就是攔截用戶的請求,功能相似於過濾器。那麼它們有什麼不同呢?

不管怎麼說,把攔截器先運行起來。在Webconfig配置文件中提供了一個addInterceptors函數來完成註冊自定義攔截器,就這麼簡單任性。

程序二:自定義攔截器


/////自定義Interceptor
@Component
public class SessionInterceptor implements HandlerInterceptor{

    public SessionInterceptor() {
        System.out.println("---SessionInterceptor---");
    }

    public boolean preHandle(HttpServletRequest request, 
HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("---preHandle----");
        return true;
    }

    public void postHandle(HttpServletRequest request, 
HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("---postHandle----");
    }

    public void afterCompletion(HttpServletRequest request, 
HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("---afterCompletion----");
    }

}

/////註冊sessionInterceptor
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(sessionInterceptor).addPathPatterns("/user/*");
}

如果你以爲Spring攔截器是仿照Filter來攔截URL那說明你太簡單了。Spring攔截器其實是利用了Aop的原理。正是因爲如此,我們才能看到上面的preHander、postHander、afterCompltion。

  • preHander:被@RequestMapping註解的方法執行前調用

  • postHander:被@RequestMapping註解的方法執行後未返回ModelView之前調用

  • afterCompltion:方法執行完成後調用

preHahder如果返回false,則postHander不執行。
多個攔截器的執行順序與註冊順序相關


現在我們再來看Spring MVC的調用順序,就一目瞭然了。先通過自定義DispatcherServlet註解啓動配置類的方式啓動Spring + MVC。實際真正起分發作用的還是org.springframework.web.servlet.DispatcherServlet.doServiet()方法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章