Spring MVC之基於java config無xml配置的web應用構建

一灰灰發表於2019-03-17

更多spring相關博文參考: spring.hhui.top

前一篇博文講了SpringMVC+web.xml的方式建立web應用,用過SpringBoot的童鞋都知道,早就沒有xml什麼事情了,其實Spring 3+, Servlet 3+的版本,就已經支援java config,不用再寫xml;本篇將介紹下,如何利用java config取代xml配置

本篇博文,建議和上一篇對比看,貼出上一篇地址

I. Web構建

1. 專案依賴

對於依賴這一塊,和前面一樣,不同的在於java config 取代 xml

<artifactId>200-mvc-annotation</artifactId>
<packaging>war</packaging>

<properties>
    <spring.version>5.1.5.RELEASE</spring.version>
</properties>

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty.aggregate</groupId>
        <artifactId>jetty-all</artifactId>
        <version>9.2.19.v20160908</version>
    </dependency>
</dependencies>

<build>
    <finalName>web-mvc</finalName>
    <plugins>
        <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>9.4.12.RC2</version>
            <configuration>
                <httpConnector>
                    <port>8080</port>
                </httpConnector>
            </configuration>
        </plugin>
    </plugins>
</build>
複製程式碼

細心的童鞋會看到,依賴中多了一個jetty-all,後面測試篇幅會說到用法

2. 專案結構

第二節依然放上專案結構,在這裡把xml的結構也截進來了,對於我們的示例demo而言,最大的區別就是沒有了webapp,更沒有webapp下面的幾個xml配置檔案

專案結構

3. 配置設定

現在沒有了配置檔案,我們的配置還是得有,不然web容器(如tomcat)怎麼找到DispatchServlet呢

a. DispatchServlet 宣告

同樣我們需要乾的第一件事情及時宣告DispatchServlet,並設定它的應用上下文;可以怎麼用呢?從官方找到教程

{% blockquote @SpringWebMvc教程 docs.spring.io/spring/docs… %}

The DispatcherServlet, as any Servlet, needs to be declared and mapped according to the Servlet specification by using Java configuration or in web.xml. In turn, the DispatcherServlet uses Spring configuration to discover the delegate components it needs for request mapping, view resolution, exception handling

{% endblockquote %}

上面的解釋,就是說下面的程式碼和web.xml的效果是一樣一樣的

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {
        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("mvc-dispatcher", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/*");
    }
}
複製程式碼

當然直接實現介面的方式有點粗暴,但是好理解,上面的程式碼和我們前面的web.xml效果一樣,建立了一個DispatchServlet, 並且繫結了url命中規則;設定了應用上下文AnnotationConfigWebApplicationContext

這個上下文,和我們前面的配置檔案mvc-dispatcher-servlet有點像了;如果有興趣看到專案原始碼的同學,會發現用的不是上面這個方式,而是及基礎介面AbstractDispatcherServletInitializer

public class MyWebApplicationInitializer extends AbstractDispatcherServletInitializer {
    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        //        applicationContext.setConfigLocation("com.git.hui.spring");
        applicationContext.register(RootConfig.class);
        applicationContext.register(WebConfig.class);
        return applicationContext;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/*"};
    }
    
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[]{new HiddenHttpMethodFilter(), new CharacterEncodingFilter()};
    }
}
複製程式碼

看到上面這段程式碼,這個感覺就和xml的方式更像了,比如Servlet應用上下文和根應用上下文

說明

上面程式碼中增加的Filter先無視,後續會有專文講什麼是Filter以及Filter可以怎麼用

b. java config

前面定義了DispatchServlet,接下來對比web.xml就是需要配置掃描並註冊bean了,本文基於JavaConfig的方式,則主要是藉助 @Configuration 註解來宣告配置類(這個可以等同於一個xml檔案)

前面的程式碼也可以看到,上下文中註冊了兩個Config類

RootConfig定義如下,注意下註解@ComponentScan,這個等同於<context:component-sca/>,指定了掃描並註冊啟用的bean的包路徑

@Configuration
@ComponentScan(value = "com.git.hui.spring")
public class RootConfig {
}
複製程式碼

另外一個WebConfig的作用則主要在於開啟WebMVC

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
}
複製程式碼

4. 例項程式碼

例項和上一篇一樣,一個普通的Server Bean和一個Controller

@Component
public class PrintServer {
    public void print() {
        System.out.println(System.currentTimeMillis());
    }
}
複製程式碼

一個提供rest服務的HelloRest

@RestController
public class HelloRest {
    @Autowired
    private PrintServer printServer;

    @GetMapping(path = "hello", produces="text/html;charset=UTF-8")
    public String sayHello(HttpServletRequest request) {
        printServer.print();
        return "hello, " + request.getParameter("name");
    }


    @GetMapping({"/", ""})
    public String index() {
        return UUID.randomUUID().toString();
    }
}
複製程式碼

5. 測試

測試依然可以和前面一樣,使用jetty來啟動,此外,介紹另外一種測試方式,也是jetty,但是不同的是我們直接寫main方法來啟動服務

public class SpringApplication {

    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);
        ServletContextHandler handler = new ServletContextHandler();

        // 伺服器根目錄,類似於tomcat部署的專案。 完整的訪問路徑為ip:port/contextPath/realRequestMapping
        //ip:port/專案路徑/api請求路徑
        handler.setContextPath("/");

        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(WebConfig.class);
        applicationContext.register(RootConfig.class);

        //相當於web.xml中配置的ContextLoaderListener
        handler.addEventListener(new ContextLoaderListener(applicationContext));

        //springmvc攔截規則 相當於web.xml中配置的DispatcherServlet
        handler.addServlet(new ServletHolder(new DispatcherServlet(applicationContext)), "/*");

        server.setHandler(handler);
        server.start();
        server.join();
    }
}
複製程式碼

測試示意圖如下

測試示意圖

6. 小結

簡單對比下xml的方式,會發現java config方式會清爽很多,不需要多個xml配置檔案,維持幾個配置類,加幾個註解即可;當然再後面的SpringBoot就更簡單了,幾個註解了事,連上面的兩個Config檔案, ServletConfig都可以省略掉

另外一個需要注意的點就是java config的執行方式,在servlet3之後才支援的,也就是說如果用比較老的jetty是起不來的(或者無法正常訪問web服務)

II. 其他

- 系列博文

web系列:

mvc應用搭建篇:

0. 專案

1. 一灰灰Blog

一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

2. 宣告

盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

3. 掃描關注

一灰灰blog

QrCode

知識星球

goals

相關文章