一、示例
为了对Javaweb中的JSP、Servlet以及Listener和Filter进行简单的总结,创建一个简单的只有登录和注册的小项目,项目采用MVC结构并结合了MyBatis进行简单的数据库操作。项目效果示例如下:
登录页面:
登录时账号或密码错误:
注册页面:
二、实现
2.1 Maven依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 引入MyBatis -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--导入数据库驱动,mysql 8.0.16 版本-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<!-- jstl标签库 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
2.2 mybatis-config.xml及SqlSessionFactory
项目中使用了Mybatis作为持久层,因此需要xml配置文件进行配置,并且需要SqlSessionFactory对象,以便获取SqlSession用以操作数据库。
<?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>
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.wyf.dao.mybatis.model"/>
</typeAliases>
<!-- <typeHandlers>-->
<!-- <typeHandler-->
<!-- javaType="com.wyf.mybaties.type.Enabled"-->
<!-- handler="com.wyf.mybaties.type.EnabledTypeHandler"/>-->
<!-- </typeHandlers>-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/devicetest?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.wyf.dao.mybatis.mapper" />
</mappers>
</configuration>
使用Mybatis,必然要有SqlSessionFactory类,此处使用ServletContextListener上下文参数监听者,在Context参数创建的时候,对SqlSessionFactory类进行初始化。如下BaseMapperTest类用于初始化创建SqlSessionFactory:
public class BaseMapperTest {
private static SqlSessionFactory sqlSessionFactory;
/**
* 使用xml配置文件,初始化SqlSessionFactory
*/
public static void init(String config){
try{
Reader reader= Resources.getResourceAsReader(config);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
}catch(IOException ex){
ex.printStackTrace();
}
}
/**
* 由SqlSessionFactory工厂对象,生产SqlSession
* @return
*/
public static SqlSession getSqlSession (){
return sqlSessionFactory.openSession();
}
}
通过部署监听者MyServletContextListener类,在上下文参数Context初始化的时候,调用BaseMapperTest类的init()方法,实现对SqlSessionFactory的初始化。
/**
* 上下文初始化参数监听者
*/
public class MyServletContextListener implements ServletContextListener {
/**
* 参数初始化
* @param servletContextEvent
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//由事件得到ServletContext
ServletContext sc = servletContextEvent.getServletContext();
String config = sc.getInitParameter("config"); //获取ServletContext初始参数
BaseMapperTest.init(config); //上下文初始化参数的时候,使用xml配置文件,初始化SqlSessionFactory
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
2.3 Model
2.3.1 Dao操作
使用Mybatis,需要将具体的sql语句入mapper.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.wyf.dao.mybatis.mapper.UserMapper">
<resultMap id="userMap" type="com.wyf.dao.mybatis.model.User">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userPassword" column="user_password"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- select查询 -->
<select id="selectById" resultMap="userMap">
select * from `user` where id = #{id}
</select>
<!-- insert数据插入 -->
<insert id="insert">
insert into `user`(
user_name, user_password, create_time)
values(#{userName}, #{userPassword}, #{createTime, jdbcType=TIMESTAMP})
</insert>
<select id="selectUser" resultType="long">
SELECT COUNT(*) from `user` WHERE user_name=#{userName} AND user_password=#{userPassword};
</select>
</mapper>
对应mapper.xml文件的映射接口如下所示:
/**
* 接口文件
*/
public interface UserMapper {
/**
* 新增用户
*
* @param sysUser
* @return 返回影响行数
*/
int insert(User sysUser);
/**
* 通过ID查询用户
* @param id
* @return
*/
User selectById(int id);
/**
* 查询符合登录条件的用户数
* @return
*/
long selectUser(User sysUser);
}
如下为对应数据库user表格的实体类:
/**
* 对应数据库user表格的实体类
*/
public class User {
private int id;
private String userName;
private String userPassword;
private Date createTime;
public int getId() {
return id;
}
public String getUserName() {
return userName;
}
public String getUserPassword() {
return userPassword;
}
public Date getCreateTime() {
return createTime;
}
public void setId(int id) {
this.id = id;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
2.3.2 Service操作
service操作中包含两个类,分别对应于登录操作以及注册操作,如下所示:
/**
* 用户登录
*/
public class LoginUser {
private String userName;
private String userPassword;
public LoginUser(String userName,String userPassword){
this.userName = userName;
this.userPassword = userPassword;
}
/**
* 登录判断
* @return
*/
public User login(){
User result = null;
SqlSession sqlSession = BaseMapperTest.getSqlSession(); //获取数据库操作对象
try{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUserName(userName);
user.setUserPassword(userPassword);
//将新建的对象插入数据库中,返回值result 是执行的SQL影响的行数
long res = userMapper.selectUser(user);
if(res>0){
result = user;
}else{
result = null;
}
} catch(Exception e){
e.printStackTrace();
} finally{
sqlSession.close(); //关闭链接
}
return result;
}
}
/**
* 用户注册
*/
public class RegistUser {
private String userName;
private String userPassword;
public RegistUser(String userName, String userPassword){
this.userName = userName;
this.userPassword = userPassword;
}
/**
* 注册
* @return
*/
public int register(){
int res = 0;
SqlSession sqlSession = BaseMapperTest.getSqlSession();
try{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUserName(userName);
user.setUserPassword(userPassword);
user.setCreateTime(new Date());
//将新建的对象插入数据库中,返回值result 是执行的SQL影响的行数
res = userMapper.insert(user);
} catch(Exception e){
res = -1;
e.printStackTrace();
} finally{
sqlSession.commit(); //提交操作结果
sqlSession.close(); //关闭链接
}
return res;
}
}
此处应该将SqlSession 及响应的操作封装到DAO层,以便更好的体现出MVC的优势。但是作为一个Javaweb示例程序,没有那么严谨,真正的项目应对Dao层及Service层有明确的界限。
2.4 View页面
首先需要有一个JSP页面,项目中的页面都属于View层。如下,是登录页面:
<%@ page isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
.h_title{position: relative;display: inline-block;}
.box_div{
width: 100%;
}
.box_main{
position: absolute;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
width: 60%;
min-width: 700px;
box-shadow: 0 0 30px #322f2f;
}
.box_main_right{
width: 50%;
height:450px;
float: left;
position: relative;
}
.box_form{
position: absolute;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
}
</style>
<title>Title</title>
</head>
<body style="text-align: center">
<div class="box_div">
<div class="box_main">
<div style="width:50%;height:450px;float: left;background: blueviolet">
<br>
<h2 class="h_title">智 能 仪 表 设 备 管 理</h2>
</div>
<div class="box_main_right">
<div class="box_form">
<div style="width: 300px">
<h3 style="float: left">登 录</h3>
<c:if test="${not empty message}">
<div style="width: 100%;height: 40px;clear: left;margin-bottom: 15px;background: rgba(252,158,158,0.3);border-radius: 5px">
<P style="font-size: 17px;color: #8a1717;line-height: 40px;">账号或密码错误!</P>
</div>
</c:if>
</div>
<form action="login.do" method="post" style="width: 300px;">
<input type="text" name="name" value="" placeholder="用户名" style="width: 100%;height: 40px;border-radius: 5px"><br><br>
<input type="password" name="pwd" value="" placeholder="密码" style="width: 100%;height: 40px;border-radius: 5px"><br><br>
<input type="submit"value="登 录" name="login" style="width: 100%;height: 35px;background: orange;color: white;font-size: 17px;border-radius: 5px">
</form>
<form action="register.jsp" style="width: 300px">
<input type="submit" value="新用户注册" style="width: 100%;height: 35px;background: deepskyblue;color:white;font-size: 17px;border-radius: 5px">
</form>
</div>
</div>
</div>
</div>
</body>
</html>
注册页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
.h_title{position: relative;display: inline-block;}
.box_div{
width: 100%;
}
.box_main{
position: absolute;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
width: 60%;
min-width: 700px;
box-shadow: 0 0 30px #322f2f;
}
.box_main_right{
width: 50%;
height:450px;
float: left;
position: relative;
}
.box_form{
position: absolute;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
}
</style>
<title>Title</title>
</head>
<body style="text-align: center">
<div class="box_div">
<div class="box_main">
<div style="width:50%;height:450px;float: left;background: blueviolet">
<br>
<h2 class="h_title">智 能 仪 表 设 备 管 理</h2>
</div>
<div class="box_main_right">
<div class="box_form">
<div style="width: 300px">
<h3 style="float: left">注 册</h3>
</div>
<form action="register.do" method="post" style="width: 300px;">
<input type="text" name="name" value="" placeholder="用户名" style="width: 100%;height: 40px;border-radius: 5px"><br><br>
<input type="password" name="pwd" value="" placeholder="密码" style="width: 100%;height: 40px;border-radius: 5px"><br><br>
<input type="submit"value="立即注册" name="login" style="width: 100%;height: 35px;background: orange;color: white;font-size: 17px;border-radius: 5px">
</form>
<form action="index.jsp" style="width: 300px">
<input type="submit" value="返回登录" style="width: 100%;height: 35px;background: deepskyblue;color:white;font-size: 17px;border-radius: 5px">
</form>
</div>
</div>
</div>
</div>
</body>
</html>
2.5 Controller(Servlet)
/**
* 创建一个简单的Servlet类
*/
public class LoginServlet extends HttpServlet {
/**
* post请求
* @param request
* @param response
* @throws IOException
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
PrintWriter out=response.getWriter();
//获取请求携带的参数
String userName = request.getParameter("name");
String userPassword = request.getParameter("pwd");
LoginUser loginUser = new LoginUser(userName,userPassword);
User user = loginUser.login();
if(user!=null){
//登录成功
request.getSession().setAttribute("user",user); //设置会话属性,会话属性与请求属性的主要区别在范围
// request.setAttribute("message", "欢迎用户"+userName); //向request域中放置信息
// request.getRequestDispatcher("/main.jsp").forward(request, response);//转发到成功页面
response.setContentType("text/html");
response.sendRedirect("main.jsp");
}else{
//验证失败
request.setAttribute("message", "用户名或密码错误,请重试!"); //向request域中放置信息
request.getRequestDispatcher("/index.jsp").forward(request, response);//转发到成功页面
}
}
}
/**
* 注册页面的servlet
*/
public class RegisterServlet extends HttpServlet {
/**
* post请求
* @param request
* @param response
* @throws IOException
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//获取jsp页面传递到服务器的参数
String userName = request.getParameter("name");
String usePassword = request.getParameter("pwd");
RegistUser registUser = new RegistUser(userName,usePassword);
if(registUser.register()>0){
//注册成功
request.getRequestDispatcher("/index.jsp").forward(request,response);
}else{
//注册失败
request.setAttribute("message","注册失败");
request.getRequestDispatcher("/index.jsp").forward(request,response);
}
}
}
为了对登录进行检查,我们使用Filter过滤器,对所有请求进行检查,以判断是否登录。
/**
* 登录检查
*/
public class LoginCheckFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//初始化
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取资源请求路径
String uri = request.getRequestURI();
String[] loginUri = {"/index.jsp","/register.jsp","/login.do","/register.do","/"};
//判断是否包含登录相关请求
for(String path:loginUri){
if (uri.endsWith(path)) {
filterChain.doFilter(request,response);
return;
}
}
//请求的url不在特定uri之内
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("user") == null) {
response.setContentType("text/html");
response.sendRedirect("index.jsp");
}else{
filterChain.doFilter(request,response);
}
}
@Override
public void destroy() {
//销毁
}
}
Filter过滤器有很多应用,如下所示,对编码进行设置:
public class EncodingFilter implements Filter {
private String encoding = "UTF-8"; // 默认编码UTF-8
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 如果用户配置了编码,则将encoding设置为用户编码
if(filterConfig.getInitParameter("ENCODING")!=null){
encoding = filterConfig.getInitParameter("ENCODING");
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 设置请求与响应的编码
servletRequest.setCharacterEncoding(encoding);
servletResponse.setCharacterEncoding(encoding);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
encoding = null;
}
}
2.6 web.xml
<!-- 部署描述文件 -->
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 上下文参数 -->
<context-param>
<param-name>config</param-name>
<param-value>mybatis-config.xml</param-value>
</context-param>
<!-- 声明过滤器 -->
<filter>
<filter-name>Encoding</filter-name>
<filter-class>com.wyf.filter.EncodingFilter</filter-class>
<init-param>
<param-name>ENCODING</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter>
<filter-name>LoginCheck</filter-name>
<filter-class>com.wyf.filter.LoginCheckFilter</filter-class>
</filter>
<!-- 过滤器映射 -->
<filter-mapping>
<filter-name>Encoding</filter-name>
<url-pattern>/*</url-pattern> <!-- url-pattern 元素定义了哪些web应用资源要使用该过滤器 --> <!-- /*是对所有的文件进行拦截 -->
</filter-mapping>
<filter-mapping>
<filter-name>LoginCheck</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 注册监听者 -->
<listener>
<listener-class>com.wyf.listener.MyServletContextListener</listener-class>
</listener>
<!-- servlet部署 -->
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.wyf.servlet.LoginServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>register</servlet-name>
<servlet-class>com.wyf.servlet.RegisterServlet</servlet-class>
</servlet>
<!-- 将servlet与url映射 -->
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>register</servlet-name>
<url-pattern>/register.do</url-pattern>
</servlet-mapping>
</web-app>
三、结束
本文主要以一个登录注册操作,对Javaweb的基础内容,servlet、jsp、session、listener、Filter以及MVC结构进行简单的总结。