近期完成了一个小项目,代码量虽然较少,但是用到了一些之前没用过的东西,在此整合记录一下。
项目地址:https://github.com/FateSolo/SSM-Test
项目环境:
Ubuntu 14.04.3
Intellij IDEA 15.0.2
Gradle 2.5
JDK 1.6.0_38
Tomcat 7.0.67
Nginx 1.4.6
应用技术:
Spring 4.2.4
Spring MVC 4.2.4
Mybatis 3.3.0
Druid 1.0.9
Ehcache 2.6.9
Gson 2.5
Dom4j 1.6.1
Poi 3.9
Tomcat 7.0.47 (WebSocket)
1. 创建项目: 1) 打开IDEA,点击Create New Project,在左侧边栏找到Gradle,并勾选Java和Web,JDK选择1.6.0_38。
2) 点击Next后,GroupId可以填写为包名,这里我写为com.fatesolo;ArtifactId可以填写为项目名,这里我写为SSM-Test;Version不需要变动。
3) 点击Next后,勾选Use auto-import和Create directories for…,Gradle选择2.5,JDK选择1.6.0_38。
4) 点击Next后,直接Finish即完成了项目的创建。
2. 基础配置 1) 点击File -> Project Structure,选择Modules,在SSM-Test下选择Web Gradle…,如图:
2) 点击Deployment Descriptors栏右侧的绿色加号,点击web.xml,在弹出的对话框中,在SSM-Test与WEB-INF路径之间添加src/main/webapp/,点击确定即完成了web.xml的创建。
3) 在src/main/java中创建com.fatesolo,并创建controller, service, mapper, model, filter, socket, util七个子包。
4) 在src/main/resources中创建mapper文件夹,该文件夹主要存放实现数据访问层具体逻辑的xml文件,一般一个xml对应一个mapper子包中的接口。至此,基础配置完成,项目目录结构如图所示:
3. 编写web.xml 1) 打开web.xml,编写如下配置:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://java.sun.com/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version ="3.0" > <context-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:spring-common.xml</param-value > </context-param > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener</listener-class > </listener > <servlet > <servlet-name > springMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:spring-mvc.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 > <servlet > <servlet-name > socket</servlet-name > <servlet-class > com.fatesolo.socket.MyWebSocketServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > socket</servlet-name > <url-pattern > /socket</url-pattern > </servlet-mapping > <session-config > <session-timeout > 120</session-timeout > </session-config > <filter > <filter-name > sessionFilter</filter-name > <filter-class > com.fatesolo.filter.SessionFilter</filter-class > </filter > <filter-mapping > <filter-name > sessionFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > <filter > <filter-name > encodingFilter</filter-name > <filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class > <init-param > <param-name > encoding</param-name > <param-value > UTF-8</param-value > </init-param > <init-param > <param-name > forceEncoding</param-name > <param-value > true</param-value > </init-param > </filter > <filter-mapping > <filter-name > encodingFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > </web-app >
2) 导入第三方库,在build.gradle的dependencies中,加入如下两句话:1 compile 'org.springframework:spring-web:4.2.4.RELEASE'
compile 'org.springframework:spring-webmvc:4.2.4.RELEASE'
3) 在socket包中创建MyWebSocketServlet类,在filter包中创建SessionFilter类,在resources文件夹内新建spring-common.xml和spring-mvc.xml。
4. 编写相关配置文件 1) 创建并编写jdbc.properties文件,代码如下:1 jdbc.url=jdbc:mysql://localhost:3306/TestDB?useUnicode=true&characterEncoding=utf8&mysqlEncoding=utf8
jdbc.username=root
jdbc.password=123456
druid.initialSize=5
druid.minIdle=5
druid.maxActive=50
注意数据库名、用户名和密码都更改成你自己的,下面的Druid配置可以根据需要自行修改。
2) 创建并编写log4j.properties文件,代码如下:1 log4j.rootLogger=INFO, CONSOLE, FILE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=../logs/log4j.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.Append=true
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
3) 创建并编写ehcache.xml文件,代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="http://ehcache.org/ehcache.xsd" > <diskStore path ="java.io.tmpdir" /> <defaultCache maxElementsInMemory ="100" eternal ="false" timeToIdleSeconds ="30" timeToLiveSeconds ="30" overflowToDisk ="false" /> <cache name ="listCache" maxElementsInMemory ="10" eternal ="true" overflowToDisk ="false" /> <cache name ="urlCache" maxElementsInMemory ="10" eternal ="false" timeToIdleSeconds ="0" timeToLiveSeconds ="14400" overflowToDisk ="false" /> </ehcache >
4) 打开spring-mvc.xml,编写如下配置:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd" > <context:component-scan base-package ="com.fatesolo.controller" /> <mvc:annotation-driven /> </beans >
5) 打开spring-common.xml,编写如下配置:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 <?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:tx ="http://www.springframework.org/schema/tx" xmlns:cache ="http://www.springframework.org/schema/cache" 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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd" > <context:component-scan base-package ="com.fatesolo.service" /> <context:property-placeholder location ="classpath:jdbc.properties" /> <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" init-method ="init" destroy-method ="close" > <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> <property name ="initialSize" value ="${druid.initialSize}" /> <property name ="minIdle" value ="${druid.minIdle}" /> <property name ="maxActive" value ="${druid.maxActive}" /> <property name ="maxWait" value ="60000" /> <property name ="timeBetweenEvictionRunsMillis" value ="60000" /> <property name ="minEvictableIdleTimeMillis" value ="300000" /> <property name ="validationQuery" value ="SELECT 'x'" /> <property name ="testWhileIdle" value ="true" /> <property name ="testOnBorrow" value ="false" /> <property name ="testOnReturn" value ="false" /> <property name ="poolPreparedStatements" value ="false" /> </bean > <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="typeAliasesPackage" value ="com.fatesolo.model" /> <property name ="mapperLocations" value ="classpath:mapper/*Mapper.xml" /> </bean > <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="basePackage" value ="com.fatesolo.mapper" /> <property name ="sqlSessionFactoryBeanName" value ="sqlSessionFactory" /> </bean > <bean id ="txManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean > <tx:annotation-driven transaction-manager ="txManager" /> <bean id ="cacheManagerFactory" class ="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" > <property name ="configLocation" value ="classpath:ehcache.xml" /> </bean > <bean id ="cacheManager" class ="org.springframework.cache.ehcache.EhCacheCacheManager" > <property name ="cacheManager" ref ="cacheManagerFactory" /> </bean > <cache:annotation-driven cache-manager ="cacheManager" /> </beans >
6) 此时发现IDEA给出了如下图提示: 点击Create Spring facet,创建成如下图样式并确定。
7) 发现缺少相关class,再打开build.gradle,加入如下依赖:1 compile 'com.alibaba:druid:1.0.9'
compile 'net.sf.ehcache:ehcache-core:2.6.9'
compile 'org.mybatis:mybatis:3.3.0'
compile 'org.mybatis:mybatis-spring:1.2.3'
compile 'org.springframework:spring-context-support:4.2.4.RELEASE'
compile 'org.springframework:spring-jdbc:4.2.4.RELEASE'
至此,Spring、SpringMVC、Mybatis、Druid、Ehcache已经配置并整合完毕。
5. 实现具体功能 实现这样一个功能:用户打开网页后可以观看腾讯视频,可以看到最新的十条留言,可以自行留言,当其他人留言时可以实时更新;admin可以登录到后台数据管理系统,可以查看所有留言,可以下载为Excel格式。
1) 首先在MySQL数据库中创建数据库与表,并编写对应的实体类,这里只给出实体类的代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.fatesolo.model;public class Message { private long id; private String name; private String message; private String createTime; public long getId () { return id; } public void setId (long id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getMessage () { return message; } public void setMessage (String message) { this .message = message; } public String getCreateTime () { return createTime; } public void setCreateTime (String createTime) { this .createTime = createTime; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.fatesolo.model;public class User { private int id; private String username; private String password; public int getId () { return id; } public void setId (int id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getPassword () { return password; } public void setPassword (String password) { this .password = password; } }
2) 创建数据访问接口1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.fatesolo.mapper;import com.fatesolo.model.Message;import org.apache.ibatis.annotations.Param;import java.util.List;public interface MessageMapper { void add (Message message) ; int getCount () ; * @param m : 从第m+1条数据开始获取 * @param n : 获取n条数据 */ List<Message> getList (@Param(value="m" ) int m, @Param (value="n" ) int n) ; }
1 2 3 4 5 6 7 8 9 package com.fatesolo.mapper;import com.fatesolo.model.User;public interface UserMapper { User getUser (String username) ; }
3) 在src/main/resources/mapper中创建对应的xml文件1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace ="com.fatesolo.mapper.MessageMapper" > <insert id ="add" parameterType ="Message" useGeneratedKeys ="true" keyProperty ="id" > insert into Message(name, message, createTime) values(#{name}, #{message}, #{createTime}) </insert > <select id ="getCount" resultType ="int" > select count(1) from Message </select > <select id ="getList" resultType ="Message" > select id, name, message, FROM_UNIXTIME(createTime/1000, '%Y-%m-%d %H:%i:%s') createTime from Message order by createTime desc limit #{m}, #{n} </select > </mapper >
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace ="com.fatesolo.mapper.UserMapper" > <select id ="getUser" parameterType ="java.lang.String" resultType ="User" > select * from User where username = #{username} </select > </mapper >
4) 编写工具类1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.fatesolo.util;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.TimeZone;public class DateTimeUtil { public static String getCurrentTimeMillis () { TimeZone.setDefault(TimeZone.getTimeZone("GMT+8" )); return String.valueOf(System.currentTimeMillis()); } public static String millisToDateTime (String millis) { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(Long.parseLong(millis)); return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" ).format(calendar.getTime()); } public static String millisToDate (String millis) { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(Long.parseLong(millis)); return new SimpleDateFormat("yyyy-MM-dd" ).format(calendar.getTime()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 package com.fatesolo.util;import com.fatesolo.model.Message;import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.ss.usermodel.Cell;import org.apache.poi.ss.usermodel.CellStyle;import org.apache.poi.ss.usermodel.Font;import org.apache.poi.ss.usermodel.IndexedColors;import org.apache.poi.ss.usermodel.Row;import org.apache.poi.ss.usermodel.Sheet;import org.apache.poi.ss.usermodel.Workbook;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class ExcelUtil { public static Workbook createWorkBook (List<Map<String, Object>> list, short [] width, String[] keys, String columnNames[]) { Workbook wb = new HSSFWorkbook(); Sheet sheet = wb.createSheet(list.get(0 ).get("sheetName" ).toString()); for (int i = 0 ; i < keys.length; i++) { sheet.setColumnWidth((short ) i, width[i] * 150 ); } Row row = sheet.createRow((short ) 0 ); CellStyle cs = wb.createCellStyle(); CellStyle cs2 = wb.createCellStyle(); Font f = wb.createFont(); Font f2 = wb.createFont(); f.setFontHeightInPoints((short ) 10 ); f.setColor(IndexedColors.BLACK.getIndex()); f.setBoldweight(Font.BOLDWEIGHT_BOLD); f2.setFontHeightInPoints((short ) 10 ); f2.setColor(IndexedColors.BLACK.getIndex()); cs.setFont(f); cs.setBorderLeft(CellStyle.BORDER_THIN); cs.setBorderRight(CellStyle.BORDER_THIN); cs.setBorderTop(CellStyle.BORDER_THIN); cs.setBorderBottom(CellStyle.BORDER_THIN); cs.setAlignment(CellStyle.ALIGN_CENTER); cs2.setFont(f2); cs2.setBorderLeft(CellStyle.BORDER_THIN); cs2.setBorderRight(CellStyle.BORDER_THIN); cs2.setBorderTop(CellStyle.BORDER_THIN); cs2.setBorderBottom(CellStyle.BORDER_THIN); cs2.setAlignment(CellStyle.ALIGN_CENTER); for (int i = 0 ; i < columnNames.length; i++) { Cell cell = row.createCell(i); cell.setCellValue(columnNames[i]); cell.setCellStyle(cs); } for (short i = 1 ; i < list.size(); i++) { Row row1 = sheet.createRow(i); for (short j = 0 ; j < keys.length; j++) { Cell cell = row1.createCell(j); cell.setCellValue(list.get(i).get(keys[j]) == null ? " " : list.get(i).get(keys[j]).toString()); cell.setCellStyle(cs2); } } return wb; } public static List<Map<String, Object>> createExcelRecord(List<Message> messages) { List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); Map<String, Object> map = new HashMap<String, Object>(); map.put("sheetName" , "sheet1" ); list.add(map); for (Message message : messages) { Map<String, Object> mapValue = new HashMap<String, Object>(); mapValue.put("id" , message.getId()); mapValue.put("name" , message.getName()); mapValue.put("message" , message.getMessage()); mapValue.put("createTime" , message.getCreateTime()); list.add(mapValue); } return list; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.fatesolo.util;import org.dom4j.Document;import org.dom4j.DocumentHelper;import java.io.BufferedReader;import java.io.InputStreamReader;import java.net.URL;import java.net.URLConnection;public class HttpUtil { public static String httpRequest (String url) throws Exception { StringBuilder response = new StringBuilder(); String line; URL realUrl = new URL(url); URLConnection connection = realUrl.openConnection(); connection.connect(); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); while ((line = in.readLine()) != null ) { response.append(line); } in.close(); return response.toString(); } public static String getVideoUrl () { String response = "" ; try { response = HttpUtil.httpRequest("http://vv.video.qq.com/geturl?vid=f0019r1sytg" ); Document document = DocumentHelper.parseText(response); response = document.getRootElement().element("vd" ).element("vi" ).element("url" ).getText(); } catch (Exception e) { e.printStackTrace(); } return response; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.fatesolo.util;import java.security.MessageDigest;public class MD5Util { public static byte [] encode2bytes(String source) { byte [] result = null ; try { MessageDigest md = MessageDigest.getInstance("MD5" ); md.reset(); md.update(source.getBytes("UTF-8" )); result = md.digest(); } catch (Exception e) { e.printStackTrace(); } return result; } public static String encode2hex (String source) { byte [] data = encode2bytes(source); StringBuilder hexString = new StringBuilder(); for (byte aData : data) { String hex = Integer.toHexString(0xff & aData); if (hex.length() == 1 ) { hexString.append('0' ); } hexString.append(hex); } return hexString.toString(); } * @param unknown : 明文字符串 * @param okHex : 加密后的32位字符串 */ public static boolean validate (String unknown, String okHex) { return okHex.equals(encode2hex(unknown)); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.fatesolo.util;import java.util.List;public class QuickPager <T > { private int currPage = 1 ; private int pageSize = 10 ; private int totalRows = 0 ; private int totalPages = 0 ; private List<T> list = null ; public QuickPager (int currPage, int pageSize) { this .currPage = currPage; this .pageSize = pageSize; } public QuickPager (String currPage, String pageSize) { if (currPage != null && !currPage.equals("" )) { this .currPage = Integer.parseInt(currPage); } if (pageSize != null && !pageSize.equals("" )) { this .pageSize = Integer.parseInt(pageSize); } } public int getCurrPage () { return currPage; } public void setCurrPage (int currPage) { this .currPage = currPage; } public int getPageSize () { return pageSize; } public void setPageSize (int pageSize) { this .pageSize = pageSize; } public int getTotalRows () { return totalRows; } public void setTotalRows (int totalRows) { this .totalRows = totalRows; this .setTotalPages(this .totalRows == 0 ? 0 : (this .totalRows % this .pageSize != 0 ? this .totalRows / this .pageSize + 1 : this .totalRows / this .pageSize)); } public int getTotalPages () { return totalPages; } private void setTotalPages (int totalPages) { this .totalPages = totalPages; } public List<T> getList () { return list; } public void setList (List<T> list) { this .list = list; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.fatesolo.util;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import java.util.HashMap;import java.util.Map;public class Response { public final static String PATH = "/message/" ; public final static String SUCCESS = "000000" ; public final static String FAILURE = "100001" ; public static String success (String msg) { return "{\"resCode\":\"" + SUCCESS + "\",\"resMsg\":\"" + msg + "\",\"data\":{}}" ; } public static String failure (String msg) { return "{\"resCode\":\"" + FAILURE + "\",\"resMsg\":\"" + msg + "\",\"data\":{}}" ; } public static String toJson (Object obj) { Map<String, Object> body = new HashMap<String, Object>(); body.put("resCode" , SUCCESS); body.put("resMsg" , "成功" ); body.put("data" , obj); Gson gson = new GsonBuilder().disableHtmlEscaping().create(); return gson.toJson(body); } }
对于Response.PATH,在Nginx中对应的配置如下:1 server {
listen 80;
server_name localhost;
location ^~ /message/ {
root /etc/nginx/html/static;
index index.html index.htm;
access_log off;
expires 5d;
}
location /messageweb/ {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
第一个location对应静态资源,路径应与Response.PATH一致,root可自行修改,第二个location是转发配置,路径应与项目在tomcat中的名称一致。
引入相关依赖:1 compile 'com.google.code.gson:gson:2.5'
compile 'dom4j:dom4j:1.6.1'
compile 'org.apache.poi:poi:3.9'
5) 创建Service1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.fatesolo.service;import com.fatesolo.mapper.MessageMapper;import com.fatesolo.model.Message;import com.fatesolo.util.DateTimeUtil;import com.fatesolo.util.HttpUtil;import com.fatesolo.util.QuickPager;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;import java.util.List;@Service @Transactional (rollbackFor = Exception.class)public class MessageService { @Resource private MessageMapper mapper; public boolean add (Message message) { message.setCreateTime(DateTimeUtil.getCurrentTimeMillis()); mapper.add(message); message.setCreateTime(DateTimeUtil.millisToDateTime(message.getCreateTime())); return message.getId() != 0 ; } @Cacheable (value = "listCache" ) public List<Message> getList () { return mapper.getList(0 , 10 ); } @CacheEvict (value = "listCache" , allEntries = true ) public void removeList () { } @Cacheable (value = "urlCache" ) public String getUrl () { return HttpUtil.getVideoUrl(); } @CacheEvict (value = "urlCache" , allEntries = true ) public void removeUrl () { } public QuickPager<Message> getQuickPager (String currPage, String pageSize) { QuickPager<Message> pager = new QuickPager<Message>(currPage, pageSize); pager.setTotalRows(mapper.getCount()); pager.setList(mapper.getList((pager.getCurrPage() - 1 ) * pager.getPageSize(), pager.getPageSize())); return pager; } public List<Message> getAll () { return mapper.getList(0 , mapper.getCount()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.fatesolo.service;import com.fatesolo.mapper.UserMapper;import com.fatesolo.model.User;import com.fatesolo.util.MD5Util;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service @Transactional (rollbackFor = Exception.class)public class LoginService { @Resource private UserMapper mapper; public boolean login (String username, String password) { User user = mapper.getUser(username); return user != null && MD5Util.validate(password, user.getPassword()); } }
6) 编写Web Socket1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.fatesolo.socket;import org.apache.catalina.websocket.StreamInbound;import org.apache.catalina.websocket.WebSocketServlet;import javax.servlet.http.HttpServletRequest;public class MyWebSocketServlet extends WebSocketServlet { @Override protected StreamInbound createWebSocketInbound (String subProtocol, HttpServletRequest request) { return new MyMessageInbound(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.fatesolo.socket;import org.apache.catalina.websocket.MessageInbound;import org.apache.catalina.websocket.WsOutbound;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.CharBuffer;public class MyMessageInbound extends MessageInbound { @Override protected void onClose (int status) { WebSocketManager.remove(this ); super .onClose(status); } @Override protected void onOpen (WsOutbound outbound) { super .onOpen(outbound); WebSocketManager.add(this ); } @Override protected void onBinaryMessage (ByteBuffer message) throws IOException { } @Override protected void onTextMessage (CharBuffer message) throws IOException { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.fatesolo.socket;import org.apache.catalina.websocket.MessageInbound;import org.apache.catalina.websocket.WsOutbound;import java.nio.CharBuffer;import java.util.concurrent.CopyOnWriteArrayList;public class WebSocketManager { private static CopyOnWriteArrayList<MessageInbound> clients = new CopyOnWriteArrayList<MessageInbound>(); public static void add (MessageInbound client) { clients.add(client); } public static void remove (MessageInbound client) { clients.remove(client); } public static int getSize () { return clients.size(); } public static void sendMessage (String message) { for (MessageInbound client : clients) { try { CharBuffer buffer = CharBuffer.wrap(message); WsOutbound outbound = client.getWsOutbound(); outbound.writeTextMessage(buffer); outbound.flush(); } catch (Exception e) { remove(client); } } } }
引入相关依赖:1 compile 'org.apache.tomcat:tomcat-catalina:7.0.47'
compile 'org.apache.tomcat:tomcat-coyote:7.0.47'
这是Tomcat的实现,现在已经不推荐使用。但因为项目需要,使用了JDK 1.6,而推荐的JSR-356标准的WebSocekt至少需要JDK 1.7。当你使用了JDK 1.7及以上时,可以使用Spring提供的WebSocket,那是基于JSR-356标准的实现。
另外,因为使用了Nginx进行反向代理,所以需要在相应配置文件中加入如下配置,令Nginx支持对socket的转发:1 location /messageweb/socket {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
7) 创建Controller1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.fatesolo.controller;import com.fatesolo.model.Message;import com.fatesolo.service.MessageService;import com.fatesolo.socket.WebSocketManager;import com.fatesolo.util.Response;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import java.util.HashMap;import java.util.Map;@RestController @RequestMapping (value = "/message" , produces = "application/json;charset=UTF-8" )public class MessageController { @Resource private MessageService service; @RequestMapping (value = "" , method = RequestMethod.POST) public String add (Message message) { if (service.add(message)) { service.removeList(); WebSocketManager.sendMessage(Response.toJson(message)); return Response.success("AddSuccess" ); } else { return Response.failure("AddFailure" ); } } @RequestMapping (value = "" , method = RequestMethod.GET) public String getList () { Map<String, Object> map = new HashMap<String, Object>(); map.put("list" , service.getList()); map.put("url" , service.getUrl()); return Response.toJson(map); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 package com.fatesolo.controller;import com.fatesolo.model.User;import com.fatesolo.service.LoginService;import com.fatesolo.service.MessageService;import com.fatesolo.socket.WebSocketManager;import com.fatesolo.util.DateTimeUtil;import com.fatesolo.util.ExcelUtil;import com.fatesolo.util.Response;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.*;import java.util.List;import java.util.Map;@Controller @RequestMapping (value = "/admin" , produces = "application/json;charset=UTF-8" )public class AdminController { @Resource private LoginService service; @Resource private MessageService service2; @RequestMapping (value = "/login" , method = RequestMethod.POST) public void login (HttpServletRequest request, HttpServletResponse response, User user) throws IOException { if (service.login(user.getUsername(), user.getPassword())) { HttpSession session = request.getSession(); session.setAttribute("user" , user); response.sendRedirect(Response.PATH + "dashboard.html" ); } else { response.sendRedirect(Response.PATH + "login.html?error=1" ); } } @RequestMapping (value = "/message" , method = RequestMethod.GET) @ResponseBody public String getList (@RequestParam(value = "currPage" , required = false ) String currPage, @RequestParam (value = "pageSize" , required = false ) String pageSize) { return Response.toJson(service2.getQuickPager(currPage, pageSize)); } @RequestMapping (value = "/download" , method = RequestMethod.GET) public void download (HttpServletResponse response) throws IOException { String fileName = "留言列表 " + DateTimeUtil.millisToDate(DateTimeUtil.getCurrentTimeMillis()); short [] width = {7 , 20 , 100 , 35 }; String[] columnNames = {"ID" , "姓名" , "留言" , "时间" }; String[] keys = {"id" , "name" , "message" ,"createTime" }; List<Map<String, Object>> list = ExcelUtil.createExcelRecord(service2.getAll()); ByteArrayOutputStream os = new ByteArrayOutputStream(); ExcelUtil.createWorkBook(list, width, keys, columnNames).write(os); byte [] content = os.toByteArray(); InputStream is = new ByteArrayInputStream(content); response.reset(); response.setContentType("application/vnd.ms-excel;charset=utf-8" ); response.setHeader("Content-Disposition" , "attachment;filename=" + new String((fileName + ".xls" ).getBytes(), "iso-8859-1" )); ServletOutputStream out = response.getOutputStream(); BufferedInputStream bis = new BufferedInputStream(is); BufferedOutputStream bos = new BufferedOutputStream(out); byte [] buff = new byte [2048 ]; int bytesRead; while (-1 != (bytesRead = bis.read(buff, 0 , buff.length))) { bos.write(buff, 0 , bytesRead); } bis.close(); bos.close(); } @RequestMapping (value = "/remove" , method = RequestMethod.GET) @ResponseBody public String removeUrl () { service2.removeUrl(); return Response.success("RemoveUrlCacheSuccess" ); } @RequestMapping (value = "/size" , method = RequestMethod.GET) @ResponseBody public String getSize () { return Response.success(String.valueOf(WebSocketManager.getSize())); } @RequestMapping (value = "/logout" , method = RequestMethod.GET) public void logout (HttpServletRequest request, HttpServletResponse response) throws IOException { request.getSession().invalidate(); response.sendRedirect(Response.PATH + "login.html" ); } }
8) 编写admin权限过滤器1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.fatesolo.filter;import com.fatesolo.util.Response;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class SessionFilter extends OncePerRequestFilter { @Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String uri = request.getRequestURI(); if (uri.contains("admin" ) && !uri.contains("admin/login" ) && request.getSession().getAttribute("user" ) == null ) { response.sendRedirect(Response.PATH + "login.html" ); } else { filterChain.doFilter(request, response); } } }
至此完成了全部的具体逻辑的实现。
6. 项目运行 1) 打开build.gradle,添加最后的项目依赖:1 compile 'mysql:mysql-connector-java:5.1.8'
compile 'log4j:log4j:1.2.16'
2) 点击Run菜单栏的Edit Configurations,点击绿色加号添加一个Tomcat Server,如图:
3) 点击右侧绿色加号,选择Artifacts,点击确定。
4) 右侧的Application context修改为/messageweb,点击确定。
5) 在MySQL中,选择TestDB,在user表中添加一个admin用户,密码请自行设定并使用MD5加密。
6) 打开Tomcat的conf/server.xml,将Connector修改为如下配置:1 <Connector executor ="tomcatThreadPool" port ="8080" protocol ="org.apache.coyote.http11.Http11NioProtocol" maxThreads ="200" maxHttpHeaderSize ="8192" emptySessionPath ="true" connectionTimeout ="20000" enableLookups ="false" redirectPort ="8443" URIEncoding ="UTF-8" />
7) 打开Tomcat的conf/context.xml,加入如下配置:1 <Loader delegate ="true" />
8) 运行Tomcat,访问http://localhost:8080/messageweb/message ,得到如下JSON结果:1 {"data ":{"list ":[] ,"url ":"http://111.202.98.157/vkp.tc.qq.com/f0019r1sytg.mp4?vkey=C27DE1EE6C583432FF4CC7FD843718AA639A2E41506E0F4276B9640910029E7CEEDE7F98F1C5164339961BC63D3C6D22AF5EEE98F0583B42D719870F3372F77E2904AD01C82D3BE4D28B609CB7B4E75CEE88C408CC0707D3&br=62474&platform=0&fmt=mp4&level=0&type=mp4" } ,"resCode ":"000000" ,"resMsg ":"成功" }
至此,项目已经搭建整合完成。
作者 [@FateSolo] 2016 年 2月 18日