SMBMS
SMBMS
源码链接
链接:https://pan.baidu.com/s/19fkYNtJ_VXD_wnG_dfC9ug
提取码:slz1
数据库:

项目搭建
- 搭建一个maven webapp模板项目
- 配置Tomcat
- 测试项目能否运行起来
- 导入所依赖的jar包:
- servlet 实现servlet接口
- jsp jsp标签
- mysql-connector-java java连接数据库
- jstl jsp标签库
- standard jsp标签库所依赖的包
<dependencies>
<!--Servlet依赖-->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--JSP依赖-->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--JSTL表达式依赖-->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--standard标签库-->
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!--连接数据库-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
- 创建项目包结构

-
编写实体类
ORM映射:表----->类
-
编写基本公共类
-
数据库配置文件
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf8&useSSL=false username=root password=123456 -
编写数据库的公共类
#db.properties driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf8&useSSL=false username=root password=123456package com.lin.dao; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; //操作数据库的公共类 public class BaseDao { private static String driver; private static String url; private static String username; private static String password; //静态代码块,类加载的时候就初始化了。 static { Properties properties = new Properties(); //通过类加载器读取对应的资源。 InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties"); try { properties.load(is); } catch (IOException e) { e.printStackTrace(); } driver = properties.getProperty("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); } //获取数据库的链接。 public static Connection getConnection(){ Connection connection = null; try { Class.forName(driver); connection = DriverManager.getConnection(url,username,password); } catch (Exception e) { e.printStackTrace(); } return connection; } //编写查询工具类。 public static ResultSet execute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet, String sql, Object[] params) throws SQLException { //预编译的sql,在后面直接执行就可以了 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length; i++) { //setObject,占位符从1开始,但是我们的数组是从0开始的。 preparedStatement.setObject(i+1,params[i]); } resultSet = preparedStatement.executeQuery(); return resultSet; } //编写增删改公共方法。 public static int execute(Connection connection,PreparedStatement preparedStatement,String sql,Object[] params) throws SQLException { preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length; i++) { //setObject,占位符从1开始,但是我们的数组是从0开始的。 preparedStatement.setObject(i+1,params[i]); } int updateRows = preparedStatement.executeUpdate(); return updateRows; } //释放资源。 public static boolean closeResourse(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet){ boolean flag = true; if (resultSet != null){ try { resultSet.close(); //GC回收 resultSet = null; } catch (SQLException throwables) { throwables.printStackTrace(); flag = false; } } if (preparedStatement != null){ try { preparedStatement.close(); //GC回收 preparedStatement = null; } catch (SQLException throwables) { throwables.printStackTrace(); flag = false; } } if (connection != null){ try { connection.close(); //GC回收 connection = null; } catch (SQLException throwables) { throwables.printStackTrace(); flag = false; } } return false; } } -
编写字符编码过滤器
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html"); filterChain.doFilter(servletRequest,servletResponse); }
-
-
导入静态资源
登录功能实现

1.编写前端页面----------->login.jsp
2.设置首页
web.xml中
<!--设置欢迎页面-->
<welcome-file-list>
<welcome-file>/login.jsp</welcome-file>
</welcome-file-list>
3.编写Dao层,得到用户登录的接口
public User getLoginUser(Connection connection, String userCode) throws SQLException;
4.编写Dao接口的实现类
public class UserDaoImpl implements UserDao{
//通过userCode和userPassword,得到要登陆的用户。
public User getLoginUser(Connection connection, String userCode) throws SQLException {
PreparedStatement pstm = null;
ResultSet rs = null;
User user = null;
if(connection != null){
String sql = "select * from smbms_user where userCode=?";
Object[] params = {userCode};
rs = BaseDao.execute(connection, pstm, rs, sql, params);
if(rs.next()){
user = new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));
}
BaseDao.closeResourse(null,pstm,rs);
}
return user;
}
}
5.业务层接口
//用户登录
public User login(String userCode,String password);
6.业务层实现类
public class UserServiceImpl implements UserService{
//业务层都会调用Dao层,所以我们要引入Dao层
private UserDao userDao;
public UserServiceImpl(){
userDao = new UserDaoImpl();
}
@Override
public User login(String userCode, String password) {
Connection connection = null;
User user = null;
try {
connection = BaseDao.getConnection();
//通过业务层调用对应的具体的数据库操作
user = userDao.getLoginUser(connection,userCode);
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeResourse(connection,null,null);
}
return user;
}
@Test
public void test(){
UserServiceImpl userService = new UserServiceImpl();
User admin = userService.login("admin", "123456");
System.out.println(admin.getUserPassword());
}
}
7.编写Servlet
package com.lin.servlet.user;
import com.lin.pojo.User;
import com.lin.service.user.UserServiceImpl;
import com.lin.util.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
//Servlet:控制层,调用业务层代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Login-Servlet--start...");
//获取用户名和密码
String userCode = req.getParameter("userCode");
String userPassword = req.getParameter("userPassword");
//和数据库中的密码进行对比,调用业务层;
UserServiceImpl userService = new UserServiceImpl();
User user = userService.login(userCode, userPassword);//这里已经把登录的人给查出来了
if(user!=null){//查到了此人可以登陆
//将用户的信息放进session中
req.getSession().setAttribute(Constants.USER_SESSION,user);
//跳转到内部主页
resp.sendRedirect("jsp/frame.jsp");
}else{//没有此人,无法登陆
//转发回登录页面,并且提示用户名或者密码错误
req.setAttribute("error","用户名或者密码不正确");
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
8.注册Servlet
<!--Servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.lin.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
9.测试访问,确保以上功能成功!
登录功能优化
注销功能(退出系统):
思路:移除session,返回登录页面
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//移除用户的session
req.getSession().removeAttribute(Constants.USER_SESSION);
resp.sendRedirect(req.getContextPath()+"/login.jsp");//返回登录页面
}
注册xml
<!--注销-->
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.lin.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
登录拦截优化(退出系统之后在地址栏输入登录后的网站不能进入系统)
编写一个过滤器并注册
public class SysFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//过滤器,从session中获取用户
User user =(User) request.getSession().getAttribute(Constants.USER_SESSION);
if(user == null){//已经被移除或者注销了,或者未登录
response.sendRedirect("/smbms/error.jsp");
}else{
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
<!--用户登录过滤器-->
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.lin.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
密码修改
1.首先导入前端素材/jsp/pwdmodify.jsp
2.写项目实现增删改查,建议从底层向上去编写

3.UserDao接口
//修改当前用户密码
public int updatePwd(Connection connection,int id,String password)throws SQLException;
4.UserDao接口实现类
//修改当前用户密码
public int updatePwd(Connection connection, int id, String password) throws SQLException {
PreparedStatement pstm = null;
int execute = 0;
if(connection!=null){
String sql = "update smbms_user set userPassword = ? where id = ?";
Object params[] = {password,id};
execute = BaseDao.execute(connection, pstm, sql, params);
BaseDao.closeResourse(null,pstm,null);
}
return execute;
}
5.UserService接口
//根据用户id修改密码。
public boolean updatePwd(int id,String password);
6.UserService实现类
public boolean updatePwd(int id, String password) {
Connection connection = null;
boolean flag = false;
//修改密码
try {
connection = BaseDao.getConnection();
if(userDao.updatePwd(connection,id,password)>0){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeResourse(connection,null,null);
}
return flag;
}
7.编写控制层:UserServlet
public void updatePwd(HttpServletRequest req, HttpServletResponse resp){
//从Session里面拿ID;
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
String newpassword = req.getParameter("newpassword");
boolean flag = false;
if(o!=null && !StringUtils.isNullOrEmpty(newpassword)){
UserServiceImpl userService = new UserServiceImpl();
flag = userService.updatePwd(((User) o).getId(), newpassword);
if(flag){
req.setAttribute("message","修改密码成功,请退出,使用新密码登录");
//密码修改成功,移除当前session
req.getSession().removeAttribute(Constants.USER_SESSION);
}else{
req.setAttribute("message","修改密码失败");
}
}else{
req.setAttribute("message","新密码有问题");
}
try {
req.getRequestDispatcher("/jsp/pwdmodify.jsp").forward(req,resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
8.注册servlet
<!--修改密码-->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.lin.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
优化密码修改使用AJAX
1.使用json实现前后端交互
阿里巴巴的fastjson
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
</dependency>
2.后台代码(Servlet)
//验证旧密码,session中有用户的密码
public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
//从Session里面拿ID;
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
String oldpassword = req.getParameter("oldpassword");
//万能的Map:结果集
Map<String, String> resultMap = new HashMap<>();
if(o==null){//session失效或者过期了
resultMap.put("result","sessionerror");
}else if(StringUtils.isNullOrEmpty(oldpassword)){//输入的密码为空
resultMap.put("result","error");
}else{
String userPassword = ((User) o).getUserPassword();//session中用户的密码
if(oldpassword.equals(userPassword)){
resultMap.put("result","true");
}else{
resultMap.put("result","false");
}
}
//因为前端使用json,需要将map转换为json格式,让前端接收
try {
resp.setContentType("application/json");
PrintWriter writer = resp.getWriter();
//JSONArray 阿里巴巴的工具类,转换格式
/*
resultMap = ["result","sessionerror","result","error"]
Json格式 = {key:value}
*/
writer.write(JSONArray.toJSONString(resultMap));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
js文件引入中文乱码的解决方法:
将js文件设置为 使用UTF-8-BOM编码,保存js文件后清除浏览器缓存即可
用户管理实现

1.导入分页的工具类util/pageSupport.java
面向对象三大特性:
- 封装(属性私有,get/set方法,在set方法中限定一些不安全的情况)
- 继承
- 多态
2.用户列表页面导入userlist.jsp和rollpage.jsp
1.获取用户数量
1.UserDao
//根据用户名或者角色查询用户总数
public int getUserCount(Connection connection,String username,int userRole) throws SQLException;
2.UserDaoImpl
//根据用户名或者角色查询用户总数(最难理解的sql)
public int getUserCount(Connection connection, String username, int userRole) throws SQLException {
PreparedStatement pstm = null;
ResultSet rs = null;
int count = 0;
if(connection!=null){
StringBuffer sql = new StringBuffer();
sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id");
ArrayList<Object> list = new ArrayList<Object>();//存放我们的参数params
if(!StringUtils.isNullOrEmpty(username)){
sql.append(" and u.userName = ?");
list.add("%"+username+"%");//index:0 %用于模糊查询
}
if(userRole >0){
sql.append(" and u.userRole = ?");
list.add(userRole);//index:1
}
//怎么把list转换为数组。
Object[] params = list.toArray();
System.out.println("UserDaoImpl-->getUserCount的sql语句:"+sql.toString());//输出最后完整的sql语句
rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params);
if (rs.next()){
count = rs.getInt("count");//从结果集中获取最终的参数。
}
BaseDao.closeResourse(null,pstm,rs);
}
return count;
}
3.UserService
//查询记录数
public int getUserCount(String username,int userRole);
4.UserServiceImpl
//查询记录数
public int getUserCount(String username, int userRole) {
Connection connection = null;
int count = 0;
try {
connection = BaseDao.getConnection();
count = userDao.getUserCount(connection, username, userRole);
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeResourse(connection,null,null);
}
return count;
}
2.获取用户列表
1.userdao
//通过条件查询用户。(分页)
List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws Exception;
2.userdaoimpl
//通过条件查询用户。(分页)
@Override
public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws Exception {
PreparedStatement pstm = null;
ResultSet rs = null;
List<User> userList = new ArrayList<User>();
if (connection != null) {
StringBuffer sql = new StringBuffer();
sql.append("select u.*,r.roleName as userRoleName from smbms_user u,smbms_role r where u.userRole = r.id");
List<Object> list = new ArrayList<Object>();
if (!StringUtils.isNullOrEmpty(userName)) {
sql.append(" and u.userName like ?");
list.add("%" + userName + "%");
}
if (userRole > 0) {
sql.append(" and u.userRole = ?");
list.add(userRole);
}
//在mysql数据库中,分页使用limit-->startIndex,pageSize ; 总数
//开始的索引=(当前的页码-1)*每页显示的条数
sql.append(" order by creationDate DESC limit ?,?");
currentPageNo = (currentPageNo - 1) * pageSize;
list.add(currentPageNo);
list.add(pageSize);
Object[] params = list.toArray();
System.out.println("getUserList的sql语句:" + sql.toString());
rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params);
while (rs.next()) {
User _user = new User();
_user.setId(rs.getInt("id"));
_user.setUserCode(rs.getString("userCode"));
_user.setUserName(rs.getString("userName"));
_user.setGender(rs.getInt("gender"));
_user.setBirthday(rs.getDate("birthday"));
_user.setPhone(rs.getString("phone"));
_user.setUserRole(rs.getInt("userRole"));
_user.setUserRoleName(rs.getString("userRoleName"));
userList.add(_user);
}
BaseDao.closeResourse(null, pstm, rs);
}
return userList;
}
3.userService
//根据条件查询用户列表.
public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize);
4.userServiceImpl
//根据条件查询用户列表
public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) {
Connection connection = null;
List<User> userList = null;
try {
connection = BaseDao.getConnection();
userList = userDao.getUserList(connection, queryUserName, queryUserRole, currentPageNo, pageSize);
} catch (Exception e) {
e.printStackTrace();
} finally {
BaseDao.closeResourse(connection, null, null);
}
return userList;
}
3.获取角色操作
为了职责统一,可以把角色的操作单独放在一个包中,和POJO类对应
1.RoleDao
//获取角色列表。
public List<Role> getRoleList(Connection connection)throws SQLException;
2.RoleDaoImpl
//获取角色列表。
@Override
public List<Role> getRoleList(Connection connection) throws SQLException {
PreparedStatement pstm = null;
ResultSet resultSet = null;
ArrayList<Role> rolelist = new ArrayList<>();
if (connection!=null){
String sql = "select * from smbms_role";
Object[] params = {};
resultSet = BaseDao.execute(connection, pstm, resultSet, sql, params);
while (resultSet.next()){
Role role = new Role();
role.setId(resultSet.getInt("id"));
role.setRoleCode(resultSet.getString("roleCode"));
role.setRoleName(resultSet.getString("roleName"));
rolelist.add(role);
}
BaseDao.closeResourse(null,pstm,resultSet);
}
return rolelist;
}
3.RoleService
//获取角色列表。
public List<Role> getRoleList();
4.RoleServiceImpl
public class RoleServiceImpl implements RoleService{
//引入Dao。
private RoleDao roleDao;
public RoleServiceImpl() {
roleDao = new RoleDaoImpl();
}
//获取角色列表。
@Override
public List<Role> getRoleList() {
Connection connection = null;
List<Role> roleList = null;
try {
connection = BaseDao.getConnection();
roleList = roleDao.getRoleList(connection);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
BaseDao.closeResourse(connection,null,null);
}
return roleList;
}
}
4.用户显示servlet
- 获取前端参数,其实就是为了查询做准备
- 实现分页操作,需要用到页面大小,总页数,总条数,页面数...
- 获取用户列表
- 根据前端需要的参数,进行传参
- 返回前端
//查询用户列表(重点,难点)。
public void query(HttpServletRequest req, HttpServletResponse resp){
//从前端获取数据:
String queryUserName = req.getParameter("queryname");
String temp = req.getParameter("queryUserRole");
String pageIndex = req.getParameter("pageIndex");
int queryUserRole = 0;
//获取用户列表
UserServiceImpl userService = new UserServiceImpl();
List<User> userList = null;
//第一次走这个请求,一定是第一页,页面大小固定的:
int pageSize = 5;//可以把这个配置到配置文件中,方便后期修改;
int currentPageNo = 1;
if (queryUserName==null){
queryUserName = "";
}
if (temp!=null && !temp.equals("")){
queryUserRole = Integer.parseInt(temp); //给查询赋值!0,1,2,3
}
if (pageIndex!=null){
currentPageNo = Integer.parseInt(pageIndex);
}
//获取用户的总量(分页:上一页,下一页的情况)
int totalCount = userService.getUserCount(queryUserName,queryUserRole);
//总页数支持。
PageSupport pageSupport = new PageSupport();
pageSupport.setCurrentPageNo(currentPageNo);
pageSupport.setPageSize(pageSize);
pageSupport.setTotalCount(totalCount);
int totalPageCount = pageSupport.getTotalPageCount();//总共有几页。
//控制首页和尾页。
//如果页面小于1,就显示第一页的东西。
if (currentPageNo<1){
currentPageNo = 1;
}else if (currentPageNo>totalPageCount){//当前页面大于了最后一页。
currentPageNo = totalPageCount;
}
//获取用户列表展示。
userList = userService.getUserList(queryUserName, queryUserRole, currentPageNo, pageSize);
req.setAttribute("userList",userList);
RoleServiceImpl roleService = new RoleServiceImpl();
List<Role> roleList = roleService.getRoleList();
req.setAttribute("roleList",roleList);
req.setAttribute("totalCount",totalCount);
req.setAttribute("currentPageNo",currentPageNo);
req.setAttribute("totalPageCount",totalPageCount);
req.setAttribute("queryUserName",queryUserName);
req.setAttribute("queryUserRole",queryUserRole);
//返回前端
try {
req.getRequestDispatcher("userlist.jsp").forward(req,resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
一切的增删改操作都需要处理事务:ACID!
增加用户时的Service:
try {
connection = BaseDao.getConnection();
connection.setAutoCommit(false);//开启JDBC事务。
addRows = userDao.add(connection,user);
connection.commit();
if (addRows>0){
flag = true;
System.out.println("add success!");
}else {
System.out.println("add failed!");
}
} catch (Exception e) {
e.printStackTrace();
try {
connection.rollback();
System.out.println("========rollback=========");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
//在service层进行connection连接的关闭.
BaseDao.closeResourse(connection,null,null);
}
SMBMS架构分析

以用户管理的增删改查为例,进入管理页面就要先查询所有的信息,这是最基础的,然后在查询出的信息上进行增删改操作。增删改查的操作无不与三层结构有关,即Servlet、Service、Dao层,这三层一层调用一层,虽然繁杂但也已经将各个功能分离的很清晰了。
如图,增删改三个操作对应了三个发起请求的方式直接发起请求user.do、异步请求ajax、标签链接herf。
之前已经实现了用户管理页面的查询,在这再实现一个用户的增加操作,熟悉一下与查不同的增,而删改与增差不多,就直接略过,进入下一阶段了。
5.增加用户实现
1.导入前端页面useradd.jsp,带着表单数据发起请求
ajax验证用户名
添加用户时,输入的用户名(userCode)不能与数据库中的重复,此处就可以使用ajax来发送请求实时验证输入的用户名是否存在于数据库中
useradd.js文件中实现
userCode.bind("blur",function(){
//ajax后台验证--userCode是否已存在
//user.do?method=ucexist&userCode=**
$.ajax({
type:"GET",//请求类型
url:path+"/jsp/user.do",//请求的url
data:{method:"ucexist",userCode:userCode.val()},//请求参数
dataType:"json",//ajax接口(请求url)返回的数据类型
success:function(data){//data:返回数据(json对象)
if(data.userCode == "exist"){//账号已存在,错误提示
validateTip(userCode.next(),{"color":"red"},imgNo+ " 该用户账号已存在",false);
}else{//账号可用,正确提示
validateTip(userCode.next(),{"color":"green"},imgYes+" 该账号可以使用",true);
}
},
error:function(data){//当访问时候,404,500 等非200的错误状态码
validateTip(userCode.next(),{"color":"red"},imgNo+" 您访问的页面不存在",false);
}
});
在UserServlet中添加userCodeExist方法,判断用户名是否存在(判断用户账号是否可用);//不要忘记在doGet中做好对应
//判断用户账号是否可用.
private void userCodeExist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断用户账号是否可用
String userCode = request.getParameter("userCode");
HashMap<String, String> resultMap = new HashMap<String, String>();
if (StringUtils.isNullOrEmpty(userCode)) {
//userCode == null || userCode.equals("")
resultMap.put("userCode", "exist");
} else {
UserService userService = new UserServiceImpl();
User user = userService.selectUserCodeExist(userCode);
if (user != null) {
resultMap.put("userCode", "exist");
} else {
resultMap.put("userCode", "notexist");
}
}
//把resultMap转为json字符串以json的形式输出
//配置上下文的输出类型
response.setContentType("application/json");
//从response对象中获取往外输出的writer对象
PrintWriter outPrintWriter = response.getWriter();
//把resultMap转为json字符串 输出
outPrintWriter.write(JSONArray.toJSONString(resultMap));
outPrintWriter.flush();//刷新
outPrintWriter.close();//关闭流
}
同时要在UserService中添加selectUserCodeExist方法调用UserDao中的getLoginUser2方法处理业务(UserDao中的getLoginUser2方法查询不到用户会返回空,即用户名未使用,使代码复用;不复用UserService中的login方法的原因是login方法中需要对密码进行判断,而查询用户是否存在用不到密码),对比数据库判断用户名是否被使用,与login方法类似,此处省略。
这个方法通过三层架构「 Servlet -> UserService -> UserDao -> 数据库 」的路线判断用户名是否被使用。
ajax请求用户列表
在useradd.jsp中,选择添加的用户的角色时需要获取角色列表,这个请求由ajax发起
useradd.js文件中实现
$.ajax({
type:"GET",//请求类型
url:path+"/jsp/user.do",//请求的url
data:{method:"getrolelist"},//请求参数
dataType:"json",//ajax接口(请求url)返回的数据类型
success:function(data){//data:返回数据(json对象)
if(data != null){
userRole.html("");
var options = "<option value=\"0\">请选择</option>";
for(var i = 0; i < data.length; i++){
//alert(data[i].id);
//alert(data[i].roleName);
options += "<option value=\""+data[i].id+"\">"+data[i].roleName+"</option>";
}
userRole.html(options);
}
},
error:function(data){//当访问时候,404,500 等非200的错误状态码
validateTip(userRole.next(),{"color":"red"},imgNo+" 获取用户角色列表error",false);
}
});
所以要在UserServlet中添加getRoleList方法,使前端能获取参数
//获取角色列表。
private void getRoleList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Role> roleList = null;
RoleService roleService = new RoleServiceImpl();
roleList = roleService.getRoleList();
//把roleList转换成json对象输出
response.setContentType("application/json");
PrintWriter outPrintWriter = response.getWriter();
outPrintWriter.write(JSONArray.toJSONString(roleList));
outPrintWriter.flush();
outPrintWriter.close();
}
这个方法通过「 Servlet -> RoleService -> RoleDao -> 数据库 」的路线获取角色列表的数据。这些过程其实都在一遍遍的走这条路线。
这样前端的页面表单就能完整的接收数据了。
2.userDao接口
返回受影响的行数,若大于0则表明有行受到影响,说明操作成功。
//增加用户信息
public int add(Connection connection, User user) throws Exception;
UserDao接口实现类UserDaoImpl
//增加用户信息
@Override
public int add(Connection connection, User user) throws Exception {
PreparedStatement pstm = null;
int addRows = 0;
if (connection!=null){
String sql = "insert into smbms_user (userCode,userName,userPassword," +
"userRole,gender,birthday,phone,address,creationDate,createdBy) " +
"values(?,?,?,?,?,?,?,?,?,?)";
Object[] params = {user.getUserCode(), user.getUserName(), user.getUserPassword(),
user.getUserRole(), user.getGender(), user.getBirthday(),
user.getPhone(), user.getAddress(), user.getCreationDate(), user.getCreatedBy()};
addRows = BaseDao.execute(connection,pstm,sql,params);
BaseDao.closeResourse(null,pstm,null);
}
return addRows;
}
3.UserService接口
在Service层中就使用UserDao中add的int返回值对数据操作是否成功进行判断了,所以此处直接返回boolean表明是否成功即可。
//增加用户信息。
public boolean add(User user);
UserService接口实现类UserServiceImpl
增删改与查询不同,涉及到了对数据库的数据修改,所以要使用事务保证数据一致性。
//增加用户信息。
@Override
public boolean add(User user) {
Connection connection = null;
int addRows = 0;
boolean flag = false;
try {
connection = BaseDao.getConnection();
connection.setAutoCommit(false);//开启JDBC事务。
addRows = userDao.add(connection,user);
connection.commit();
if (addRows>0){
flag = true;
System.out.println("add success!");
}else {
System.out.println("add failed!");
}
} catch (Exception e) {
e.printStackTrace();
try {
connection.rollback();
System.out.println("========rollback=========");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
//在service层进行connection连接的关闭.
BaseDao.closeResourse(connection,null,null);
}
return flag;
}
在业务层测试一下这个业务是否能执行,方便后面改其他错误。
4.UserServlet处理请求
UserServlet中接收到前端发来的method为add的请求,就调用add方法,并把req和resp参数传进去
//增加用户信息。
public void add (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userCode = req.getParameter("userCode");
String userName = req.getParameter("userName");
String userPassword = req.getParameter("userPassword");
String gender = req.getParameter("gender");
String birthday = req.getParameter("birthday");
String phone = req.getParameter("phone");
String address = req.getParameter("address");
String userRole = req.getParameter("userRole");
User user = new User();
user.setUserCode(userCode);
user.setUserName(userName);
user.setUserPassword(userPassword);
user.setAddress(address);
try {
user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse(birthday));//把字符串变成日期。
} catch (ParseException e) {
e.printStackTrace();
}
user.setGender(Integer.valueOf(gender));
user.setPhone(phone);
user.setUserRole(Integer.valueOf(userRole));
user.setCreationDate(new Date());
user.setCreatedBy(((User) req.getSession().getAttribute(Constants.USER_SESSION)).getId());
UserService userService = new UserServiceImpl();
if (userService.add(user)) {
resp.sendRedirect(req.getContextPath() + "/jsp/user.do?method=query");
} else {
req.getRequestDispatcher("useradd.jsp").forward(req, resp);
}
}
经过几条路线,添加用户的操作总算是完成了,比查询麻烦了不少,主要是有花里胡哨的操作。
参考链接: smbms(超市管理系统)源码 + 分析_有你的晚风的博客
后续加入供应商管理和订单管理部分
IDEA新建项目或模块中没有Java EE 和Web Application
解决办法:来到idea项目界面,直接Ctrl + Alt + Shift + / 调出如下界面

点击Registry,找到 javaee.legacy.project.wizard ,选中

接着回到创建项目的界面,就会发现有Java EE组件出现
文件传输原理(上传和下载)
在We应用中,文件上传和下载功能是非常有用的功能
1.搭建环境
先建一个Empty Project
然后添加Module(Java EE -->Web Application)

对于文件上传,浏览器在上船过程中是将文件以流的形式提交到服务器端的。
一般选择采用apache的开源工具common-fileupload这个文件上传组件
common-fileupload是依赖于common-io这个包的,所以还需要下载这个包
2.导入必要的包
https://mvnrepository.com/artifact/commons-io/commons-io/2.6
https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload/1.4

文件上传原理

ping域名获取域名对应的公网ip
【文件上传的注意事项】
- 为保证服务器安全,上传文件应该放在外界无法直接访问的怒路下,比如放在WEB-INF目录下
- 为防止文件覆盖的现象发生,要为伤处啊我呢间产生一个唯一的文件名
- 要限制上传文件的最大值
- 可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法
为避免同名文件,可以使用uuid、时间戳、md5、位运算算法
【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,在使用ServletFileUpload对象解析请求时需要用到DiskFileItemFactory对象。所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
表单如果包含一个文件上传输入项的话,这个表单的enctype属性就必须设置为multipart/form-data
浏览器表单的类型如果为multipart/form-data,在服务器端想获取数据就要通过流。
扩展内容
//网络传输中的东西都需要序列化,POJO实体类,如果想要在多个电脑上运行,传输====>需要把对象都序列化
//JNI = Java Native Interface
//implements java.io.Serializable:标记接口,JVM--->Java栈 + 本地方法栈native---->C++
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%--通过表单上传文件
get:上传文件大小有限制
post:上传文件大小没有限制
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
上传用户:<input type="text" name="username"><br>
<input type="file" name="file1"><br>
<input type="file" name="file2">
<p><input type="submit"> | <input type="reset"></p>
</form>
</body>
</html>
info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>info</title>
</head>
<body>
${msg}
</body>
</html>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>FileServlet</servlet-name>
<servlet-class>com.lin.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileServlet</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
</web-app>
FileServlet.java
package com.lin.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
public class FileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断上传的文件是普通表单还是带文件的表单
if(!ServletFileUpload.isMultipartContent(request)){
return;//终止方法运行,说明这是一个普通的表单,直接返回
}
//创建上传文件的保存路径,建议在WEB-INF目录下,安全,用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("WEB-INF/upload");
File uploadFile = new File(uploadPath);
if(!uploadFile.exists()){
uploadFile.mkdir();//创建目录
}
//缓存,临时文件
//临时路径,假如文件超过了预期的大小,就把他放到一个临时文件中,过几天自动删除,或者提醒用户转存为永久
String tempPath = this.getServletContext().getRealPath("WEB-INF/temp");
File tempFile = new File(tempPath);
if(!tempFile.exists()){
tempFile.mkdir();//创建临时目录
}
//处理上传的文件,一般需要通过流兰获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
//但是我们一般都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件
try {
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
DiskFileItemFactory factory = getDiskFileItemFactory(tempFile);
//2.获取ServletFileUpload
ServletFileUpload upload = getServletFileUpload(factory);
//3.处理上传文件
//把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取
String msg = uploadParseRequest(upload, request, uploadPath);
// Servlet请求转发消息
System.out.println(msg);
if(msg.equals("文件上传成功!")) {
// Servlet请求转发消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("info.jsp").forward(request, response);
}else {
msg ="请上传文件";
request.setAttribute("msg",msg);
request.getRequestDispatcher("info.jsp").forward(request, response);
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
public static DiskFileItemFactory getDiskFileItemFactory(File tempFile) {
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中;
factory.setSizeThreshold(1024*1024);//缓冲区大小为1M
factory.setRepository(tempFile);//临时目录的保存目录,需要一个File
return factory;
}
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
ServletFileUpload upload = new ServletFileUpload(factory);
// 监听文件上传进度
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesReadL:已经读取到的文件大小
//pContentLength:文件大小
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("总大小:"+pContentLength+"已上传"+pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
upload.setFileSizeMax(1024*1024*10);
//设置总共能够上传文件的大小
//1024 = 1kb*1024 = 1M * 10 = 10M
upload.setSizeMax(1024*1024*10);
return upload;
}
public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath)
throws FileUploadException, IOException {
String msg = "";
// 把前端请求解析,封装成FileItem对象
List<FileItem> fileItems = upload.parseRequest(request);
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()) {// 判断上传的文件是普通的表单还是带文件的表单
// getFieldName指的是前端表单控件的name;
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8"); // 处理乱码
System.out.println(name + ": " + value);
} else {// 判断它是上传的文件
// ============处理文件==============
// 拿到文件名
String uploadFileName = fileItem.getName();
System.out.println("上传的文件名: " + uploadFileName);
if (uploadFileName.trim().equals("") || uploadFileName == null) {
continue;
}
// 获得上传的文件名/images/girl/pojie.png
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
// 获得文件的后缀名
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
/*
* 如果文件后缀名fileExtName不是我们所需要的 就直按return.不处理,告诉用户文件类型不对。
*/
System.out.println("文件信息[文件名: " + fileName + " ---文件类型" + fileExtName + "]");
// 可以使用UUID(唯一识别的通用码),保证文件名唯一
// UUID.randomUUID(),随机生一个唯一识别的通用码;
String uuidPath = UUID.randomUUID().toString();
//网络传输中的东西都需要序列化,POJO实体类,如果想要在多个电脑上运行,传输====>需要把对象都序列化
//JNI = Java Native Interface
//implements java.io.Serializable:标记接口,JVM--->Java栈 + 本地方法栈native---->C++
// ================处理文件完毕==============
// ==============存放地址==============
// 存到哪? uploadPath
// 文件真实存在的路径realPath
String realPath = uploadPath + "/" + uuidPath;
// 给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if (!realPathFile.exists()) {
realPathFile.mkdir();
}
// ==============存放地址完毕==============
////=============文件传输=============
// 获得文件上传的流
InputStream inputStream = fileItem.getInputStream();
// 创建一个文件输出流
// realPath =真实的文件夹;
// 差了一个文件;加上输出文件的名字"/"+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);
System.out.println("path:"+realPath + "/" + fileName);
// 创建一个缓冲区
byte[] buffer = new byte[1024 * 1024];
// 判断是否读取完毕
int len = 0;
// 如果大于0说明还存在数据;
while ((len = inputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
// 关闭流
fos.close();
inputStream.close();
msg = "文件上传成功!";
fileItem.delete(); // 上传成功,清除临时文件
//=============文件传输完成=============
}
}
return msg;
}
}
掌握HashMap底层和多线程底层源码
邮件发送原理及实现
邮件发送原理

服务器结构简图
申请一个邮箱账号相当于在服务器FileSystem中申请一个地址
jar包支持
mail-1.5.0-b01.jar
activation-1.1.1.jar
JavaMail是实现邮件发送和接收功能的标准开发包,编写邮件是,无需考虑邮件的底层实现细节,只要调用JavaMail开发保证相应的API类就可以
简易文本邮件发送的实现

由上图我们可以确定几个必须步骤
1.创建session对象
2.创建Transport对象
3.使用邮箱的用户名和授权码连上邮件服务器
4.创建一个Message对象(需要传递session)
- message需要指明发件人、收件人以及文件内容
5.发送邮件
6.关闭连接
package com.lin;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
//发送一封简单的邮件
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop=new Properties();
prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器
prop.setProperty("mail.transport.protocol","smtp");///邮件发送协议
prop.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf=new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需的环境信息的session对象
Session session=Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("1289274798@qq.com","tixdmfjdpfvpbabi");
}
});
//开启session的debug模式,这样可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码脸上邮件服务器
ts.connect("smtp.qq.com","1289274798@qq.com","tixdmfjdpfvpbabi");
//4.创建邮件:写邮件
//注意需要传递session
MimeMessage message=new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress("1289274798@qq.com"));
//指明邮件的收件人
message.setRecipient(Message.RecipientType.TO,new InternetAddress("linlixue901@163.com"));
//邮件标题
message.setSubject("学习java");
//邮件的文本内容
message.setContent("<h1 style='color:red'>今天学习邮件发送实现</h1>","text/html;charset=UTF-8");
//5.发送邮件
ts.sendMessage(message, message.getAllRecipients());
//6.关闭连接
ts.close();
}
}
MIME:多用途互联网邮件扩展类型,是一个互联网标准,扩展了电子邮件标准,使其能够支持:非ASCII字符文本;非文本格式附件(二进制、声音、图像等),由多部分(multiple parts)组成的消息体;包含非ASCII字符的头信息(Header information)
复杂文件内容的发送
先认识两个类一个名词:
MIME(多用途互联网邮件扩展类型)
MimeBodyPart类
javax.mail.internet.MimeBodyPart类 表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。
MimeMultipart类
javax.mail.internet.MimeMultipart是抽象类 Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。

包含图片的发送
package com.lin;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;
//发送一封简单的邮件
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop=new Properties();
prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器
prop.setProperty("mail.transport.protocol","smtp");///邮件发送协议
prop.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf=new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需的环境信息的session对象
Session session=Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("1289274798@qq.com","tixdmfjdpfvpbabi");
}
});
//开启session的debug模式,这样可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码脸上邮件服务器
ts.connect("smtp.qq.com","1289274798@qq.com","tixdmfjdpfvpbabi");
//4.创建邮件:写邮件
//注意需要传递session
MimeMessage message=new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress("1289274798@qq.com"));
//指明邮件的收件人
message.setRecipient(Message.RecipientType.TO,new InternetAddress("linlixue901@163.com"));
//邮件标题
message.setSubject("学习java");
//邮件的文本内容
//=================================准备图片数据=======================================
MimeBodyPart image=new MimeBodyPart();
//图片需要经过数据处理
DataHandler dh=new DataHandler(new FileDataSource("D:\\Javaworkspace\\javaweb-function\\mail-java\\src\\images\\图片5.jpg"));
//在MimeBodyPart中放入这个处理的图片数据
image.setDataHandler(dh);
//给这个图片设置一个ID,在后面可以使用
image.setContentID("bz.jpg");
//准备正文的数据
MimeBodyPart text=new MimeBodyPart();
text.setContent("这是一封邮件的正文<img src='cid:bz.jpg'>附带图片","text/html;charset=UTF-8");
//描述数据关系
MimeMultipart mm=new MimeMultipart();
mm.addBodyPart(text);
mm.addBodyPart(image);
mm.setSubType("related");
//设置到消息中,保存修改
message.setContent(mm);//把最后编辑好的邮件放到消息当中
message.saveChanges();//保存修改
//5.发送邮件
ts.sendMessage(message, message.getAllRecipients());
//6.关闭连接
ts.close();
}
}
包含附件的发送
package com.lin;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
//发送一封简单的邮件
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop=new Properties();
prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器
prop.setProperty("mail.transport.protocol","smtp");///邮件发送协议
prop.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf=new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需的环境信息的session对象
Session session=Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("1289274798@qq.com","tixdmfjdpfvpbabi");
}
});
//开启session的debug模式,这样可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码脸上邮件服务器
ts.connect("smtp.qq.com","1289274798@qq.com","tixdmfjdpfvpbabi");
//4.连接上之后我们需要发送邮件
MimeMessage mimeMessage = imageMail(session);
//5.发送邮件
ts.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
//6.关闭连接
ts.close();
}
public static MimeMessage imageMail(Session session) throws MessagingException {
//消息的固定格式
MimeMessage mimeMessage=new MimeMessage(session);
//指明邮件的发件人
mimeMessage.setFrom(new InternetAddress("1289274798@qq.com"));
//指明邮件的收件人
mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress("linlixue901@163.com"));
//邮件标题
mimeMessage.setSubject("学习java发送带附件邮件");
/*
编写邮件内容
1.图片
2.附件
3.文本
*/
//图片
MimeBodyPart body1 = new MimeBodyPart();
DataHandler dh=new DataHandler(new FileDataSource("D:\\Javaworkspace\\javaweb-function\\mail-java\\src\\images\\图片5.jpg"));
body1.setDataHandler(dh);
//给这个图片设置一个ID,在后面可以使用
body1.setContentID("dsn.jpg");
//文本
MimeBodyPart body2 = new MimeBodyPart();
body2.setContent("我不是广告邮件<img src='cid:dsn.jpg'>附带图片和附件","text/html;charset=UTF-8");
//附件
MimeBodyPart body3 = new MimeBodyPart();
body3.setDataHandler(new DataHandler(new FileDataSource("D:\\Javaworkspace\\javaweb-function\\mail-java\\src\\resoueces\\1.txt")));
body3.setFileName("");//附件设置名字
MimeBodyPart body4 = new MimeBodyPart();
body4.setDataHandler(new DataHandler(new FileDataSource("D:\\Javaworkspace\\javaweb-function\\mail-java\\src\\resoueces\\log.properties")));
body4.setFileName("log4j.properties");//附件设置名字
//拼装邮件正文内容
MimeMultipart multipart1 = new MimeMultipart();
multipart1.addBodyPart(body1);
multipart1.addBodyPart(body2);
multipart1.setSubType("related");//1.文本和图片内嵌成功!
//new MimeBodyPart().setContent(multipart1);将拼接好的正文内容设置为主体
MimeBodyPart contentText = new MimeBodyPart();
contentText.setContent(multipart1);
//拼接附件
MimeMultipart allFile = new MimeMultipart();
allFile.addBodyPart(body3);//附件
allFile.addBodyPart(body4);//附件
allFile.addBodyPart(contentText);//正文
allFile.setSubType("mixed");//正文和附件都存在邮件中,所有类型设置为mixed;
//放到Messae消息中
mimeMessage.setContent(allFile);
mimeMessage.saveChanges();//保存修改
return mimeMessage;
}
}
Javaweb发送邮件
很多网站用户注册成功之后会收到来自注册网站的邮件。
下面实现用户注册成功之后,将用户的注册信息以Eamil的形式发动到用户的注册邮箱中。
导入lombok依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
@Data注解可以自动生成get/set方法
@AllArgsConstructor注解可以生成所有参数的构造函数
@NoArgsConstructor注解可以生成无参构造
引入外部jar包时web程序报500或者404错误, 需要调整ProjectStructure下的Artifacts目录结构

首先右击lib目录add as library,手动点击加号加入Library Files

index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="text" name="pwd"><br/>
邮箱:<input type="text" name="email"><br/>
<input type="submit" value="注册">
</form>
</body>
</html>
实体类User.java
package com.lin.pojo;
import java.io.Serializable;
public class User implements Serializable {
private String username;
private String password;
private String email;
public User() {
}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
RegisterServlet.java
package com.lin.servlet;
import com.lin.pojo.User;
import com.lin.util.Sendmail;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try{
//接收用户请求,封装成对象
String username=req.getParameter("username");
String password=req.getParameter("pwd");
String email=req.getParameter("email");
User user = new User(username, password, email);
//写完工具类后,用户注册成功之后,给用户发送一封邮件
//我们使用线程来专门发送邮件,防止出现耗时,和网站注册人数过多的情况;
Sendmail send=new Sendmail(user);
//启动线程,线程启动之后就会执行run方法来发送邮件
send.start();
//注册用户
req.setAttribute("message", "注册成功,我们已经发了一封带了注册信息的电子邮件,请查收!如网络不稳定,可能过会儿才能收到!!");
req.getRequestDispatcher("info.jsp").forward(req, resp);
} catch (Exception e) {
e.printStackTrace();
req.setAttribute("message", "注册失败!!");
req.getRequestDispatcher("info.jsp").forward(req, resp);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.lin.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/RegisterServlet.do</url-pattern>
</servlet-mapping>
</web-app>
邮件发送类Sendmail.java
package com.lin.util;
import com.lin.pojo.User;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
//网站3秒原则:用户体验
//多线程实现用户体验 异步处理
public class Sendmail extends Thread{
//用于给用户发送邮件的邮箱
private String from = "1289274798@qq.com";
//邮箱的用户名
private String username = "1289274798@qq.com";
//邮箱的密码(授权码)
private String password = "tixdmfjdpfvpbabi";
//发送邮件的服务器地址
private String host = "smtp.qq.com";
private User user;
public Sendmail(User user){
this.user = user;
}
//重写run方法的实现,在run方法中发送邮件给指定的用户
@Override
public void run() {
try{
Properties prop = new Properties();
prop.setProperty("mail.host", host);
prop.setProperty("mail.transport.protocol", "smtp");
prop.setProperty("mail.smtp.auth", "true");
// 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
//1、创建定义整个应用程序所需的环境信息的 Session 对象
Session session = Session.getDefaultInstance(prop, new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("1289274798@qq.com", "tixdmfjdpfvpbabi");
}
});
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
//2、通过session得到transport对象
Transport ts = session.getTransport();
//3、使用邮箱的用户名和授权码连上邮件服务器
ts.connect(host, username, password);
//4、创建邮件
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from)); //发件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail())); //收件人
message.setSubject("用户注册邮件"); //邮件的标题
String info = "恭喜您注册成功,您的用户名:" + user.getUsername() + ",您的密码:" + user.getPassword() + ",请妥善保管,如有问题请联系网站客服!!";
message.setContent(info, "text/html;charset=UTF-8");
message.saveChanges();
//发送邮件
ts.sendMessage(message, message.getAllRecipients());
ts.close();
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这里引入多线程的目的是为了提高用户的体验,防止因发送文件时间过长,导致前端响应过久,因此这里采用异步响应。
浙公网安备 33010602011771号