1. 程式人生 > 其它 >SpringMVC從入門到精通

SpringMVC從入門到精通

SpringMVC 從入門到精通

  最近暫停Spring的原始碼學習,主要是路神講的有點難,消化起來太慢了,為了知識的迅速擴張,決定開始穿插SpringMVC的學習,等SpringMVC的幾節課聽完(預計兩週)之後繼續Spring原始碼的學習,之後再學SpringBoot,tomcat。

從傳統的web專案說起。

  • 其實距離學習傳統的JavaWeb已經過去了三年多了,中間也沒有用過,只能通過idea新建的web專案來找找目錄結構。

  • 經典的MVC專案的結構圖與target展開圖如下:

      從中可以看出resources資料夾下面的檔案在打包之後直接在classes目錄下,也就是我們常說的classpath,但是webapp資料夾下的會直接進war包的WEB-INF目錄下面。(這裡webapp其實是後來通過手動方式新增成為web source directory的),我們都知道WEB-INF中的檔案只能通過服務端進行訪問,而客戶端不能夠直接對其進行訪問。

  • web.xml作為Servlet的規範也是整個Web專案的啟動器。

web.xml
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!--listener作用:在web應用啟動的時候能夠載入Spring 環境-->

    <!--<listener>-->
        <!--<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
    <!--</listener>-->
    <!--<context-param>-->
        <!--<param-name>contextConfigLocation</param-name>-->
        <!--<param-value>spring.xml</param-value>-->
    <!--</context-param>-->

    <!--<servlet>-->
        <!--<servlet-name>springmvc</servlet-name>-->
        <!--<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>-->
        <!--<init-param>-->
            <!--<param-name>contextConfigLocation</param-name>-->
            <!--<param-value>spring-mvc.xml</param-value>-->
        <!--&lt;!&ndash;</init-param>&ndash;&gt;-->
        <!--<load-on-startup>1</load-on-startup>-->
    <!--</servlet>-->


    <!--<servlet-mapping>-->
        <!--<servlet-name>springmvc</servlet-name>-->
        <!--<url-pattern>*.do</url-pattern>-->
    <!--</servlet-mapping>-->


</web-app>
  • spring-mvc.xml中一般配置一些controller的對映資訊。
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
      name:給當前控制器取的一個名字,相當於Servlet中的資源名稱,以便瀏覽器訪問,必須以斜槓/開頭
      建議使用 name屬性,不要使用id,因為早期版本 id 不支援特殊字元 如 /斜槓
     -->
    <bean name="/hello/hello.do" class="abc.moxi.mvc.controller.helloController" />
    <bean name="/hh.do" class="abc.moxi.mvc.controller.regularController"/>
</beans>

Servlet3.0規範

  • tomcat作為一個web容器,其必須實現的就是Servlet的規範。而規範中的啟動時就要從下圖的資料夾中載入類。

      裡面寫的是org.springframework.web.SpringServletContainerInitializer
SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@Override
	public void onStartup(@Nullable Set\\<Class\\<\\?\\>\\> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class\\\\\<\\\?\> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

  • 這裡就用到了這個規範。在tomcat啟動的時候會載入這個類執行onStartup方法,將@HandlerType註解中的所有實現類均載入進來執行它們的onStartup方法。

  • 那麼就可以利用這一點來去掉web.xml。

0配置原理
public class MyWebApplicationInitializer implements WebApplicationInitializer {


   
    //實現0xml
    //寫一個類 實現spring 的介面:WebApplicationInitializer


    //tomcat 啟動的時候會呼叫 onStartup方法 為什麼?

    //傳入一個ServletContext : web上下文物件   web.xml能做的 ServletContext都能做
    //因為servlet 3.0的一個新規範   為什麼不是tomcat規範而是servlet規範
    //SPI “你”
    @Override
    public void onStartup(ServletContext servletCxt) {
        //初始化spring 容器  以註解的方式
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
//        ac.setServletContext(servletCxt);
//        ac.refresh();
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
//        registration.setInitParameter("contextConfigLocation","spring mvc.xml 的地址");
        registration.addMapping("*.do");
}
}
- 0XML中也可以載入springmvc.xml來進行配置,但是既然都0XML了,Springmvc.xml怎麼能夠還存在呢?當然是在AppConfig類當中進行配置啦!!!
AppConfig
@Configuration
@ComponentScan("com")
@EnableWebMvc  // <annotation:driver>
public class AppConfig implements WebMvcConfigurer  {

    //這接口裡面的方法貫穿了所有spring mvc的配置

//    @Bean

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {

    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {

    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

    }

    @Override
    public void addFormatters(FormatterRegistry registry) {

    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {

    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

    }

    /**
     * 在這裡配置檢視解析器
     * @param registry
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/page/",".html");
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {

    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//        for (HttpMessageConverter<?> converter : converters) {
//            System.out.println(converter);
//        }
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter
                = new FastJsonHttpMessageConverter();
        converters.add(fastJsonHttpMessageConverter);
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {

    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {

    }

    @Override
    public Validator getValidator() {
        return null;
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }


//    @Bean
//    public TestController user(){
//         return  new TestController();
//    }



}