功能介绍html
登陆页面提供一个用户名/密码输入表单,用户填写并提交表单后,服务端检查是否有匹配的用户名/密码。若没有,则返回到登陆页面,并给出提示。若匹配,则记录用户的登陆日志,更新用户的最近登陆时间和IP,并给用户增长5个积分,而后重定向到欢迎页面。java
分析mysql
①首先由两个实体对象类User、Log.实体对象类通常都有对应的数据表。web
②在持久层有两个DAO类,UserDAO和LogDAO。UserDAO负责查询用户名/密码是否存在,并更新用户信息。LogDAO用来记录登陆日志。spring
在业务层有一个UserService服务类、sql
在视图层有一个LoginController类,和两个jsp页面login.jsp和main.jsp。数据库
用一个时序图来描述登陆模块的总体交互过程apache
环境准备app
①建立数据库表框架
CREATE DATABASE sampleDB DEFAULT CHARACTOR SET UTF8;
USER sampleDB;
CREATE TABLE t_user (user_id int auto_increment primay kay,
user_name varchar(30),
credits int,
password varchar(30),
last_visit datetime,
last_ip varchar(30))ENGINE=InnoDB;
CREATE TABLE t_log (log_id int auto_increment primay kay,
user_id int,
ip varchar(30),
login_datetime datetime)ENGINE=InnoDB;
③创建工程。加入jar包spring.jar,commons-dbcp.jar,commons-logging.jar,jstl.jar.standard.jar,log4j-1.2.14.jar,mysql.jar,commons-pool.jar,junit,jar
⑤创建package
dao及其子包impl、domain、service、web、test
持久层开发
①创建实体对象
实体对象也称领域对象,实体对象是数据表操做以面向对象的方式进行。持久层的主要工做就是从数据表中加载数据并实例化实体对象,或将实体对象持久化到数据表中。
public class User { private int userId; private String userName; private String password; private int credits; private String lastIp; private Date lastVisit; //省略getter/setter方法 } public class Log { private int logId; private int userId; private String ip; private Date loginDate; //省略getter/setter方法 }
②开发DAO及其实现类
public interface UserDAO { int getMatchCount(String userName, String password); User findUserByUsername(String username); void updateLoginInfo(User user); } public interface LogDAO { void insertLoginInfo(Log log); }
开发DAO的实现类
public class UserDAOImpl implements UserDAO { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate plate){ jdbcTemplate = plate; } public int getMatchCount(String userName, String password){ String sql = "SELECT count(*) FROM t_user WHERE user_name=? AND password=?"; return jdbcTemplate.queryForInt(sql, new Object[]{userName, password}); } public User findUserByUsername(String userName) { String sql = "SELECT user_id, user_name, credits FROM t_user WHERE user_name=?"; final User user = new User(); jdbcTemplate.query(sql, new Object[]{userName}, new RowCallbackHandler(){ public void processRow(ResultSet rs) throws SQLExeception{ user.setUserId(rs.get("user_id")); user.setUserName(userName); user.setCredits(rs.get("credits")); } }); return user; } public void updateLoginInfo(User user){ String sql = "UPDATE t_user SET last_visit=?,last_ip=?,credits=? WHERE user_id=?"; jdbcTemplate.update(sql, new Object[]{user.getLastVisit(), user.getLastIp(), user.getCredits(), user.getUserId()}); } } public class LogDAOImpl implements LogDAO { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate plate){ jdbcTemplate = plate; } public void insertLoginInfo(Log log){ String sql = "INSERT INTO t_log (user_id, ip, login_datetime) VALUES (?,?,?)"; jdbcTemplate.update(sql, new Object[]{log.getUserId(), log.getIp(), log.getLoginDate()}); } }
在Spring中装配DAO
建立一个名为myProject-dao.xml的spring配置文件。
JdbcTemplate自己须要一个DataSource,这样他就能够根据须要从数据库链接池中获取链接,因此必须先配置一个DataSource,而后定义一个JdbcTemplate Bean,而后再装配好两个DAObean
myProject-dao.xml <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/sampleDB"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="userDAO" class="dao.UserDAOImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <bean id="logDAO" class="dao.LogDAOImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
业务层开发
①开发业务层代码
public interface UserService { boolean hasMatchUser(String userName, String password); User findUserByUserName(String userName); void loginSuccess(User user); } public classUserServiceImpl implements UserService { private UserDAO userDAO; private LogDAO logDAO; public void setUserDAO(UserDAO dao){ userDAO = dao; } public void setLogDAO(LogDAO dao){ logDAO = dao; } public boolean hasMatchUser(String userName, String password) { int matchCount = userDAO.getMatchCount(userName, password); return matchCount > 0; } User findUserByUserName(String userName) { return userDAO.findUserByUserName(userName); } void loginSuccess(User user) { user.setCredits(user.getCredits() + 5); Log log = new Log(); log.setUserId(user.getUserId()); log.setIp(user.getLastIp()); log.setLoginDate(user.getLastVisit()); userDAO.updateLoginInfo(user); logDAO.insertLoginLog(log); } }
②在spring中装配Service。不只装配两个DAO,并且要配置声明式事务。
声明式事务配置:事务管理器(dataSource),target(要代理的对象),代理类(事务管理器,要代理的对象,策略)
myProject-service.xml <beans> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="userServiceTarget" class="service.UserServiceImpl"> <property name="userDAO" ref="userDAO"/> <property name="logDAO" ref="logDAO"/> </bean> <bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager"/> <property name="target" ref="userServiceTarget"/> <property name="transactionAttributes"> <props> <prop key="findUserByUserName"> PROPAGATION_REQUIRED,readOnly </prop> <prop key="hasMatchUser"> PROPAGATION_REQUIRED,readOnly </prop> <prop key="loginSuccess"> PROPAGATION_REQUIRED </prop> </props> </property> </bean> </beans>
视图层开发
Struts2是最流行的视图层框架。但我要先用SpringMVC来开发。
①配置Spring,使Web容器启动时可以自动启动Spring容器
在web.xml中配置 <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:myProject-dao.xml,classpath:myProject-service.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
②配置SpringMVC
在web.xml中配置 <servlet> <servlet-name>myProject</servlet> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>myProject</servlet> <url-pattern>*.html</url-pattern> </servlet-mapping>
SpringMVC一样也有一个配置文件,默认为:servlet名字-servlet.xml。并且这个文件必须放置在WEB-INF目录下,并且这个文件无需在web.xml中说明,SpringMVC会自动将这个文件与其它的Spring的配置文件相拼装。
请求被SpringMVC截获后,首先根据请求的URL找到目标的处理控制器,并将请求参数封装“命令”对象一块儿传给控制器处理,控制器调用Spring容器中的业务Bean完成业务处理工做并返回结果视图。
处理登陆请求
①定义一个Command对象,来封装表单的请求参数
public class LoginCommand { private String userName; private String password; //省略setter/getter方法
①控制器类LoginController,负责处理登陆请求,完成登陆业务,并根据登陆成功与否转向欢迎页面或失败页面
public class LoginController extends AbstractCommandController { private UserService userService; public LoginController(){ setCommandClass(LoginCommand.class); // } public void setUserService(UserService service){ userService = service; } protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, BindException) throws Exception { LoginCommand loginCommand = (LoginCommand)command; boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(), loginCommand.getPassword()); if(!isValidUser){ return New ModelAndView("login", "error", "用户名或密码错误。"); }else { User user = userService.getUserByUserName(loginCommand.getUserName()); user.setLastIp(request.getLocalAddr()); user.setLastVisit(new Date()); userService.loginSuccess(user); request.getSession().setAttribute("user", user); return new ModelAndView("main"); } } }
②在SpringMVC的配置文件中,声明控制器
myProject-servlet.xml <beans> <bean id="loginController" class="web.LoginController"> <property name="userService" ref="userService"/> </bean> ... </beans>
③如何经过URL来调用这个Controller呢?在web.xml中配置的因此.html的URL都截获,它须要根据URL和控制器的映射才能进一步将截获的请求转发给某一特定的控制器处理。这个URL-控制器映射的工做能够经过SpringMVC的SimpleUrlHandlerMapping来完成。
在myProject-servlet.xml中配置 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/loginController.html">loginController</prop> </props> </property> </bean>
④ModelAndView的解析配置
ModelAndView的第一个参数表明视图的逻辑名,第二个和第三个分别为数据模型名称和数据模型对像,将放置到request的属性中。SpringMVC经过一个Bean将逻辑视图名映射为具体的视图页面。
在myProject-servlet.xml中配置 <bean id="viewResolver" class="org.springframework.web.servlet.view.InteralResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean>
JSP视图页面
login.jsp <body> <c:if test="${!empty error}"> <font color="red"><c:out value="${error}"/></font> </c:if> <form action="<c:url value="/loginController.html"/>" method="post"> 用户名:<input type="text" name="userName"/><br> 密码:<input type="password" name="password"/><br> <input type="submit" value="登陆"/> <input type="reset" value="重置"/> </form> </body> main.jsp <body> S{user.userName},欢迎到来,您的积分为${user.credits}。 </body>
login.jsp位于WEB-INF下,没法直接访问,必须经过一个控制器来转发。这个转发控制器能够继承AbstractController。
public class LoginPage extends AbstractController { public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { return new ModelAndView("login"); } }
而后在URL解析器中加上一项映射
在myProject-servlet.xml中配置 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/loginController.html">loginController</prop> <prop key="/index.html">loginPage</prop> </props> </property> </bean>
输入URL http://localhost:8080/myProject/index.html便可看到网页