一、环境准备
- 开发工具:IntelliJ IDEA 2017.3.1 x64
- 数据库:MySQL 5.5,其中MySQL的管理工具为Navicat
- JDK版本:JDK1.8
- Maven版本:apache-maven-3.2.2
- Tomcat版本:apache-tomcat-7.0.91
当然了,以上只是我个人电脑上的开发环境,只要版本不过于老旧,应该是没有什么问题的。除此之外,有关于IDEA的基本操作以及JDK、Maven、Tomcat等的配置,这些也需要大家配置好(可以百度搜索一下)。本案例的完整代码会在文章末尾给出。
二、框架搭建
1.使用IDEA创建一个基本的Maven工程
(1)打开IDEA,点击Create New Project
(2)
(3)
(4)
(5)进去之后的界面如下
(6)由于IDEA创建maven的Java web项目时,main文件夹下没有java,resources目录等源文件夹,所以我们需要自己创建,具体过程可以查看这篇博客。这样一个基本的项目结构就创建好了。
2.在pom.xml中添加该项目所需要依赖jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<name>ssmtest</name>
<groupId>com.test</groupId>
<artifactId>ssmtest</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.7</version>
<configuration>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8888</port>
<maxIdleTime>30000</maxIdleTime>
</connector>
</connectors>
<webAppSourceDirectory>${project.build.directory}/${pom.artifactId}-${pom.version}</webAppSourceDirectory>
<contextPath>/</contextPath>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!--dependency>
<groupId>com.test</groupId>
<artifactId>[the artifact id of the block to be mounted]</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency-->
<!-- Maven中央仓库官网: https://mvnrepository.com/ -->
<!-- 引入项目依赖的jar包 -->
<!-- SpringMVC、Spring
包含Spring MVC框架相关的所有类,包括框架的Servlets,Web MVC框架,控制器和视图支持
外部依赖spring-web, (spring-support,Tiles,iText,POI)
-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- Spring-JDBC
包含对Spring对JDBC数据访问进行封装的所有类。外部依赖spring-beans,spring-dao
-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- Spring面向切面编程
提供对AspectJ的支持,以便可以方便的将面向方面的功能集成进IDE中,比如Eclipse AJDT
-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--MyBatis包 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<!-- MyBatis整合Spring的适配包 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 数据库连接池、驱动 -->
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<!-- JSP:标准标签库
引入jstl/jar保证jsp页面可以使用EL表达式 -->
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<!-- provided表明该包只在编译和测试的时候用,
当启动tomcat(在其中有servlet-api包)的时候,就不会冲突了-->
<scope>provided</scope>
</dependency>
<!-- 单元测试junit包 -->
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--与json有关的包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
</dependencies>
</project>
添加完成之后,刷新该工程
这样便可以看见已经添加成功的jar包
3.配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--
1.将Spring框架集成到项目中
1).Spring环境构建时读取web应用的初始化参数contextConfigLocation,
从classpath中读取配置文件spring/spring-*.xml
2).Spring的初始化:ContextLoaderListener的作用是启动Web容器时, 自动装配
spring-*.xml的配置信息(因为它实现了ServletContextListener
这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。)
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
2.集成SpringMVC框架
1)SpringMVC环境构建时需要读取servlet初始化参数init-param,
从classpath中读取配置文件spring/springmvc-context.xml
2)load-on-startup:当值为0或者大于0时,表示容器在应用启动时就加载这个servlet,
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
正数的值越小,启动该servlet的优先级越高。
-->
<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/springmvc-context.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>
<!--
3.使用spring提供的过滤器CharacterEncodingFilter解决乱码问题
乱码问题有get方式提交和post方式提交2种情况
1)解决get方式:在Tomcat的配置文件server.xml中的<Connector connectionTimeout="20000"
port="8080" protocol="HTTP/1.1" redirectPort="8443"/>添加URIEncoding="UTF-8"即可
2)解决post方式:如下所示,增加过滤器
-->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
</web-app>
4.在resource目录下分别创建spring和mybatis目录,并且添加相应的配置文件
(1)spring-context.xml
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--
1.base-package:指定Spring IOC容器扫描的包
2.context:exclude-filter:指定排除哪些表达式的组件,此处排除了Controller注解
原因:Controller注解的的作用是声明控制器(处理器)类。从数据流转的角度,
这个类应该是由SpringMVC框架进行管理和组织的,所以不需要由Spring框架扫描。
-->
<context:component-scan base-package="com.test.*" >
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 3.加载数据库的属性文件并配置数据库连接池(c3p0) -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssmtest?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8"/>
<property name="user" value="root"/>
<property name="password" value="15272595644"/>
</bean>
<!-- 4.集成Mybatis框架 ,通过spring配置Mybatis的核心对象SqlSessionFactory以及完成其他配置-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<property name="configLocation" value="classpath:mybatis/config.xml" />
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" >
<list>
<value>classpath*:mybatis/mapper-*.xml</value>
</list>
</property>
</bean>
<!--
在spring与mybatis整合时,需要对每一个mapper定义对应的一个MapperFactoryBean,
可以使用MapperScannerConfigurer自动扫描mapper,然后自动注册对应的MapperFactoryBean对象
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer" >
<property name="basePackage" value="com.test.dao" />
</bean>
<!-- 5.Spring框架采用声明式事务,通过AOP的方式将事务增加到业务中 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制住数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="transactionAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.Exception" />
<tx:method name="query*" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="transactionAdvice" pointcut="execution(* com..*Service.*(..))"/>
</aop:config>
</beans>
(2)springmvc-context.xml
<?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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!--
1.context:include-filter:表示要包含的目标类,
此处要扫描Controller,与spring-context.xml中的相反
2.use-default-filters:当其值为false时,表示不使用默认的 Filter进行扫描,
默认的扫描是全部都扫描
-->
<context:component-scan base-package="com.*" use-default-filters="false" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--
3.配置<mvc:default-servlet-handler/>的作用:
会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler,
它像一个检查员,对进入DispatcherServlet的URL进行筛查,
如果发现是静态资源的请求, 就将该请求转由Web应用服务器默认的Servlet处理,
如果不是静态资源的请求, 才由DispatcherServlet继续处理。
-->
<mvc:default-servlet-handler/>
<!--4.解决@Controller标识的类的bean的注入和使用-->
<mvc:annotation-driven/>
<!-- 5.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
(3)config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
</typeAliases>
</configuration>
(4)mapper-user.xml
<?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.test.dao.UserDao" >
</mapper>
5.在java目录下创建一些基本的包
包名 | 作用 |
---|---|
bean | 存放一些实体类,有些一与数据库的表相对应,例如,用户类等等 |
controller | 控制层 |
dao | 数据传输层,与数据库进行交数据交互 |
service | 编写 业务逻辑的接口 |
serviceImpl | 实现业务逻辑 |
当然了,每个人给包的命名或者结构安排可能不一样,我所给出的只是一种参考。
三、分析需求创建表并连接数据库
1.由于本文的案例比较简单,只有用户的登录和注册功能,所以在名为ssmtest的数据库(自己在MySQL创建)中只建立了一张t_user表,如下图所示。
2.在IDEA中连接到该名为ssmtest的数据库
这样便连接成功了!与此同时在User.java中添加相应的属性
User.java
package com.test.bean;
public class User {
private String username;
private String password;
//用于判断是否记住用户名
private String flag;
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
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;
}
}
四、添加前端页面
具体目录结构如下图所示
1.login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.test.utils.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<link rel="stylesheet" href="./css/login.css">
</head>
<%
//完成用户名的回显
String username="";
//获得从客户端携带过来的所有Cookie
Cookie[] cookies=request.getCookies();
//从Cookie的数组中查找指定名称的Cookie
Cookie cookie = CookieUtils.findCookie(cookies, "username");
if(cookie!=null)
{
username=cookie.getValue();
}
%>
<body>
<div class="login">
<div class="header">
<h1>
<a href="./login">登录</a> <a href="./register">注册</a>
</h1>
</div>
<form action="" method="post">
<table>
<tr>
<td class="td1">用户名</td>
<td><input type="text" class="input1" id="username" name="username" value="<%= username %>" placeholder="请输入用户名"></td>
</tr>
<tr>
<td class="td1">密码</td>
<td><input type="password" class="input1" id="password" name="password" placeholder="请输入密码"></td>
</tr>
<tr>
<td class="td1" colspan="2">
<input type="checkbox" name="rememberUsername" value="true" checked="checked"> 记住用户名</td>
</tr>
<tr>
<td colspan="2">
<div class="btn-red">
<input type="button" value="登录" id="login-btn" onclick="doLogin()">
</div>
</td>
</tr>
</table>
</form>
</div>
<script src="jquery/jquery-2.1.1.min.js"></script>
<script src="layer/layer.js"></script>
<script>
//1.登录单击事件
function doLogin(){
//账号和密码的非空校验(使用jQuery做的id选择器)
var username = $("#username").val();
var password = $("#password").val();
//表单元素的value取值不会为null,当为空时是空字符串
if(username == "" || password==""){
//alert("用户登录账号或密码为空,请输入");
/*为了使弹出框的样式保持一致,不受浏览器的影响,可以使用组件layer
* layer.msg(arg1,arg2,arg3)
* arg1:提示信息
* arg2: time为弹出框持续的时间,单位为ms
* icon为弹出框呈现的样式
* shift为弹出框出现的样式
* arg3: 回调方法
* 具体细节可以参考layer文档
* */
layer.msg("用户登录账号或密码为空,请输入", {time:2000, icon:5, shift:6}, function(){});
return;
}
var rememberUsername = document.getElementsByName("rememberUsername");
var flag = rememberUsername[0].checked;
//提交表单
var loadingIndex=null;
$.ajax({
type:"POST",
url:"doAjaxLogin",
data:{"username":username,"password":password,"flag":flag},
beforeSend:function(){
loadingIndex = layer.msg('登录中', {icon: 16});
},
success:function(result){
layer.close(loadingIndex);
if(result.success){
//后台验证用户的信息成功之后,跳转到index.jsp页面
window.location.href = "index";
}else{
layer.msg("用户的登录账号或密码不正确,请重新输入!", {time:2000, icon:2, shift:6}, function(){});
}
}
});
}
</script>
</body>
</html>
2.register.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户注册</title>
<link rel="stylesheet" href="./css/reg.css">
</head>
<body>
<div class="reg">
<div class="header">
<h1>
<a href="./login">登录</a> <a href="./register">注册</a>
</h1>
</div>
<form action="" method="post" id="userForm">
<table>
<tr>
<td class="td1">用户名</td>
<td><input type="text" class="input1" name="username" id="username" placeholder="请输入用户名"></td>
</tr>
<tr>
<td class="td1">密码</td>
<td><input type="password" class="input1" name="password" id="password" placeholder="请输入密码"></td>
</tr>
<tr>
<td colspan="2">
<div class="btn-red">
<input type="button" value="注册" id="reg-btn" onclick="doRegister()">
</div>
</td>
</tr>
</table>
</form>
</div>
<script src="jquery/jquery-2.1.1.min.js"></script>
<script src="layer/layer.js"></script>
<script>
//1.登录单击事件
function doRegister(){
//账号和密码的非空校验(使用jQuery做的id选择器)
var username = $("#username").val();
var password = $("#password").val();
var pwdReg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/;
//表单元素的value取值不会为null,当为空时是空字符串
if(username == "" || password == ""){
/*为了使弹出框的样式保持一致,不受浏览器的影响,可以使用组件layer
* layer.msg(arg1,arg2,arg3)
* arg1:提示信息
* arg2: time为弹出框持续的时间,单位为ms
* icon为弹出框呈现的样式
* shift为弹出框出现的样式
* arg3: 回调方法
* 具体细节可以参考layer文档
* */
layer.msg("用户注册账号或密码为空,请输入", {time:2000, icon:5, shift:6}, function(){});
return;
}
//验证密码是否符合要求
if(!pwdReg.test(password)){
layer.msg("密码必须由数字和字母组成,且长度在8-16位之间,请重新输入", {time:2000, icon:5, shift:6}, function(){});
$("#password").reset();
return;
}
//提交表单
var loadingIndex=null;
$.ajax({
type:"POST",
url:"doAjaxRegister",
data:{"username":username,"password":password},
beforeSend:function(){
loadingIndex = layer.msg('注册中', {icon: 16});
},
success:function(result){
layer.close(loadingIndex);
if(result.success){
layer.msg("注册成功!", {time:2000, icon:2, shift:6}, function(){});
//后台验证用户注册成功之后,跳转到login.jsp页面
window.location.href = "login";
}else{
layer.msg("该用户名已经被注册,请重新填写!", {time:2000, icon:2, shift:6}, function(){});
//清除已经填写的用户信息
$("#userForm")[0].reset();
}
}
});
}
</script>
</body>
</html>
五、完成前端与后台的交互
后台的目录结构如下所示
AjaxResult.java
package com.test.bean;
public class AjaxResult {
//用于后台的逻辑判断
private boolean success;
//后台向前端返回的对象
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
1.登录与注册功能
(1)控制器类 DispatcherController.java,用于转发各种请求
package com.test.controller;
import com.test.bean.AjaxResult;
import com.test.bean.User;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Controller
public class DispatcherController {
@Autowired
private UserService userService;
//1.跳转到登录页面
@RequestMapping("login")
public String login(){
return "login";
}
//2.跳转到注册页面
@RequestMapping("register")
public String register(){
return "register";
}
//2.登录成功后跳转到index.jsp页面
@RequestMapping("index")
public String index(){
return "index";
}
//3.用Ajax完成登录操作,但不跳转页面
@ResponseBody
@RequestMapping("doAjaxLogin")
public Object doAjaxLogin(User user, HttpSession session, HttpServletResponse response){
//创建一个AjaxResult对象,用于保存各种
AjaxResult ajaxResult = new AjaxResult();
User dbUser = userService.queryForLogin(user);
//登录成功
if(dbUser != null){
//将用户放入session中
session.setAttribute("loginUser",dbUser.getUsername());
//获取是否记住用户名单选框的状态
if("true".equals(user.getFlag())){
// 完成记住用户名的功能:
Cookie cookie = new Cookie("username",user.getUsername());
//设置有效路径:
cookie.setPath("/ssmtest");
// 设置有效时间:
cookie.setMaxAge(60*60*24);// 保存24小时
// 将cookie回写到浏览器:
response.addCookie(cookie);
}
ajaxResult.setSuccess(true);
}else{
ajaxResult.setSuccess(false);
}
return ajaxResult;
}
//3.用Ajax完成注册操作,但不跳转页面
@ResponseBody
@RequestMapping("doAjaxRegister")
public Object doAjaxRegister(User user){
//创建一个AjaxResult对象,用于保存各种
AjaxResult ajaxResult = new AjaxResult();
User dbUser = userService.queryForLogin(user);
if(dbUser != null){
//该用户已经被注册
ajaxResult.setSuccess(false);
}else{
try {
userService.insertUser(user);
ajaxResult.setSuccess(true);
}catch (Exception e){
e.printStackTrace();
ajaxResult.setSuccess(false);
}
}
return ajaxResult;
}
}
(2).业务逻辑层(接口) UserService
package com.test.service;
import com.test.bean.User;
public interface UserService {
//查询登录用户
User queryForLogin(User user);
//注册用户
void insertUser(User user);
}
(3).业务逻辑实现 UserServiceImpl
package com.test.serviceImpl;
import com.test.bean.User;
import com.test.dao.UserDao;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User queryForLogin(User user) {
return userDao.queryForLogin(user);
}
@Override
public void insertUser(User user) {
userDao.insertUser(user);
}
}
(4)数据访问层
package com.test.dao;
import com.test.bean.User;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
//由于该查询语句较为简单,所以可以直接在@Select注解中写SQL语句
@Select("select * from t_user where username = #{username} and password = #{password}")
User queryForLogin(User user);
//sql语句在mapper-user.xml文件中编写
void insertUser(User user);
}
此时应该在mapper-user.xml文件中添加insertUser方法对应的SQL语句
mapper-user.xml
<?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.test.dao.UserDao" >
<!--用户注册-->
<insert id="insertUser">
insert INTO t_user (username, password)
values (#{username},#{password})
</insert>
</mapper>
此时进行测试
不出问题的话,注册应该也是没有什么问题的。
2.使用拦截器阻止未登陆的用户访问index.jsp页面
可能有人会想到如果直接在浏览器中输入http://localhost:8080/ssmtest/index,那么能不能直接访问到index.jsp页面?如果在用户访问该页面之前不进行登陆信息验证的话,那么是可以直接访问到的,这显然是不安全的。所以这里可以使用SpringMVC的组件拦截器(具体相关内容可以参考我之前整理的浅谈SpringMVC中的拦截器控件)
(1)添加拦截器类LoginInteceptor.java
package com.test.web;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInteceptor implements HandlerInterceptor{
/*在拦截器拦截之前判断是否之后的操作
* @return false:不再继续执行后面的操作
* true:继续执行后面的操作
* */
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//判断当前的用户是否已经登录
HttpSession session = httpServletRequest.getSession();
String loginUser = (String)session.getAttribute("loginUser");
//用户未登陆,跳转到登录页面
if(loginUser == null){
String path = session.getServletContext().getContextPath();
httpServletResponse.sendRedirect(path + "/login");
return false;
}else{
return true;
}
}
//在拦截器拦截完毕之后进行的操作
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
//在完成视图渲染之后进行的操作
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
(2)在springmvc-context.xml文件中配置该拦截器
<?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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!--
1.context:include-filter:表示要包含的目标类,
此处要扫描Controller,与spring-context.xml中的相反
2.use-default-filters:当其值为false时,表示不使用默认的 Filter进行扫描,
默认的扫描是全部都扫描
-->
<context:component-scan base-package="com.*" use-default-filters="false" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--
3.配置<mvc:default-servlet-handler/>的作用:
会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler,
它像一个检查员,对进入DispatcherServlet的URL进行筛查,
如果发现是静态资源的请求, 就将该请求转由Web应用服务器默认的Servlet处理,
如果不是静态资源的请求, 才由DispatcherServlet继续处理。
-->
<mvc:default-servlet-handler/>
<!--4.解决@Controller标识的类的bean的注入和使用-->
<mvc:annotation-driven/>
<!-- 5.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--6.配置拦截器-->
<mvc:interceptors>
<!--配置登录拦截器-->
<mvc:interceptor>
<!--拦截所有的请求-->
<mvc:mapping path="/**"/>
<!--排除要拦截的请求-->
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/doAjaxLogin"/>
<mvc:exclude-mapping path="/register"/>
<mvc:exclude-mapping path="/doAjaxRegister"/>
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/css/**"/>
<mvc:exclude-mapping path="/jquery/**"/>
<mvc:exclude-mapping path="/layer/**"/>
<bean class="com.test.web.LoginInteceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
3.使用Cookie完成记住用户名的功能
在CookieUtils .java中创建findCookie方法,便于寻找用户名
package com.test.utils;
import javax.servlet.http.Cookie;
//在浏览器中回显用户名
public class CookieUtils {
public static Cookie findCookie(Cookie[] cookies , String name){
if(cookies == null){
// 说明客户端没有携带Cookie:
return null;
}else{
// 说明客户端携带Cookie:
for (Cookie cookie : cookies) {
if(name.equals(cookie.getName())){
return cookie;
}
}
return null;
}
}
}
其余相关的操作在前面的代码已经给出
4.防止用户多处登录
(1).创建一个用户缓存类LoginCache.java,用于在session中保存用户的相关信息,以便于之后的验证
package com.test.utils;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
public class LoginCache {
//使用单例模式
private LoginCache(){}
private static LoginCache instance = new LoginCache();
public static LoginCache getInstance(){
return instance;
}
// key值:登录用户登录名,value值:登录用户sessionId
private Map<String,String> loginUserSession = new HashMap<String,String>();
//key值:登录用户sessionId,value值:登录用户session对象
private Map<String,HttpSession> loginSession = new HashMap<String,HttpSession>();
/**
* 通过登录名获取对应登录用户的sessionId
* @param username
* @return
*/
public String getSessionIdByUsername(String username){
return loginUserSession.get(username);
}
/**
* 通过sessionId获取对应的session对象
* @param sessionId
* @return
*/
public HttpSession getSessionBySessionId(String sessionId){
return loginSession.get(sessionId);
}
/**
* 存储登录名与对应的登录sessionID至缓存对象
* @param username
* @param sessionId
*/
public void setSessionIdByUserName(String username,String sessionId){
loginUserSession.put(username, sessionId);
}
/**
* 存储sessionId与对应的session对象至缓存对象
* @param sessionId
* @param session
*/
public void setSessionBySessionId(String sessionId,HttpSession session){
loginSession.put(sessionId, session);
}
}
(2)创建LoginSessionListener类,用户监听用户属性在session中的变化
package com.test.listener;
import com.test.utils.LoginCache;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
public class LoginSessionListener implements HttpSessionAttributeListener{
private static final String LOGINUSER="loginUser";
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
//获得session中添加的属性值的名称
String attrName = httpSessionBindingEvent.getName();
if(LOGINUSER.equals(attrName)){
//获取添加的属性值,即用户登录名
String attrValue = (String)httpSessionBindingEvent.getValue();
//该次操作的session对象
HttpSession session1 = httpSessionBindingEvent.getSession();
//该次操作的session对象ID
String sessionId = session1.getId();
//从缓存对象里面,获得该用户登录名对应的sessionID值
String sessionId2 = LoginCache.getInstance().getSessionIdByUsername(attrValue);
//未获得结果,不需要清理前次登录用户会话信息
if(null == sessionId2){
}else{
HttpSession session2 = LoginCache.getInstance().getSessionBySessionId(sessionId2);//获取前次该用户登录对应的session对象
//清理前次登录用户会话存储信息,使得前次登录失效
session2.invalidate();
}
//完成该次登录用户登录名、sessionID,session对象的缓存对象存储
LoginCache.getInstance().setSessionIdByUserName(attrValue, sessionId);
LoginCache.getInstance().setSessionBySessionId(sessionId, session1);
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
}
}
(3)在web.xml文件中注册该监听器
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--
1.将Spring框架集成到项目中
1).Spring环境构建时读取web应用的初始化参数contextConfigLocation,
从classpath中读取配置文件spring/spring-*.xml
2).Spring的初始化:ContextLoaderListener的作用是启动Web容器时, 自动装配
spring-*.xml的配置信息(因为它实现了ServletContextListener
这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。)
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
2.集成SpringMVC框架
1)SpringMVC环境构建时需要读取servlet初始化参数init-param,
从classpath中读取配置文件spring/springmvc-context.xml
2)load-on-startup:当值为0或者大于0时,表示容器在应用启动时就加载这个servlet,
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
正数的值越小,启动该servlet的优先级越高。
-->
<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/springmvc-context.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>
<!--
3.使用spring提供的过滤器CharacterEncodingFilter解决乱码问题
乱码问题有get方式提交和post方式提交2种情况
1)解决get方式:在Tomcat的配置文件server.xml中的<Connector connectionTimeout="20000"
port="8080" protocol="HTTP/1.1" redirectPort="8443"/>添加URIEncoding="UTF-8"即可
2)解决post方式:如下所示,增加过滤器
-->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<!--4.注册监听器-->
<listener>
<listener-class>com.test.listener.LoginSessionListener</listener-class>
</listener>
</web-app>
这样一来,若同一个用户在两处登录同一个账号时,那么前一个登录的用户信息会被清除,并且由拦截器拦截到用户信息在session中为空之后,直接跳转到login.jsp页面,这样便实现了同一个用户不能多处登录。
好啦!到这里为止,一个基于SSM框架的登录注册案例便完成了。一些必要的注释我已经在代码中给出,由于文字不能完全地还原整个开发过程,所以给出的代码顺序会存在一些前后台不同步的情况,不过给出的代码应该是完整的。如果大家发现了错误可以直接提出来哦~~
该案例源码:ssmtest
提取码:86rm