Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目

目录

引入

效果图

Eclipse EE上集成tomcat

1、添加一个Server

2、 添加servlet相关的jar包

3、设置端口号和context-path

Dao层封装jdbc,简化数据库操作

1、导入对应版本的驱动jar包

2、编写数据库连接的自定义配置文件

3、编写获取数据库连接的类

4、编写基础的数据库操作工具类

5、编写数据库表对应的实体类

编写Servlet

1、生成验证码图片的servlet

2、登录的servlet

3、退出登录的servlet

编写Service层

编写登录过滤器


引入

其实这源于软件体系结构这门课程的实验作业,体验B/S体系结构风格,原实验要求我们实现简单的注册登录功能。然而学校的课程体系结构很迷,php和java web等技术型课程全是选修,而且这学期的java web课程,跳过了servlet直接教SSM。

所以实验课老师给出了php和java的代码,但是代码比较简单,不考虑功能扩展。我还是选择自己写吧。。。

在Eclipse EE上集成tomcat,自己封装原生 servlet 和 jdbc,实现登录demo。同时,这样的封装能够支持在不用任何java 框架的情况下,实现一个小型web项目。

效果图

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第1张图片

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第2张图片

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第3张图片

Eclipse EE上集成tomcat

1、添加一个Server

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第4张图片

2、 添加servlet相关的jar包

tomcat是支持servlet和jsp的、用java写的一个web应用服务器。servlet和jsp相关的jar包,并不是jdk自带,而是来源于tomcat的类库(安装目录下的lib目录)。我们需要为新建的web Project添加tomcat的运行类库,否则在project中没办法使用servlet和jsp。

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第5张图片   

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第6张图片

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第7张图片   Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第8张图片

3、设置端口号和context-path

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第9张图片   

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第10张图片

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第11张图片

Dao层封装jdbc,简化数据库操作

1、导入对应版本的驱动jar包

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第12张图片

2、编写数据库连接的自定义配置文件

Eclipse集成tomcat,不用任何框架,自己封装原生servlet和jdbc,实现小型web项目_第13张图片

JDBC.properties

# 中心仓库:https://mvnrepository.com/

# mysql驱动jar包
# 8.0.16:https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.16
# 5.1.47:https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.47

# 数据库连接的基本配置
user=root
password=ysqJYKL2010
# 不设置useSSL会有警告
url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=true
driver=com.mysql.jdbc.Driver

3、编写获取数据库连接的类

DbConnector.java

package dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import utils.CloseUtil;

/**
 * 数据库连接类 初始化数据库连接配置,获取数据库连接
 * 
 * @author passerbyYSQ
 * @date 2020-9-29 14:19:37
 */
public class DbConnector {

	/**
	 * 连接数据库的基本配置,从配置文件读入。
	 */
	private static String user;
	private static String password;
	// 数据库连接协议的url
	private static String url;
	// 数据库驱动包的类路径
	private static String driver;

	// 存储配置文件的数据结构Properties
	private static Properties configProp = new Properties();

	static {
		InputStream inputStream = null;
		try {
			ClassLoader classLoader = DbConnector.class.getClassLoader();
			// 在src目录下获取JDBC.properties的输入流
			inputStream = classLoader.getResourceAsStream("JDBC.properties");

			if (inputStream == null) {
				throw new RuntimeException("配置文件不存在");
			}

			// 将配置文件加载到configProp
			configProp.load(inputStream);

			user = configProp.getProperty("user");
			password = configProp.getProperty("password");
			url = configProp.getProperty("url");
			driver = configProp.getProperty("driver");

			// 注册数据库驱动包
			Class.forName(driver);
//			System.out.println("注册数据库驱动包成功!");

		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			CloseUtil.close(inputStream);
		}
	}

	/**
	 * 获取一个数据库连接对象
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		try {
			return DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 释放资源
	 * 
	 * @param stmt 执行sql语句的对象
	 * @param conn 数据库连接对象
	 */
	public static void close(Statement stmt, Connection conn) {
		close(null, stmt, conn);
	}

	/**
	 * 释放资源的重载形式
	 * 
	 * @param rs   结果集对象
	 * @param stmt 执行sql语句的对象
	 * @param conn 数据库连接对象
	 */
	public static void close(ResultSet rs, Statement stmt, Connection conn) {
		try {
			if (rs != null) {
				rs.close();
			}
			if (stmt != null) {
				stmt.close();
			}
			if (conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

4、编写基础的数据库操作工具类

DaoUtil.java

package dao;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Date;
 
/**
 * 基础的数据库操作工具类
 * 简化通用的基础的增删改查的操作,与具体业务逻辑无关
 * 
 * @author	passerbyYSQ
 * @date	2020-9-29 14:51:16
 */
public class DaoUtil {
	/**
	 * 通用的查询操作
	 * 
	 * @param 	查询返回的数据,基类中无法确定,需要通过泛型抛给子类
	 * @param sql	sql语句(里面有占位符)
	 * @param pack	一个接口,该接口有个抽象方法将ResultSet回调给子类,让子类来处理ResultSet中数据
	 * @param args	与占位符一一对应的参数值
	 * @return		封装后的查询结果
	 */
	public static  T select(String sql, PackResult pack, Object... args) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet resultSet = null;
		T result = null;
		try {
			conn = DbConnector.getConnection();
			pstmt = conn.prepareStatement(sql);
			
			// 将参数设置到sql语句的占位符中
			setValue(pstmt, args);
			
			resultSet = pstmt.executeQuery();
 
			// 由于不同的表对应不同的实体类,在基类中无法实现将resultSet封装成对应的实体类返回
			// 故只能通过接口中的回调函数交给子类去实现
			result = pack.onResultReturn(resultSet);
			return result;
 
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			DbConnector.close(resultSet, pstmt, conn);
		}
	}
	
	/**
	 * 获取结果集数量
	 * @param from	from部分,传递时不需要带"from"。之所以传递from部分,而不是表名,是考虑到多表查询
	 * @param where	where部分,传递时不需要带"where",里面含有占位符
	 * @param args	代替占位符的参数值
	 * @return		记录数
	 */
	public static int getCount(String from, String where, Object... args) {
		String sql = "select count(*) from " + from + " where " + where;
 
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet resultSet = null;
		try {
			conn = DbConnector.getConnection();
			pstmt = conn.prepareStatement(sql);
			
			// 将参数设置到sql语句的占位符中
			setValue(pstmt, args);
			
			resultSet = pstmt.executeQuery();
			// 获取失败返回-1
			return resultSet.next() ? resultSet.getInt(1) : -1;
 
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			DbConnector.close(resultSet, pstmt, conn);
		}
 
	}
	
	/**
	 *  插入记录,比较适用于插入一条或几条数据。不适合插入一个集合的数据
	 * @param sql	sql语句(里面有占位符)
	 * @param args	与占位符一一对应的参数值
	 * @return		受影响的行数
	 */
	public static int insertOrUpdateOrDelete(String sql, Object... args) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		int count = 0;
		try {
			conn = DbConnector.getConnection();
			pstmt = conn.prepareStatement(sql);
			
			// 将参数设置到sql语句的占位符中
			setValue(pstmt, args);
			//System.out.println(pstmt.toString());
			
			// 受影响的行数
			count = pstmt.executeUpdate();
			return count;
			
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			DbConnector.close(pstmt, conn);
		}
	}
	
	/**
	 * 事务操作
	 * 如果实现类不使用回调的PreparedStatement和ResultSet的引用,
	 * 而是选择自己new新的实例,那么就需要自己负责资源关闭
	 * 
	 * @param sqlEntitys	
	 */
	public static void updateInTransaction(MoreUpdate moreUpdate) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			conn = DbConnector.getConnection();
			conn.setAutoCommit(false); // 开启事务(手动提交)
			
			moreUpdate.updateActions(conn, pstmt, rs);
			
			conn.commit(); // 提交事务
			
		} catch (SQLException e) {
			e.printStackTrace();
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} finally {
			DbConnector.close(rs, pstmt, conn);
		}
	}
	
	/**
	 * 插入一条记录。如果主键是自增长的,返回该条记录的id
	 * 注意:主键必须是自增长的!!!
	 * @param sql
	 * @param args
	 * @return
	 */
	public static int insertReturnId(String sql, Object... args) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			conn = DbConnector.getConnection();
			pstmt = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
 
			// 将参数设置到sql语句的占位符中
			setValue(pstmt, args);
			//System.out.println(pstmt.toString());
 
			pstmt.executeUpdate();
			// 获取最后插入的记录的自增长id。修改是不会返回的
			rs = pstmt.getGeneratedKeys();
 
			return rs.next() ? rs.getInt(1) : 0;
 
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			DbConnector.close(rs, pstmt, conn);
		}
	}
 
 
	// 将参数设置到sql语句的占位符中
	public static void setValue(PreparedStatement pstmt, Object... args) throws SQLException {
		for (int i = 0; i < args.length; i++) {	
			if (args[i] instanceof String) { // 主要
				pstmt.setString(i + 1, (String) args[i]);
			} else  if (args[i] instanceof Integer) { // 主要
				int num = (Integer) args[i];
				pstmt.setInt(i + 1, num);
			} else  if (args[i] instanceof LocalDateTime) { // 主要
				pstmt.setTimestamp(i + 1, Timestamp.valueOf((LocalDateTime) args[i]));
			} else if (args[i] instanceof Date) { // 主要 ((Date) args[i]).getTime()
				pstmt.setTimestamp(i + 1, new Timestamp(((Date) args[i]).getTime()));
			} else if (args[i] instanceof BigDecimal) {
				pstmt.setBigDecimal(i + 1, (BigDecimal) args[i]);
			} else if (args[i] instanceof Double) {
				pstmt.setDouble(i + 1, (Double) args[i]);
			} else if (args[i] instanceof Float) {
				pstmt.setFloat(i + 1, (Float) args[i]);
			} else if (args[i] instanceof Boolean) {
				pstmt.setBoolean(i + 1, (boolean) args[i]);
			} else {
				throw new RuntimeException("DaoUtil类暂不支持set该数据类型");
			}
		}
	}
	
	public interface PackResult {
		// 将ResultSet回调给子类,让调用者来处理ResultSet中的数据(转换成特定的pojo)
        T onResultReturn(ResultSet rs) throws Exception; 
	}
	
	public interface MoreUpdate {
		/**
		 * 多种update相关的操作
		 * 
		 * 如果实现类不使用回调的pstmt,而是选择自己new新的pstmt,那么
		 * 就需要自己负责pstmt的关闭操作。
		 * 
		 * @param conn
		 * @param pstmt		回调给实现类的是null
		 * @throws SQLException 
		 */
		void updateActions(Connection conn, PreparedStatement pstmt, ResultSet rs) throws SQLException;
	}
	
}

5、编写数据库表对应的实体类

这里只有一张 tb_user 表,对应 User 实体类

User.java

package entity;

import java.time.LocalDateTime;

/**
 * 用户实体类
 * 数据库表所对应的实体类
 * 
 * @author	passerbyYSQ
 * @date	2020-9-30 20:46:28
 */
public class User {
	
	private Integer userId;
	
	private String username;
	
	private String password;
	
	// 最后一次登录的时间
	private LocalDateTime lastLoginTime;
	
	public User(Integer userId, String username) {
		this.userId = userId;
		this.username = username;
	}

	public Integer getUserId() {
		return userId;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

	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;
	}

	public LocalDateTime getLastLoginTime() {
		return lastLoginTime;
	}

	public void setLastLoginTime(LocalDateTime lastLoginTime) {
		this.lastLoginTime = lastLoginTime;
	}
}

编写Servlet

1、生成验证码图片的servlet

VerificationCodeServlet.java

package servlet;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 生成简单的验证码图片
 * 
 * @author	passerbyYSQ
 * @date	2020-9-30 20:24:02
 */
@WebServlet("/verification_code")
public class VerificationCodeServlet extends HttpServlet {
	private static final long serialVersionUID = 2523187418823246381L;
	
	private Integer width = 100;
	private Integer height = 40;
	private Integer charCnt = 4; // 验证码图片中字符的个数
	private Integer lineCnt = 8;

	private Random r = new Random(); // 用于生产随机数

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 创建图片对象
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

		Graphics g = image.getGraphics();
				
		// 填充背景色
		g.setColor(getBgColor());
		g.fillRect(0, 0, width, height);

		g.setFont(new Font("黑体", Font.BOLD, 30));
		// 写字符
		StringBuilder sbd = new StringBuilder();
		for (int i = 1; i <= charCnt; i++) {
			g.setColor(getCharColor());
			int x = width / (charCnt + 2) * i;
			int y = height * 2 / 3;
			char c = getRandomChar();
			sbd.append(c);
			g.drawString(c + "", x, y);
		}
		request.getSession().setAttribute("verification_code", sbd.toString());

		for (int i = 1; i < lineCnt; i++) {
			int x1 = r.nextInt(width);
			int y1 = r.nextInt(height);

			int x2 = r.nextInt(width);
			int y2 = r.nextInt(height);

			g.setColor(getLineColor());
			g.drawLine(x1, y1, x2, y2);
		}

		// 输出图片
		ImageIO.write(image, "jpg", response.getOutputStream());
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doPost(request, response);
	}

	/**
	 * 生成一个在区间[low, high]的随机整数
	 * 
	 * @param low
	 * @param high
	 * @return
	 */
	private int getInt(int low, int high) {
		return (low + r.nextInt(high - low + 1));
	}

	/**
	 * 生成一种背景色(相对字符颜色来说较浅)
	 * 
	 * @return
	 */
	private Color getBgColor() {
		int red = getInt(170, 255);
		int green = getInt(170, 255);
		int blue = getInt(170, 255);
		return new Color(red, green, blue);
	}

	/**
	 * 生成一种干扰线的颜色
	 * 
	 * @return
	 */
	private Color getLineColor() {
		int red = getInt(85, 169);
		int green = getInt(85, 169);
		int blue = getInt(85, 169);
		return new Color(red, green, blue);
	}

	/**
	 * 生成一种字符颜色(相对字体颜色较深)
	 * 
	 * @return
	 */
	private Color getCharColor() {
		int red = getInt(0, 84);
		int green = getInt(0, 84);
		int blue = getInt(0, 84);
		return new Color(red, green, blue);
	}

	/**
	 * 生成一个随机字符
	 * 
	 * @return
	 */
	private char getRandomChar() {
		final String chs = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
		return chs.charAt(r.nextInt(chs.length()));
	}

}

2、登录的servlet

LoginServlet.java

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import entity.User;
import service.AccountService;
import service.impl.AccountServiceImpl;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1821754212240924673L;
	
	private AccountService accountService = new AccountServiceImpl();

	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		doPost(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		// 获取username
		String username = request.getParameter("username");
		// 获取密码
		String password = request.getParameter("password");
		// 简单的参数判断
		if (username == null || "".equals(username)) {
			request.setAttribute("login_error", "用户名不能为空");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
			return;
		}
		if (password == null || "".equals(password)) {
			request.setAttribute("login_error", "密码不能为空");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
			return;
		}
		
		// 获取用户输入的验证码
		String codeByUser = request.getParameter("verification_code");
		HttpSession session = request.getSession();
		// 取出正确的验证码
		String correctCode = (String) session.getAttribute("verification_code");
		// 验证码是一次性的,取出后失效
		session.removeAttribute("verification_code");
		
		if (codeByUser == null || "".equals(codeByUser)
				|| !codeByUser.equalsIgnoreCase(correctCode)) {
			request.setAttribute("login_error", "验证码错误");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
			return;
		}
		
		User user = accountService.login(username, password);
		if (user == null) {
			request.setAttribute("login_error", "用户名或者密码错误");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
		}
		
		// 登录成功
		session.setAttribute("username", user.getUsername()); 
		response.sendRedirect(request.getContextPath() + "/index.jsp");
	}
}

3、退出登录的servlet

LogoutServlet.java

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    
	private static final long serialVersionUID = 5967081510030254471L;
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		HttpSession session = request.getSession();
		String username = (String) session.getAttribute("username");
		
		if (username != null) {
			session.removeAttribute("username");
		}
		response.sendRedirect(request.getContextPath() + "/login.jsp");
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

编写Service层

AccountServiceImpl.java

package service.impl;

import java.sql.ResultSet;
import java.time.LocalDateTime;

import dao.DaoUtil;
import entity.User;
import service.AccountService;

/**
 * @author	passerbyYSQ
 * @date	2020-9-30 20:50:33
 */
public class AccountServiceImpl implements AccountService {

	@Override
	public User login(String username, String password) {
		String sql = "select * from tb_user where username=? and password=?";
		User user = DaoUtil.select(sql, new DaoUtil.PackResult() {
			@Override
			public User onResultReturn(ResultSet rs) throws Exception {
				if (!rs.next()) {
					return null;
				}
				int userId = rs.getInt("id");
				String username = rs.getString("username");
				// 密码不需要取出
				return new User(userId, username);
			}
		}, username, password);
		
		// 登录成功,更新最后一次登录的时间
		if (user != null) {
			sql = "update tb_user set last_login_time=? where id=?";
			DaoUtil.insertOrUpdateOrDelete(sql, LocalDateTime.now(), user.getUserId());
		}
		
		return user;
	}

}

编写登录过滤器

非登录用户不能访问主页,已登录用户不能访问登录页面

LoginFilter.java 

package filter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 拦截器,用于验证是否已经登录
 * 
 * @author	passerbyYSQ
 * @date	2020-10-1 16:28:20
 */
@WebFilter(
		filterName = "loginFilter",
		// 实际上可以把需要拦截的页面分类到某个目录下,某些api也分类,
		// 这样拦截的针对性更强,需要排除的也更少
		urlPatterns = "/*", // 拦截所有url
		initParams = {
			// 不拦截某些页面、api、静态资源
			@WebInitParam(name = LoginFilter.EXECUDED_PAGE, value="login.jsp"),
			@WebInitParam(name = LoginFilter.EXECUDED_API, 
				value="login;logout;verification_code"),
			@WebInitParam(name = LoginFilter.EXECUDED_RESOURCE, 
				value=".jpg;.png;.jpeg;.css;.js"),
			@WebInitParam(name = "encoding", value = "utf-8"), // 编码
		}		
)
public class LoginFilter implements Filter {
	// 页面
	public static final String EXECUDED_PAGE = "EXECUDED_PAGE";
	// API
	public static final String EXECUDED_API = "EXECUDED_API";
	// 静态资源
	public static final String EXECUDED_RESOURCE = "EXECUDED_RESOURCE";
	
	private FilterConfig filterConfig;
	private Set execudedUrl = new HashSet<>();

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		processInitParamArray(EXECUDED_PAGE);
		processInitParamArray(EXECUDED_API);
		processInitParamArray(EXECUDED_RESOURCE);
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
	
		// 设置字符集,防止post表单乱码
		String encoding = filterConfig.getInitParameter("encoding");
		if (encoding == null) {
			encoding = "utf-8";
		}
		httpRequest.setCharacterEncoding(encoding);
		
		// 判断是否需要拦截。默认需要拦截
		boolean isNeedFilter = true;
		String uri = httpRequest.getRequestURI();
		for (String str : execudedUrl) {
			if (uri.endsWith(str)) { // 注意用endsWith
				// 不需要拦截
				isNeedFilter = false;
				break;
			}
		}
		
		HttpSession session = httpRequest.getSession();
		String username = (String) session.getAttribute("username");
		
		// 不需要拦截,放行
		if (!isNeedFilter) {
			if ((uri.endsWith("login") || uri.endsWith("login.jsp")) && username != null) {
				// 已登录,不能访问登录页面
				httpResponse.sendRedirect(httpRequest.getContextPath() + "/index.jsp");
			} else {
				chain.doFilter(httpRequest, httpResponse);
			}
		} else {
			if (username != null) {
				// 已登录,放行
				chain.doFilter(httpRequest, httpResponse);
			} else {
				// 未登录。重定向到登录界面
				httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.jsp");
			}
		}
	}

	@Override
	public void destroy() {
		filterConfig = null;
		execudedUrl.clear();
	}
	
	private void processInitParamArray(String key) {
		String param = filterConfig.getInitParameter(key);
		if (param != null) {
			String[] split = param.split(";");
			for (String str : split) {
				// 静态资源
				if (!"".equals(str)) {
					execudedUrl.add(str);
				}
			}
		}
	}

}

好了,完结撒花!!!

前端页面比较简单,我就不贴出来了。有的人可能会问,不就是一个登录功能,用得着写这么多吗?注意,我们的着眼点是:封装原生的servlet和jdbc,能够扩展支持小型的web项目开发。只不过我们以登录功能作为demo而已。这对于学习过java web但又没接触过java框架的同学,有比较大的借鉴意义。

你可能感兴趣的:(Java,封装,jdbc,servlet,java,web)