基於Backbone.js的JavaScript MVC示例程序(2)


二.REST Server的實現

Server端使用Java來實現,用到了Spring、Mybatis、c3p0、Jersey等技術。

代碼結構如下圖所示:

2.1 REST API設計

根據系統的功能設計了如下一些REST API:

功能

Method

URL

獲取User信息列表

GET

/rest/user

獲取某個User的詳細信息

GET

/rest/user/[id]

添加一個User

POST

/rest/user

修改某個User的信息

PUT

/rest/user/[id]

刪除某個User

DELETE

/rest/user/[id]

驗證Username是否合法

GET

/rest/user/validate/[username]

其中POST請求的JSON中沒有User的id,因為在數據庫中這是自增的字段,所以在insert成功之后,需要將id設置上之后再返回整個JSON對象,方便前端更新數據。

最后一個方法是用來檢測用戶名是否已經被注冊的,返回true或者false,用來進行表單驗證。

2.2 數據庫設計

數據庫使用的是SQLServer,只有一張表,字段也比較少,創建數據表的SQL如下:

CREATE TABLE [dbo].[rd_user](
  [id] [int] IDENTITY(1,1) NOT NULL,
  [username] [varchar](50) NOT NULL,
  [password] [varchar](50) NOT NULL,
  [email] [varchar](50) NOT NULL,
  [phone] [varchar](50) NULL
) ON [PRIMARY];

2.3 用MyBatis實現的DAO層

連接池采用了c3p0,在Spring中的配置如下:

 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}"/>            
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="minPoolSize" value="5"></property>
        <property name="maxPoolSize" value="30"></property>
        <property name="initialPoolSize" value="5"></property>
        <property name="maxIdleTime" value="60"></property>
        <property name="acquireIncrement" value="5"></property>
</bean>

在Spring中配置MyBatis:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
</bean>

在Spring中注冊DAO的bean:

<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.demo.register.dao.mybatis.MyBatisUserDao"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

User的POJO:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
    private int id;
    private String username;
    private String password;
    private String email;
    private String phone;
    //省略getter setter
}

IUserDao接口:

public interface IUserDao {
    public User getUserById(int id);
    public User getUserByUsername(String username);
    public List<User> getUserList();
    public void insert(User user);
    public void update(User user);
    public void deleteUserById(int id);
}

MyBatisUserDao接口,繼承自IUserDao,使用Mybatis的annotation實現增刪改查功能,但是有些復雜的查詢必須使用XML。

public interface MyBatisUserDao extends IUserDao {

    @Select("SELECT * FROM rd_user WHERE id = #{id}")
    public User getUserById(@Param("id") int id);
   
    @Select("SELECT * FROM rd_user WHERE username = #{username}")
    public User getUserByUsername(@Param("username") String username);
   
    @Select("SELECT * FROM rd_user order by id")
    public List<User> getUserList();
   
    @Insert("INSERT INTO rd_user(username, password, phone, email) " +
            "VALUES(#{username}, #{password}, #{phone}, #{email})")
//Insert成功之后會將id更新到User的對象 @SelectKey(statement
="SELECT @@IDENTITY", keyProperty="id", before=false, resultType=int.class) public void insert(User user); @Update("UPDATE rd_user " + "SET username=#{username}, password=#{password}, phone=#{phone}, email=#{email} " + "WHERE id=#{id}") public void update(User user); @Delete("DELETE FROM rd_user WHERE id = #{id}") public void deleteUserById(@Param("id") int id); }

2.4 用Jersey實現的REST API

在Web.xml中整合Jersey和Spring:

 <servlet>
        <servlet-name>jersey-serlvet</servlet-name>
        <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>com.demo.register.rest</param-value>
        </init-param>
</servlet>
<servlet-mapping>
        <servlet-name>jersey-serlvet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

關於Jersey的使用參看官網的文檔:http://jersey.java.net/nonav/documentation/latest/user-guide.html

UserAPI的實現類:

package com.demo.register.rest;

import java.util.List;
 
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

 
import com.demo.register.bean.User;
import com.demo.register.dao.IUserDao;

@Path("/user")
public class UserAPI {
    private IUserDao userDao;
   
    public IUserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<User> getUserList() {
        return userDao.getUserList();
    }
   
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public User addUser(User user) {
        userDao.insert(user);
        return user;
    }
   
    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User getUser(@PathParam("id") int id) {
        return userDao.getUserById(id);
    }

    @PUT
    @Path("/{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String updateUser(User user) {
        userDao.update(user);
        return "{\"success\":\"true\"}"; //這里如果不加雙引號,前端的JS不會將它識別為JSON,而且會產生error事件
    }

    @DELETE
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public String deleteUser(@PathParam("id") int id) {
        userDao.deleteUserById(id);
        return "{\"success\":\"true\"}";
    }

    @GET
    @Path("/validate/{username}")
    @Produces(MediaType.TEXT_PLAIN)
    public String validate(@PathParam("username") String username) {
        return userDao.getUserByUsername(username) == null ? "true" : "false";
    }
}

當然還需要在Spring中給UserAPI加上對UserDao的依賴,我試了一下這個userDao不能自動注入,我覺得是因為注冊bean的class是MyBatis里面的類而不是IUserDao的子類,或許還有我不知道的方法:

<bean id="userAPI" class="com.demo.register.rest.UserAPI">
        <property name="userDao" ref="userDao"/>
</bean>

2.5 用Spring AOP實現的日志功能

使用Spring AOP對所有的REST API相關方法輸出日志。首先需要一個日志類,然后再將這個日志類編織到REST API的相關方法。使用這樣的方法就不需要在每個REST API的方法中各自輸出日志了。

日志的實現類:

package com.demo.register.log;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;

public class RestLogger {
    //為每個類創建一個Log private static final Map<Class<?>, Log> LOG_MAP = new HashMap<Class<?>, Log>();
    
    private Log getLogger(Class<?> clazz) {
        Log log = (Log) LOG_MAP.get(clazz);
        if (log == null) {
            log = LogFactory.getLog(clazz);
            LOG_MAP.put(clazz, log);
        }
        return log;
    }
    
    public Object profileMethod(ProceedingJoinPoint call) throws Throwable {
        Log log = getLogger(call.getTarget().getClass());

        if (log.isDebugEnabled()) {
            log.debug("method call: " + call.getSignature().toString()); //輸出調用的方法
            log.debug("method parameter: " //輸出參數
                    + ReflectionToStringBuilder.toString(call.getArgs(),
                        ToStringStyle.SHORT_PREFIX_STYLE, true));
            try {
                Object rt = call.proceed(); //調用原方法

                log.debug("method return: " //輸出方法的返回值
                        + ReflectionToStringBuilder.toString(rt,
                                ToStringStyle.SHORT_PREFIX_STYLE, true));

                return rt;
            } catch (Throwable e) {
                log.error("method exception: " + e.getMessage(), e);
                throw e;
            } 
        } else {
            return call.proceed();
        }
    }

}

在Spring中配置AOP

    <bean id="restLogger" class="com.demo.register.log.RestLogger" ></bean>

    <aop:config>
        <aop:aspect ref="restLogger">
            <aop:pointcut id="restMethod" expression="execution(* com.demo.register.rest.*.*(..))" />
            <aop:around pointcut-ref="restMethod" method="profileMethod" />
        </aop:aspect>
    </aop:config>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM