1. 程式人生 > >maven+SpringMVC搭建RESTful後端服務框架

maven+SpringMVC搭建RESTful後端服務框架

rest 頁面 pen update 靈活性 date cte requests compile

今天來嘗試一下搭建基於SpringMVC的RESTful標準的後端服務。

首先,什麽是MVC?M-model,模型,指業務數據層,具體一點就是業務邏輯與數據庫的交互;V-view,視圖,用來展示數據,傳統SpringMVC中控制器有返回類型ModelAndView,即返回含有數據模型與頁面視圖的jsp文件;C-controller,控制器,通常負責處理與用戶間的交互,控制從數據庫取數與返回結果到用戶等。

那麽,什麽是REST風格的服務呢?REST(Representational State Transfer),中文可以叫表述性狀態轉移,有以下幾個特點

  • Resources expose easily understood directory structure URIs.資源通過URI結構直接對外展示
  • Representations transfer JSON or XML to represent data objects and attributes.通過JSON或者XML來傳遞數據對象或者屬性
  • Messages use HTTP methods explicitly (for example, GET, POST, PUT, and DELETE).信息通過HTTP請求傳遞
  • Stateless interactions store no client context on the server between requests. State dependencies limit and restrict scalability. The client holds session state.無狀態的交互,服務器在交互過程中不存儲任何客戶端有關的上下文。session的狀態通過客戶端來存儲。

簡單來說,傳統的MVC框架,後端和前端是部署在一起的,服務器接受請求,去數據庫(緩存實質也是非關系型的鍵值對數據庫)查詢然後計算,然後和jsp進行組裝渲染,直接返回一個完整的html給瀏覽器。而RESTful的結構下,前端和後端是可以分離部署的,瀏覽器訪問一個頁面,前端根據需要向後端發送HTTP請求,後端接收請求然後同樣查詢數據庫再計算,把結果封裝為JSON報文後返回給前端,前端把數據組裝到靜態頁面中,生成最終展示給用戶的頁面。

從個人在開發中的理解,RESTful相比之下有以下幾個優點:

  1. 前後端分離解耦合,尤其是在現在前端一天一個樣,動不動就啥啥啥的已經不適合這個版本了的背景下,後端需要輸出一個標準化的結果,給前端開發留下足夠的靈活性。你要什麽,我給什麽,這是面向服務的設計方式,在企業開發中這樣子的靈活性較好,不容易造成前後端相互之間成為開發瓶頸。
  2. 部署與性能上的可擴展性更好。我們都知道在用戶量增大的時候,最容易產生壓力的資源在數據庫,而前端服務器是較容易通過負載均衡增加機器的方式來解決壓力問題。所以,從後端到數據庫所需的資源數量和前端是不同的,前後端的分離部署與面向服務的分布式系統對於提升系統承載並發量是有利的。同時,要擴展系統功能時,可以直接通過新增一個相關服務的服務器或集群,讓前端新增該請求,不需要修改其他後端服務器。
  3. 開發測試更加方便。更加便於模塊的劃分,容易進行接口測試,不同模塊之間的耦合度非常低。

下面正式開始環境的搭建,因為主要是講環境搭建,一下配置內容分析先不寫了,留到深入研究spring之後

首先編輯pom.xml,把需要的jar包先弄進來,如果沒有自動下載的話,手動在工程項目上右鍵maven->update project,註意要把forcre那行勾上。

技術分享圖片

列表可以參考下面這個,這裏建議spring這樣一個系列的包的版本號統一在最上方以參數寫入,便於集體版本替換,這點在企業開發中也是非常常用的。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.graywind</groupId>
    <artifactId>webdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <!-- springframe 版本控制 -->
        <spring.version>4.1.6.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- springframe start -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</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-oxm</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- springframe end -->

        <!--log4j日誌包 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!-- JUnit單元測試工具 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <!-- aspectJ AOP 織入器 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <!-- jstl -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- JSON: jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.jr</groupId>
            <artifactId>jackson-jr-all</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.46</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- define the project compile level -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

然後修改些項目屬性,右鍵項目properties->prject facts確定設置如下。若web module不是3.0可把勾去掉,應用後再修改到3.0.

技術分享圖片

然後到deployment assembly,設置如下

技術分享圖片

修改web.xml如下,這裏有兩個配置文件的路徑是以classpath開頭,根據上方的設置classpath會從WEB-INF/classes開始尋找,而src/main/java和src/main/resources下的內容會被裝配至該路徑下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>mybatis</display-name>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- springmvc配置 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <description>spring mvc 配置文件</description>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <filter>
        <description>字符集過濾器</description>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <description>字符集編碼</description>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--設置log4j的配置文件位置 -->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:log4j.properties</param-value>
    </context-param>
    <!--使用監聽加載log4j的配置文件 -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
</web-app>

現在添加application.xml和springmvc.xml以及log4j.properties至resources目錄下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">


    <!-- 啟用註解掃描,並定義組件查找規則 ,除了@controller,掃描所有的Bean -->
    <context:component-scan base-package="webdemo.*">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

    <!-- 啟用註解掃描,並定義組件查找規則 ,mvc層只負責掃描@Controller、@ControllerAdvice -->
    <!-- base-package 如果多個,用“,”分隔 -->
    <context:component-scan base-package="webdemo.*"
        use-default-filters="false">
        <!-- 掃描@Controller -->
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
        <!-- 控制器增強,使一個Contoller成為全局的異常處理類,類中用@ExceptionHandler方法註解的方法可以處理所有Controller發生的異常 -->
        <context:include-filter type="annotation"
            expression="org.springframework.web.bind.annotation.ControllerAdvice" />
    </context:component-scan>

    <!-- 會自動註冊RequestMappingHandlerMapping與RequestMappingHandlerAdapter兩個Bean, 
        這是SpringMVC為@Controllers分發請求所必需的,並提供了數據綁定支持、@NumberFormatannotation支持、 @DateTimeFormat支持、@Valid支持、讀寫XML的支持和讀寫JSON的支持等功能。 -->
    <mvc:annotation-driven />


    <!-- 使用默認的Servlet來響應靜態文件 -->
    <mvc:default-servlet-handler />


    <!-- 視圖解析器 -->
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前綴 -->
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <!-- 後綴 -->
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

    <bean id="stringConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/plain;charset=UTF-8</value>
            </list>
        </property>
    </bean>

    <!-- 輸出對象轉JSON支持 -->
    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean
                    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/html;charset=UTF-8</value>
                            <value>text/plain;charset=UTF-8</value>
                            <value>application/json;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
</beans>
### set log levels ###
log4j.rootLogger = debug,stdout,D,E

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ${catalina.home}/logs/webdemo-info.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = INFO
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =${catalina.home}/logs/webdemo-error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
${catalina.home}表示日誌文件會產生到tomcat的文件目錄下
然後添加controller與bean
package webdemo.hello;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class UserController {
    
    private static final Logger log = Logger.getLogger(UserController.class);

    @RequestMapping("/hello")
    public String hello(){        
        return "hello";
    }
    
    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = "application/json")
    public User getUser(@PathVariable("id") int id){        
        log.info("get user " + id);
        User user = new User();
        user.setId(id);
        user.setName("Hello");
        return user;
    }
    
    @RequestMapping(value = "/userlist", method = RequestMethod.GET, produces = "application/json")
    public List<User> getUserlist(){        
        System.out.println("get userlist");
        List<User> list = new ArrayList<User>();
        for(int i=1;i<10;i++){
            User user = new User();
            user.setId(i);
            user.setName("Hello");
            list.add(user);
        }
        return list;
    }
}
package webdemo.hello;

public class User {
    private String name;
    private int id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

現在啟動服務器,測試下接口,因為我把server設置那裏的path去掉了,所以直接輸入IP與端口

技術分享圖片技術分享圖片

現在網頁上顯示的就是json報文了,基本的環境也就搭建完成了。但是以網站開發角度來說,後續還有很多需要擴展的地方,比如權限控制過濾器,ajax報文的組裝,錯誤控制,日誌切面等等,後續會進一步在這個基礎上擴展。

maven+SpringMVC搭建RESTful後端服務框架