第二章 快速入门
2.1 实例功能概述
2.1.1比HelloWorld更适用的实例
论坛的登陆模块
2.1.2实例功能简介
登陆页面提供输入账号密码的输入表单。填写并提交表单,服务端检查是否匹配,不匹配返回登陆页面,匹配就成功登陆并重定向到欢迎页面。
2.2 环境准备
使用MySQL5.X
2.2.1 创建库表
创建用户表,创建用户登录表。并插入初始化的一个数据
//新建数据库
DROP DATABASE IF EXISTS sampledb;
CREATE DATABASE sampledb DEFAULT CHARACTER SET utf8;
USE sampledb;
//新建用户表
CREATE TABLE t_user(
user_id INT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(30),
credit INT,
password VARCHAR(32),
last_visit datetime,
last_ip VARCHAR(23)
)ENGINE=InnoDB;
//新建登陆日志表
CREATE TABLE t_login_log(
login_long_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
ip VARCHAR(23),
login_datetime datetime
)ENGINE=InnoDB;
//初始化数据
INSERT INTO t_user(user_name, password)VALUES('admin','123456');
2.2.2 建立工程
使用MyEclipse
2.2.3 类包及Spring配置文件规划
Spring可以将所有的配置信息统一到一个文件中,也可以放置到多个文件中。即applicationContext.xml配置文件。
2.3 持久层
2.3.1 建立领域对象
领域对象(Domain Object)也称为实体类。
用户领域对象
package com.liwenguang.baobaotao.domain;
import java.util.Date;
public class User implements Serializable{
private int userId;
private String userName;
private String password;
private int credits;
//省略Get、Set方法
}
package com.liwenguang.baobaotao.domain;
import java.util.Date;
public class LoginLog implements Serializable{
private int loginLogId;
private int userId;
private String ip;
private Date loginDate;
//省略Get、Set方法
}
2.3.2 UserDao和LoginLogDao
访问实体类的DAO。
package com.liwenguang.baobaotao.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import com.liwenguang.baobaotao.domain.User;
@Repository //通过注解定义一个DAO
public class UserDao {
/**
* 根据用户名和密码获取匹配的用户数
*/
@Autowired //自动注入JdbcTemplate的Bean
private JdbcTemplate jdbcTemplate;
public int getMatchCount(String userName, String password){
String sqlString = "SELECT count(*) FROM t_user"+
"WHERE user_name = ? and password = ?";
return jdbcTemplate.queryForInt(sqlString, new Object[]{userName, password});
}
/**
* 根据用户名获取用户对象
* @param userName
* @return
*/
public User findUserByUserName(final String userName){
String sqlString = "SELECT user_id, user_name, credits FROM t_user"
+" WHERE user_name = ?";
final User user = new User();
jdbcTemplate.query(sqlString, new Object[]{userName},
new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
// 对数据指针进行回调
user.setUserId(rs.getInt("user_id"));
user.setUserName(userName);
user.setCredits(rs.getInt("credits"));
}
});
return user;
}
/**
* 更新用户积分,最后登陆IP,最后登陆时间
* @param user
*/
public void updateLoginInfo(User user){
String sqlString = "UPDATE t_user SET last_visit = ?, last_ip = ?, credits = ?"+
" WHERE user_id = ?";
jdbcTemplate.update(sqlString, new Object[]{user.getLastVisit(), user.getLastIp(), user.getCredits()});
}
}
package com.liwenguang.baobaotao.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.liwenguang.baobaotao.domain.LoginLog;
@Repository
public class LoginLogDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insertLoginLog(LoginLog loginLog){
String sqlString = "INSERT INTO t_login_log(user_id, ip, login_datetime)"
+"VALUES(?,?,?)";
Object[] argsObjects = {loginLog.getUserId(), loginLog.getIp(), loginLog.getLoginDate()};
}
}
2.3.4 在Spring中装配DAO
在以上两个DAO实现类中都没有打开/释放Connection的代码,DAO类究竟如何访问数据库呢?所以我们首先声明一个数据源即数据库,然后定义一个JdbcTemplate Bean,通过Spring的容器上下文进行Bean的注入。即applicationContext.xml的Spring配置文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 引用Spring的多个Schema空间的格式定义文件 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入,这样@Repository以及@Autowires才会起作用 -->
<context:component-scan base-package="com.liwenguang.baobaotao.dao"/>
<!-- 定义一个使用DBCP实现的数据源 -->
<bean
id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/sampledb"
p:username="root"
p:password=""/>
<!-- 定义Jdbc模版Bean -->
<bean
id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource"/>
</beans>
2.4 业务层
2.4.1 UserService
package com.liwenguang.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.liwenguang.baobaotao.dao.LoginLogDao;
import com.liwenguang.baobaotao.dao.UserDao;
import com.liwenguang.baobaotao.domain.LoginLog;
import com.liwenguang.baobaotao.domain.User;
@Service //将UserService标注为一个服务层的Bean
public class UserService {
@Autowired //注入DAO层的Bean
private UserDao userDao;
@Autowired
private LoginLogDao loginLogDao;
public boolean hasMatchUser(String userName, String password){
int matchCount = userDao.getMatchCount(userName, password);
return matchCount>0;
}
public User findUserByUserName(String userName){
return userDao.findUserByUserName(userName);
}
public void loginSuccess(User user){
user.setCredits(5 + user.getCredits());
LoginLog loginLog = new LoginLog();
loginLog.setUserId(user.getUserId());e
loginLog.setIp(user.getLastIp());
loginLog.setLoginDate(user.getLastVisit());
userDao.updateLoginInfo(user);
loginLogDao.insertLoginLog(loginLog);
}
}
2.4.2 在Spring中装配Service
<!-- 配置事务管理器 -->
<bean
id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- 通过AOP配置提供事务增强,让service包下所有Bean的所有方法 拥有事务-->
<aop:config
proxy-target-class="true">
<aop:pointcut
id="serviceMethod"
expression=" execution(* com.liwenguang.baobaotao.service..*(..))"/>
<aop:advisor
pointcut-ref="serviceMethod"
advice-ref="txAdvice"/>
</aop:config>
<tx:advice
id="txAdvice"
transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
2.4.3 单元测试
package com.liwenguang.baobaotao.service;
import static org.junit.Assert.*;
import java.util.Date;
import javax.swing.Spring;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.liwenguang.baobaotao.domain.User;
@RunWith(SpringJUnit4ClassRunner.class) //基于JUnit4的Spring测试框架
@ContextConfiguration(locations={"/applicationContext.xml"}) //启动Spring容器
public class TestUserService {
@Autowired //注入Spring容器中的Bean
private UserService userService;
@Test
public void testHasMatchUser() {
boolean b1 = userService.hasMatchUser("admin", "123456");
boolean b2 = userService.hasMatchUser("admin", "1111");
assertTrue(b1);
assertTrue(!b2);
}
@Test
public void testFindUserByUserName(){
User user = userService.findUserByUserName("admin");
assertEquals(user.getUserName(), "admin");
}
@Test
public void testAddLoginLog(){
User user = userService.findUserByUserName("admin");
user.setUserId(1);
user.setUserName("admin");
user.setLastIp("192.168.12.7");
user.setLastVisit(new Date());
userService.loginSuccess(user);
}
}
2.5 展现层
2.5.1 配置Spring MVC框架
对web.xml文件进行配置,以便Web容器启动时能够自动启动Spring容器
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
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">
<!-- 从类路径下记载Spring配置文件,classpath关键字特指类路径下加载 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 负责启动Spring容器的监听器,它将引用上下文参数获得Spring配置文件参数 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>baobaotao</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>baobaotao</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
2.5.2 处理登陆请求
POJO控制器类
package com.liwenguang.baobaotao.web;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.liwenguang.baobaotao.domain.User;
import com.liwenguang.baobaotao.service.UserService;
@Controller //标注为SpringMVC的控制器,处理HTTP的请求,首先会是一个Bean,所以可以使用@Autowired进行Bean的注入
public class LoginController {
@Autowired
private UserService userService;
//负责处理/index.html的请求
@RequestMapping(value = "/index.html")
public String loginPage(){
return "login";
}
//负责处理/loginCheck.html的请求
@RequestMapping(value = "/loginCheck.html")
public ModelAndView loginCheck(HttpServletRequest request, LoginCommand loginCommand){
boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(), loginCommand.getPassword());
if (!isValidUser) {
return new ModelAndView("login", "error", "用户名或密码错误。");
}else{
User user = userService.findUserByUserName(loginCommand.getUserName());
user.setLastIp(request.getRemoteAddr());
user.setLastVisit(new Date());
userService.loginSuccess(user);
request.getSession().setAttribute("user", user);
return new ModelAndView("main");
}
}
}
package com.liwenguang.baobaotao.web;
public class LoginCommand {
private String userName;
private String password;
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;
}
}
Spring MVC配置文件:编写完控制器后,需要在baobaotao-servlet中声明该控制器,扫描Web路径,指定Spring MVC的仕途解析器。
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 扫描web包,应用Spring的注解 -->
<context:component-scan base-package="com.liwenguang.baobaotao.web"/>
<!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:viewClass="org.springframework.web.servlet.view.JstlView"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
</beans>
2.5.3 JSP视图页面
2.6 运行Web应用
2.7 小结
复杂的代码实现简单的功能,但是如果新增功能非常方便
2.8 学习中的问题:
aopalliance-1.0.jar、aspectjweaver-1.7.0.jar、spring-aspects-4.0.2.RELEASE.jar三个包是用于支持applicationContext.xml中声明的AOP所依赖的三个包。