spring学习总结(mybatis,事务,测试JUnit4,日志log4j&slf4j,定时任务quartz&spring-task,jetty,Restful-jersey等)
在实战中学习,模仿博客园的部分功能。包括用户的注册,登陆;发表新随笔,阅读随笔;发表评论,以及定时任务等。Entity层设计3张表,分别为user表(用户),essay表(随笔)以及comment表(评论)。表结构如下:
项目开发采用Intellij IDEA + maven,整个项目结构如下如下图所示:
在项目的pom.xml文件中,导入项目需要的依赖。pom.xml内容如下所示:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 3 <modelVersion>4.0.0</modelVersion> 4 <groupId>spring_demo2</groupId> 5 <artifactId>com.everSeeker</artifactId> 6 <packaging>war</packaging> 7 <version>1.0</version> 8 <name>com.everSeeker Maven Webapp</name> 9 <url>http://maven.apache.org</url> 10 11 <properties> 12 <spring.version>4.2.4.RELEASE</spring.version> 13 <jetty.version>9.3.7.v20160115</jetty.version> 14 <slf4j.version>1.7.14</slf4j.version> 15 <jersey.version>1.19</jersey.version> 16 </properties> 17 18 <dependencies> 19 20 <!--数据库相关, mysql, mybatis--> 21 <dependency> 22 <groupId>mysql</groupId> 23 <artifactId>mysql-connector-java</artifactId> 24 <version>5.1.38</version> 25 </dependency> 26 <dependency> 27 <groupId>org.mybatis</groupId> 28 <artifactId>mybatis</artifactId> 29 <version>3.3.0</version> 30 </dependency> 31 <dependency> 32 <groupId>org.mybatis</groupId> 33 <artifactId>mybatis-spring</artifactId> 34 <version>1.2.3</version> 35 </dependency> 36 37 <!--数据源配置, dataSource--> 38 <dependency> 39 <groupId>c3p0</groupId> 40 <artifactId>c3p0</artifactId> 41 <version>0.9.1.2</version> 42 </dependency> 43 44 <!--事务相关, transcationManager--> 45 <dependency> 46 <groupId>org.springframework</groupId> 47 <artifactId>spring-jdbc</artifactId> 48 <version>${spring.version}</version> 49 </dependency> 50 51 <!--可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。外部依赖spring-beans, (spring-aop)。--> 52 <!--提供基于注解的配置, 比如@Component, @Service, @Repository, @Controller等--> 53 <dependency> 54 <groupId>org.springframework</groupId> 55 <artifactId>spring-context</artifactId> 56 <version>${spring.version}</version> 57 </dependency> 58 <dependency> 59 <groupId>org.springframework</groupId> 60 <artifactId>spring-context-support</artifactId> 61 <version>${spring.version}</version> 62 </dependency> 63 <dependency> 64 <groupId>org.springframework</groupId> 65 <artifactId>spring-tx</artifactId> 66 <version>${spring.version}</version> 67 </dependency> 68 69 <!--测试--> 70 <dependency> 71 <groupId>junit</groupId> 72 <artifactId>junit</artifactId> 73 <version>4.12</version> 74 <scope>test</scope> 75 </dependency> 76 <dependency> 77 <groupId>org.springframework</groupId> 78 <artifactId>spring-test</artifactId> 79 <version>${spring.version}</version> 80 </dependency> 81 82 <!--任务调度--> 83 <dependency> 84 <groupId>org.quartz-scheduler</groupId> 85 <artifactId>quartz</artifactId> 86 <version>2.2.1</version> 87 </dependency> 88 89 <!--log4j && slf4j--> 90 <dependency> 91 <groupId>org.slf4j</groupId> 92 <artifactId>slf4j-api</artifactId> 93 <version>${slf4j.version}</version> 94 </dependency> 95 <dependency> 96 <groupId>org.slf4j</groupId> 97 <artifactId>slf4j-log4j12</artifactId> 98 <version>${slf4j.version}</version> 99 </dependency> 100 <dependency> 101 <groupId>org.slf4j</groupId> 102 <artifactId>jcl-over-slf4j</artifactId> 103 <version>${slf4j.version}</version> 104 <scope>runtime</scope> 105 </dependency> 106 107 <!--jetty相关--> 108 <dependency> 109 <groupId>org.eclipse.jetty</groupId> 110 <artifactId>jetty-server</artifactId> 111 <version>${jetty.version}</version> 112 </dependency> 113 <dependency> 114 <groupId>org.eclipse.jetty</groupId> 115 <artifactId>jetty-servlet</artifactId> 116 <version>${jetty.version}</version> 117 </dependency> 118 <dependency> 119 <groupId>org.eclipse.jetty</groupId> 120 <artifactId>jetty-webapp</artifactId> 121 <version>${jetty.version}</version> 122 </dependency> 123 <dependency> 124 <groupId>org.eclipse.jetty</groupId> 125 <artifactId>jetty-servlets</artifactId> 126 <version>${jetty.version}</version> 127 </dependency> 128 129 <!--jersey--> 130 <dependency> 131 <groupId>com.sun.jersey</groupId> 132 <artifactId>jersey-core</artifactId> 133 <version>${jersey.version}</version> 134 </dependency> 135 <dependency> 136 <groupId>com.sun.jersey.contribs</groupId> 137 <artifactId>jersey-spring</artifactId> 138 <version>${jersey.version}</version> 139 <exclusions> 140 <exclusion> 141 <artifactId>spring-core</artifactId> 142 <groupId>org.springframework</groupId> 143 </exclusion> 144 <exclusion> 145 <artifactId>spring-beans</artifactId> 146 <groupId>org.springframework</groupId> 147 </exclusion> 148 <exclusion> 149 <artifactId>spring-context</artifactId> 150 <groupId>org.springframework</groupId> 151 </exclusion> 152 <exclusion> 153 <artifactId>spring-web</artifactId> 154 <groupId>org.springframework</groupId> 155 </exclusion> 156 <exclusion> 157 <artifactId>spring-aop</artifactId> 158 <groupId>org.springframework</groupId> 159 </exclusion> 160 </exclusions> 161 </dependency> 162 <dependency> 163 <groupId>com.sun.jersey</groupId> 164 <artifactId>jersey-server</artifactId> 165 <version>${jersey.version}</version> 166 </dependency> 167 <dependency> 168 <groupId>com.sun.jersey</groupId> 169 <artifactId>jersey-servlet</artifactId> 170 <version>${jersey.version}</version> 171 </dependency> 172 <dependency> 173 <groupId>com.sun.jersey</groupId> 174 <artifactId>jersey-json</artifactId> 175 <version>${jersey.version}</version> 176 </dependency> 177 178 <!--用来将POJO序列化为JSON对象--> 179 <dependency> 180 <groupId>org.glassfish.jersey.media</groupId> 181 <artifactId>jersey-media-json-jackson</artifactId> 182 <version>2.22.2</version> 183 </dependency> 184 185 </dependencies> 186 187 <build> 188 <finalName>com.everSeeker</finalName> 189 </build> 190 191 </project>
注:以下所有介绍,第一步都是在pom.xml文件中导入相关依赖。之后文章中不再说明。
下面开始详细介绍。
一、Mybatis
1、先做准备工作。在mysql数据库中创建表。
1 create database if NOT EXISTS spring_demo default character set utf8; 2 use spring_demo; 3 show engines; 4 5 create table if not exists user(id int primary key not null auto_increment, username varchar(12) not null, password varchar(20), score int, ranking int, essay_count int, UNIQUE(username)) engine=InnoDB; 6 show table status like 'user'\G; 7 8 create table if not exists essay(id int primary key not null auto_increment, title varchar(40) not null, create_date datetime, user_id int, reading_count int, comment_count int, tag varchar(40), UNIQUE(title)) engine=InnoDB; 9 show table status like 'essay'\G; 10 11 create table if not exists comment(id int PRIMARY KEY NOT NULL AUTO_INCREMENT, content TEXT, user_id int, essay_id int, comment_date DATETIME) ENGINE=InnoDB; 12 show table status like 'comment'\G;
2、在entity目录下创建与数据库中表对应的类,以user表为例。
1 package com.everSeeker.entity; 2 3 import java.io.Serializable; 4 5 /** 6 * 对象的序列化 class implements Serializable 7 * 参考文档:http://www.cnblogs.com/xudong-bupt/archive/2013/05/19/3086493.html 8 */ 9 public class User implements Serializable { 10 private int id; 11 private String username; 12 private String password; 13 private int score; 14 private int ranking; 15 private int essayCount; 16 17 public User() {} 18 19 public User(String username, String password) { 20 this.username = username; 21 this.password = password; 22 score = 0; 23 ranking = 0; 24 essayCount = 0; 25 } 26 27 public int getId() { 28 return id; 29 } 30 31 public void setId(int id) { 32 this.id = id; 33 } 34 35 public String getUsername() { 36 return username; 37 } 38 39 public void setUsername(String username) { 40 this.username = username; 41 } 42 43 public String getPassword() { 44 return password; 45 } 46 47 public void setPassword(String password) { 48 this.password = password; 49 } 50 51 public int getScore() { 52 return score; 53 } 54 55 public void setScore(int score) { 56 this.score = score; 57 } 58 59 public int getRanking() { 60 return ranking; 61 } 62 63 public void setRanking(int ranking) { 64 this.ranking = ranking; 65 } 66 67 public int getEssayCount() { 68 return essayCount; 69 } 70 71 public void setEssayCount(int essayCount) { 72 this.essayCount = essayCount; 73 } 74 75 @Override 76 public String toString() { 77 return "User [id=" + id + ", username=" + username + ", password=" + password + ", score=" + score + 78 ", rankding=" + ranking + ", essayCount=" + essayCount + "]"; 79 } 80 }
3、在dao目录下创建操作数据表的接口,以userDao为例。
1 package com.everSeeker.dao; 2 3 import com.everSeeker.entity.User; 4 import org.apache.ibatis.annotations.Param; 5 import org.springframework.stereotype.Repository; 6 7 @Repository 8 public interface UserDao { 9 10 void addUser(@Param("user") User user); 11 12 User getUserById(int id); 13 14 User getUserByUsername(String username); 15 16 void updateUser(User user); 17 18 void rankingByScore(); 19 }
4、为使用mybatis管理操作数据库,首先需要设置spring与mybatis配合使用的相关配置。
mybatis.xml:在本项目中,仅仅用作给实体类配置别名。
spring-mybatis.xml:在本项目中,用来配置数据源dataSource,sqlSessionFactory等。
具体文件内容如下:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> 4 5 <configuration> 6 7 <!--配置实体类的别名--> 8 <typeAliases> 9 <!--以下2种方法选其一即可。 第1种方法:使用typeAlias,为单个类设置别名。--> 10 <typeAlias type="com.everSeeker.entity.User" alias="User" /> 11 <typeAlias type="com.everSeeker.entity.Essay" alias="Essay" /> 12 <typeAlias type="com.everSeeker.entity.Comment" alias="Comment" /> 13 <!--第2种方法:使用package,为包下面的所有类设置别名,默认规则为com.everSeeker.entity.User设置为User,去除前面的包名。--> 14 <!--<package name="com.everSeeker.entity" />--> 15 </typeAliases> 16 17 </configuration>
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:tx="http://www.springframework.org/schema/tx" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 8 http://www.springframework.org/schema/tx 9 http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> 10 11 <!--数据源配置 c3p0 12 常见的数据源实现类包有2个,一个是apache的DBCP(org.apache.commons.dbcp.BasicDataSource),另一个为C3P0。 13 --> 14 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" 15 destroy-method="close"> 16 17 <property name="driverClass" value="${db.mysql.driverClass}" /> 18 <property name="jdbcUrl" value="${db.mysql.jdbcUrl}" /> 19 <property name="user" value="${db.mysql.user}" /> 20 <property name="password" value="${db.mysql.password}" /> 21 22 <!--连接池中保留的最小连接数。 --> 23 <property name="minPoolSize" value="${db.minPoolSize}" /> 24 25 <!--连接池中保留的最大连接数。Default: 15 --> 26 <property name="maxPoolSize" value="${db.maxPoolSize}" /> 27 28 <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 --> 29 <property name="initialPoolSize" value="${db.initialPoolSize}" /> 30 31 <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> 32 <property name="maxIdleTime" value="${db.maxIdleTime}" /> 33 34 <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 --> 35 <property name="acquireIncrement" value="${db.acquireIncrement}" /> 36 37 <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。 38 如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 --> 39 <property name="maxStatements" value="${db.maxStatements}" /> 40 41 <!--每60秒检查所有连接池中的空闲连接。Default: 0 --> 42 <property name="idleConnectionTestPeriod" value="${db.idleConnectionTestPeriod}" /> 43 44 <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 --> 45 <property name="acquireRetryAttempts" value="${db.acquireRetryAttempts}" /> 46 47 <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试 48 获取连接失败后该数据源将申明已断开并永久关闭。Default: false --> 49 <property name="breakAfterAcquireFailure" value="${db.breakAfterAcquireFailure}" /> 50 51 <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable 52 等方法来提升连接测试的性能。Default: false --> 53 <property name="testConnectionOnCheckout" value="${db.testConnectionOnCheckout}" /> 54 </bean> 55 56 <!-- myBatis配置. 57 classpath和classpath*的区别,参考文档:http://blog.csdn.net/zl3450341/article/details/9306983. 58 classpath只会返回第一个匹配的资源,建议确定路径的单个文档使用classpath;匹配多个文档时使用classpath*. 59 --> 60 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" 61 p:dataSource-ref="dataSource" 62 p:configLocation="classpath:mybatis.xml" 63 p:mapperLocations="classpath*:com/everSeeker/*Mapper.xml" /> 64 65 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 66 <!--basePackage指定要扫描的包,在此包之下的映射器都会被搜索到。可指定多个包,包与包之间用逗号或分号分隔 67 MapperScannerConfigurer将扫描basePackage所指定包下的所有接口类(包括子包),如果他们在SQL映射文件 68 中定义过,则将他们动态定义为一个Spring Bean. --> 69 <property name="basePackage" value="com.everSeeker.dao" /> 70 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> 71 <!--<property name="annotationClass" value="com.everSeeker.dao" />--> 72 </bean> 73 74 </beans>
在spring-mybatis.xml文件中,引入了db.properties文件中的内容。
1 # Database 2 db.mysql.driverClass = com.mysql.jdbc.Driver 3 db.mysql.jdbcUrl = jdbc:mysql://localhost:3306/spring_demo?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true 4 db.mysql.user = root 5 db.mysql.password = 333 6 db.minPoolSize = 10 7 db.maxPoolSize = 100 8 db.initialPoolSize = 20 9 db.maxIdleTime = 60 10 db.acquireIncrement = 5 11 db.maxStatements = 100 12 db.idleConnectionTestPeriod = 60 13 db.acquireRetryAttempts = 30 14 db.breakAfterAcquireFailure = true 15 db.testConnectionOnCheckout = false
最后,在spring.xml配置文件中载入与mybatis相关的配置文件。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context.xsd"> 9 10 <!-- 加载Spring配置文件 --> 11 <context:property-placeholder location="classpath:db.properties"/> 12 <context:property-placeholder location="classpath:log4j.properties"/> 13 14 <import resource="classpath:spring-mybatis.xml"/> 15 16 <!-- 使用spring annotation自动扫描配置 --> 17 <context:component-scan base-package="com.everSeeker"/> 18 <!-- 自动注入 --> 19 <context:annotation-config/> 20 21 </beans>
5、准备工作已经完成,现在就可以通过在**Mapper.xml文件中以直接写sql语句的方式来操作数据库并同时实现dao层中相关类的接口了。以UserDao为例,在resources/com/everSeeker目录下创建对应的UserMapper.xml文件。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 3 4 <mapper namespace="com.everSeeker.dao.UserDao"> 5 <resultMap id="ResultMapUser" type="com.everSeeker.entity.User"> 6 <id column="id" property="id"/> 7 <result column="username" property="username"/> 8 <result column="password" property="password"/> 9 <result column="score" property="score"/> 10 <result column="ranking" property="ranking"/> 11 <result column="essay_count" property="essayCount"/> 12 </resultMap> 13 14 <insert id="addUser" parameterType="User"> 15 INSERT INTO user(username, password, score, ranking, essay_count) VALUES(#{user.username}, #{user.password}, #{user.score}, #{user.ranking}, #{user.essayCount}) 16 </insert> 17 18 <select id="getUserById" parameterType="int" resultMap="ResultMapUser"> 19 SELECT * FROM user WHERE id=#{id} 20 </select> 21 22 <select id="getUserByUsername" parameterType="String" resultMap="ResultMapUser"> 23 SELECT * FROM user where username=#{username} 24 </select> 25 26 <update id="updateUser" parameterType="User"> 27 UPDATE user SET username=#{username}, password=#{password}, score=#{score}, ranking=#{ranking}, essay_count=#{essayCount} where id=#{id} 28 </update> 29 30 <!--在mysql中执行多条语句,可以采用存储过程,如{call proc()};也可以通过连接数据库时设置allowMultiQueries=true来实现--> 31 <update id="rankingByScore"> 32 -- { call proc() } 33 SET @row=0; 34 UPDATE user SET ranking=(@row:=@row+1) ORDER BY score DESC; 35 </update> 36 </mapper>
6、更多关于mybatis的内容参考:
1) http://www.mybatis.org/mybatis-3/zh/index.html
2) 如果数据表中的column字段和modal(entity)中定义的类的字段不一致,比如数据库中User表有字段t_username,而在类User中定义字段username,则可以使用ResultMap来代替ResultType。详细信息可参考MyBatis中关于resultType和resultMap的区别以及MyBatis魔法堂:ResultMap详解以及MyBatis魔法堂:即学即用篇。
二、事务
在spring中实现事务可以很简单。只需要配置好事务管理器,之后给需要事务处理的类或者方法直接通过@Transactional注解即可。
1、在本项目中,通过在spring-mybatis.xml文件中配置事务管理。
1 <!-- 事务管理器配置, 使用jdbc事务 --> 2 <bean id="transactionManager" 3 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 4 <property name="dataSource" ref="dataSource" /> 5 </bean> 6 7 <!-- 使用annotation定义事务,对标注了@Transactional注解的bean进行处理,以织入事务管理切面. 8 默认情况下,自动使用名称为transactionManager的事务管理器。 9 proxy-target-class为true,表示spring将通过创建子类来代理业务类,需要在类路径中添加CGLib.jar类库。--> 10 <tx:annotation-driven transaction-manager="transactionManager" 11 proxy-target-class="true" />
2、给需要事务处理的类或者方法通过@Transactional注解。以CommentServiceImpl.java为例,对类中所有方法进行事务处理。publicNewComment方法为发表新的评论,需要在comment表中新增一条评论的记录,之后在essay表中对被评论的随笔评论数+1,同时还需要在user表中对随笔的作者score+10分,这3个操作组合成了一个原子操作,需要进行事务处理。
1 package com.everSeeker.service.impl; 2 3 import com.everSeeker.dao.CommentDao; 4 import com.everSeeker.dao.EssayDao; 5 import com.everSeeker.dao.UserDao; 6 import com.everSeeker.entity.Comment; 7 import com.everSeeker.entity.Essay; 8 import com.everSeeker.entity.User; 9 import com.everSeeker.service.CommentService; 10 import org.springframework.stereotype.Service; 11 import org.springframework.transaction.annotation.Transactional; 12 13 import javax.annotation.Resource; 14 15 @Service("commentService") 16 @Transactional 17 public class CommentServiceImpl implements CommentService { 18 @Resource 19 private CommentDao commentDao; 20 @Resource 21 private EssayDao essayDao; 22 @Resource 23 private UserDao userDao; 24 25 public void publishNewComment(Comment comment) { 26 //comment表中新增一条记录 27 commentDao.addComment(comment); 28 //essay表comment_count+1 29 Essay essay = essayDao.getEssayById(comment.getEssayId()); 30 if(essay != null) { 31 essay.setCommentCount(essay.getCommentCount() + 1); 32 essayDao.updateEssay(essay); 33 //user表随笔作者对应的记录score+10 34 User user = userDao.getUserById(essay.getUserId()); 35 if(user != null) { 36 user.setScore(user.getScore() + 10); 37 userDao.updateUser(user); 38 } 39 } 40 } 41 }
三、JUnit4测试
使用JUnit4可以很方便的进行单元测试。假设我们需要对UserService类中的各个方法进行测试,只需要在test/com/everSeeker/service目录下新建测试类TestUserService即可。
1、在测试类前面新增2个注解:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/spring.xml"})
@RunWith:加载JUnit4。
@ContextConfiguration:加载spring配置文件,是一个字符串数组,可以加载多个配置文件。
2、在具体方法前新增注解@Test。
TestUserService.java关键内容如下:
1 package com.everSeeker.service; 2 3 import com.everSeeker.dao.UserDao; 4 import com.everSeeker.entity.User; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.test.annotation.Rollback; 8 import org.springframework.test.context.ContextConfiguration; 9 import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; 10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 12 import javax.annotation.Resource; 13 14 15 @RunWith(SpringJUnit4ClassRunner.class) 16 @ContextConfiguration(locations = {"/spring.xml"}) 17 public class TestUserService extends AbstractTransactionalJUnit4SpringContextTests { 18 19 @Resource 20 private UserService userService; 21 22 @Resource 23 private UserDao userDao; 24 25 /** 26 * AbstractTransactionalJUnit4SpringContextTests默认回滚。如果需要修改为不回滚设置为false即可。 27 * 默认回滚的主要目的是避免产生脏数据。但是如果数据库主键采取自增模式的话,实质上对数据库还是有一点影响。如果主键采取UUID就没这个问题。 28 */ 29 @Test 30 @Rollback(false) 31 public void TestAddUser() { 32 User user = new User("ponpon7", "888888"); 33 userService.addUser(user); 34 } 35 36 @Test 37 public void TestGetUserByUsername() { 38 System.out.println(userService.getUserByUsername("ppp")); 39 } 40 }
四、日志(log4j & slf4j)
1、关键是配置好log4j.properties文件。
1 #更多详情请参考: 2 #http://www.cnblogs.com/pigtail/archive/2013/02/16/2913195.html 3 #http://it.oyksoft.com/log4j/ 4 5 #此句为将等级为INFO的日志信息输出到stdout和R这两个目的地,stdout和R的定义在下面的代码,可以任意起名。 6 #等级可分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF则不打出任何信息, 7 #如果配置为INFO这样只显示INFO, WARN, ERROR的log信息,而DEBUG信息不会被显示,具体讲解可参照第三部分定义配置文件中的logger。 8 log4j.rootCategory = INFO, R, stdout 9 10 #此句为定义名为stdout的输出端是哪种类型,可以是 11 #org.apache.log4j.ConsoleAppender(控制台), 12 #org.apache.log4j.FileAppender(文件), 13 #org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), 14 #org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件) 15 #org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方) 16 log4j.appender.stdout = org.apache.log4j.ConsoleAppender 17 18 #此句为定义名为stdout的输出端的layout是哪种类型,可以是 19 #org.apache.log4j.HTMLLayout(以HTML表格形式布局), 20 #org.apache.log4j.PatternLayout(可以灵活地指定布局模式), 21 #org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串), 22 #org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息) 23 #具体讲解可参照第三部分定义配置文件中的Layout。 24 log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 25 26 #如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下: 27 #%m 输出代码中指定的消息 28 #%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL 29 #%r 输出自应用启动到输出该log信息耗费的毫秒数 30 #%c 输出所属的类目,通常就是所在类的全名 31 #%t 输出产生该日志事件的线程名 32 #%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n” 33 #%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921 34 #%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。 35 #[QC]是log信息的开头,可以为任意字符,一般为项目简称。 36 log4j.appender.stdout.layout.ConversionPattern = [%p]-[%t]-[%l]-[%d{yyyyMMdd HH:mm:ss}]%n%m%n 37 38 #将日志信息存入文件中 39 log4j.appender.R = org.apache.log4j.DailyRollingFileAppender 40 log4j.appender.R.Threshold = INFO 41 log4j.appender.R.File = /Users/pingping/Projects/IdeaProjects/cnblogs/spring_demo2/logs/output.log 42 log4j.appender.R.DatePattern = '.'yyyy-MM-dd 43 log4j.appender.R.Append = true 44 log4j.appender.R.layout = org.apache.log4j.PatternLayout 45 log4j.appender.R.layout.ConversionPattern = [%p]-[%t]-[%l]-[%d{yyyyMMdd HH:mm:ss}]%n%m%n
更多详细信息可参考:
http://www.cnblogs.com/pigtail/archive/2013/02/16/2913195.html
2、直接使用即可,以UserServiceImpl.java为例。
1 package com.everSeeker.service.impl; 2 3 import com.everSeeker.dao.UserDao; 4 import com.everSeeker.entity.User; 5 import com.everSeeker.service.UserService; 6 import org.slf4j.Logger; 7 import org.slf4j.LoggerFactory; 8 import org.springframework.stereotype.Service; 9 10 import javax.annotation.Resource; 11 12 @Service("userService") 13 public class UserServiceImpl implements UserService { 14 @Resource 15 private UserDao userDao; 16 17 private static Logger log = LoggerFactory.getLogger(UserServiceImpl.class); 18 19 public void addUser(User user) { 20 userDao.addUser(user); 21 } 22 23 public User getUserByUsername(String username) { 24 User user = userDao.getUserByUsername(username); 25 log.info("All info about {}: \n{}", username, user); 26 return user; 27 } 28 29 public int checkUser(String username, String password) { 30 log.info("start check username: {}", username); 31 User user = userDao.getUserByUsername(username); 32 if (user == null) { 33 log.warn("username is incorrect!"); 34 return 10; 35 } 36 if (!user.getPassword().equals(password)) { 37 log.warn("passowrd is incorrect!"); 38 return 100; 39 } 40 log.info("{} has successed checked!", username); 41 return 1; 42 } 43 }
五、定时任务(Quartz & spring-task)
主要介绍目前主流的2种在指定时间执行或者按某个频率自动执行的实现方式。
1、spring-task:采用@Scheduled注解方式,配置简单,使用灵活方便。
2、quartz:配置稍微复杂,功能强大。
下面以具体代码详细说明。
首先,新创建spring-task.xml配置文件,具体内容如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:task="http://www.springframework.org/schema/task" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/task 8 http://www.springframework.org/schema/task/spring-task-3.0.xsd" 9 default-lazy-init="false"> 10 11 <!--开启定时任务的2种方法,(1)通过spring-task,采用@Scheduled注解方式,配置简单,使用灵活方便; 12 (2)通过quartz,配置稍微复杂,功能强大 --> 14 15 <!--方法一:--> 16 <!--开启task:annotation-driven,spring可以通过注解@Scheduled来开启任务--> 17 <task:executor id="executor" pool-size="5"/> 18 <task:scheduler id="scheduler" pool-size="10"/> 19 <task:annotation-driven executor="executor" scheduler="scheduler"/> 20 21 <!--方法二:--> 22 <!--配置作业类--> 23 <bean id="quartzTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> 24 <property name="targetObject"> 25 <bean class="com.everSeeker.task.QuartzTask"/> 26 </property> 27 <property name="targetMethod" value="rankingByScore"/> 28 <property name="concurrent" value="false"/> 29 </bean> 30 <!--配置触发器--> 31 <!--关于cronExpression, 请参考: http://www.cnblogs.com/yaowen/p/3779284.html--> 32 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> 33 <property name="jobDetail" ref="quartzTask"/> 34 <!--每隔10s执行一次--> 35 <property name="cronExpression" value="0/10 * * * * ?"/> 36 </bean> 37 <!--配置调度工厂--> 38 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> 39 <property name="triggers"> 40 <list> 41 <ref bean="cronTrigger"/> 42 </list> 43 </property> 44 </bean> 45 46 </beans>
在com/everSeeker/task目录下新建2个文件QuartzTask.java以及SpringTask.java,分别用来测试quartz以及spring-task。
QuartzTask.java
1 package com.everSeeker.task; 2 3 import com.everSeeker.dao.UserDao; 4 import org.springframework.stereotype.Service; 5 6 import javax.annotation.Resource; 7 8 @Service 9 public class QuartzTask { 10 @Resource 11 private UserDao userDao; 12 13 public void rankingByScore() { 14 System.out.println("通过quartz, 每隔10s执行一次任务。。。"); 15 // userDao.rankingByScore(); 16 } 17 }
SpringTask.java
1 package com.everSeeker.task; 2 3 import com.everSeeker.dao.UserDao; 4 import org.springframework.scheduling.annotation.Scheduled; 5 import org.springframework.stereotype.Component; 6 7 import javax.annotation.Resource; 8 9 @Component("springTask") 10 public class SpringTask { 11 12 @Resource 13 private UserDao userDao; 14 15 @Scheduled(cron = "0/20 * * * * ?") 16 public void rankingByScoreJob() { 17 System.out.println("通过spring-task,每隔20秒执行一次任务。。。"); 18 System.out.println("----------------------------------------"); 19 // userDao.rankingByScore(); 20 } 21 }
六、jetty
jetty需要定义Server, Connector以及至少一个handler, ThreadPool可选。
先定义自己的handler,内容是输出"hello jetty"。
1 public class MyHandler extends AbstractHandler { 2 3 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 4 throws IOException, ServletException { 5 response.setContentType("text/html;charset=utf-8"); 6 response.setStatus(HttpServletResponse.SC_OK); 7 baseRequest.setHandled(true); 8 response.getWriter().println("<h1>Hello jetty</h1>"); 9 } 10 }
之后在Spring配置文件中将Server,Connector以及Handler配置好即可。
1 # spring-jetty.xml 2 <bean id="jetty_server" class="org.eclipse.jetty.server.Server" init-method="start" destroy-method="stop"> 3 4 <!--<property name="threadPool">--> 5 <!--<bean id="defaultThreadPool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">--> 6 <!--<property name="minThreads" value="10"/>--> 7 <!--<property name="maxThreads" value="100"/>--> 8 <!--</bean>--> 9 <!--</property>--> 10 11 <property name="connectors"> 12 <list> 13 <bean id="Connector" class="org.eclipse.jetty.server.ServerConnector"> 14 <constructor-arg name="server"><ref bean="jetty_server"/></constructor-arg> 15 <property name="port" value="8080"/> 16 </bean> 17 </list> 18 </property> 19 20 <property name="handler"> 21 <bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerList"> 22 <property name="handlers"> 23 <list> 24 <bean class="com.everSeeker.jetty.MyHandler"/> 25 <!--<bean class="com.everSeeker.jetty.RestfulHandler"/>--> 26 <bean class="org.eclipse.jetty.server.handler.DefaultHandler"/> 27 </list> 28 </property> 29 </bean> 30 </property> 31 32 </bean>
在网页中打开输入网址, http://localhost:8080,页面显示为"hello jetty"。
更多详情请参考:
http://www.eclipse.org/jetty/documentation/current/index.html
http://hbiao68.iteye.com/blog/2111007
http://www.cnblogs.com/windlaughing/archive/2013/06/07/3125358.html
七、Restful(jersey)
实现Restful的框架很多,本案例采用的是jersey.
首先建立一个jetty服务,并指定要处理jersey资源的包名com.everSeeker.action,然后启动jetty。
public class App { public static void main(String[] args) throws Exception { Server server = new Server(8080); ServletHolder servlet = new ServletHolder(ServletContainer.class); servlet.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", "com.sun.jersey.api.core.PackagesResourceConfig"); servlet.setInitParameter("com.sun.jersey.config.property.packages", "com.everSeeker"); ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS); handler.setContextPath("/"); handler.addServlet(servlet, "/*"); server.setHandler(handler); server.start(); server.join(); } }
之后在包com.everSeeker.action下新建Restful类,以UserAction.java为例。
1 @Component 2 @Path("/user") 3 public class UserAction { 4 5 /** 6 * 如果userService不采用getBean方式获得的话,即直接写成private UserService userService,会报空指针错误。 7 * 通过debug方式查看会发现,userService=null,没有注入成功,原因暂时还不知道,请高手告知。 8 */ 9 @Resource 10 private UserService userService = SpringContextUtils.getApplicationContext().getBean(UserServiceImpl.class); 11 12 /** 13 * @GET : get请求 14 * @Path : 路径,由于类的路径为/user,所以该方法的路径为/user/{username} 15 * @Produces : 返回类型。该方法为文本。 16 * @Consumes : 可以接受的类型。 17 */ 18 @GET 19 @Path("{username}") 20 @Produces(MediaType.TEXT_PLAIN) 21 public String getByUsername(@PathParam("username") String username) throws Exception { 22 return userService.getUserByUsername(username).toString(); 23 } 24 25 /** 26 * 返回的类型为json。需要将类User转换为json格式。本案例采用的转换方式为jackson, 在pom.xml中有说明。 27 */ 28 @GET 29 @Path("json/{username}") 30 @Produces(MediaType.APPLICATION_JSON) 31 public User getUserByUsername(@PathParam("username") String username) throws Exception { 32 return userService.getUserByUsername(username); 33 } 34 }
更多信息请参考:
https://jersey.java.net/nonav/documentation/latest/index.html
http://www.zhaochao.net/index.php/2015/12/07/5/
http://waylau.com/jersey-2-spring-4-rest/
代码清单请见个人github。地址:https://github.com/everseeker0307/spring-demo。