MVC 分层思想(解耦:高内聚低耦合)

controller层
	- 接收请求
	  (调用service层,返回结果)
	- 响应结果
service层
	- 业务逻辑判断
mapper
	- 接口类
	  mapper.xml	mybatis与数据库的相关操作
entity(po、model)
	- JavaBean实体
util
	- 工具类(通用的方法/)
test
	- 测试类/方法

优点:

  1. 耦合性低
    视图层和业务层分离,允许更改视图层代码而不用重新编译模型和控制器代码。

  2. 重用性高
    多个视图能共享一个模型,如电脑和手机访问同一服务。

  3. 开发效率提高,人员职责明确
    Java 开发人员专注于业务逻辑,界面开发人员专注于表现形式。

  4. 耦合度低,可维护性高
    每层代码修改可达到无损替换效果。

  5. 有利软件工程化管理
    每层职责明确,便于工具化管理。

分层思想案例

页面完成登录功能,登录成功跳转到项目首页,登录失败提示信息。

用户登录思路分析

用户登录
	1.数据库创建对应的用户表	tb user
	2.前台页面
		登录页面	 login.jsp
			用户登录	JS校验
			1.给登录按钮绑定点击事件
            2.获取用户姓名和密码的值
            3.判断姓名是否为空
            	如果姓名为空,提示用户(span标签赋值),并且return
			4.判断密码是否为空
            	如果密码为空,提示用户(span标签赋值),并且return
			5.如果都不为空,则手动提交表单

		首页		  index.jsp

	3.后代实现
		登录功能
			思路:
				1.接收客户端的请求(接收参数:姓名、密码)
				2.参数的非空判断
					如果参数为空
						通过消息模型对象返回结果(设置状态、设置提示信息、回显数据)
						将消息模型对象设置到request作用域中
						请求转发跳转到登录页面
						return
				3.通过用户姓名查询用户对象
				4.判断用户对象是否为空
					如果为空
						通过消息模型对象返回结果(设置状态、设置提示信息、回显数据)
						将消息模型对象设置到request作用域中
						请求转发跳转到登录页面
						return
				5.将数据库中查询到的用户密码与前台传递的密码作比较
					如果不相等
						通过消息模型对象返回结果(设置状态、设置提示信息、回显数据)
						将消息模型对象设置到request作用域中
						请求转发跳转到登录页面
					如果相等,表示登录成功
						将用户信息设置到session作用域中
						重定向跳转到首页
			controller层(接收请求、响应结果)
				1.接收客户端的请求(接收参数:姓名、密码)
				2.调用service层的方法,返回消息模型对象
				3.判断消息模型的状态码
					如果状态码是失败
						将消息模型对象设置到request作用域中,请求转发跳转到1ogin.jsp
					如果状态码是成功
						将消息模型中的用户信息设置到session作用域中,重定向跳转到index.jsp
			service层(业务逻辑判断)
				1.参数的非空判断
					如果参数为空
						将状态码、提示信息、回显数据设置到消息模型对象中,返回消息模型对象
				2.调用dao层的查询方法,通过用户名查询用户对象
				3.判断用户对象是否为空
					如果为空,将状态码、提示信息、回显数据设置到消息模型对象中,返回消息型对象
				4.判断数据库中查询到的用户密码与前台传递过来的密码作比较
					如果不相等,将状态码、提示信息、回显数据设置到消息模型对象中,返回消息模型对象
				5.登陆成功,将成功状态、提示信息、用户对象设置消息模型对象,并return
			mapper层(Dao层)
				定义对应的接口

项目实现

【1】创建数据库表

CREATE TABLE t_user (
  uid INT PRIMARY KEY,
  uname VARCHAR(255),
  pwd VARCHAR(255)
);

【2】创建项目,并导入 jar 包

项目结构:

LoginProject
├── src
│   ├── com.rito.controller
│   │   └── LoginServlet
│   ├── com.rito.dao
│   │   ├── UserDao
│   │   └── impl
│   │       └── UserDaoImpl
│   ├── com.rito.pojo
│   │   └── User
│   ├── com.rito.service
│   │   ├── UserService
│   │   └── impl
│   │       └── UserServiceImpl
│   └── Main
├── web
│   ├── WEB-INF
│   │   ├── lib
│   │   │   ├── jsp-api.jar
│   │   │   ├── jstl.jar
│   │   │   ├── mysql-connector-java-8.0.11.jar
│   │   │   ├── servlet-api.jar
│   │   │   └── standard.jar
│   │   └── web.xml
│   ├── index.jsp
│   └── login.jsp
└── LoginProject.iml

【3】登录页面实现:login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
    <div style="text-align:center">
        <form action="loginServlet" method="post">
        	用户名: <input type="text" name="uname"><br>
        	密码: <input type="password" name="pwd"><br>
            <input type="submit" value="登录"><br>
            ${failmsg}
    	</form>
    </div>
</body>
</html>

【4】实体类 User.java

package com.msb.pojo;

public class User {
    private int uid;
    private String uname;
    private String pwd;

    // 构造方法、getter/setter、toString 略
}
/**消息模型对象(数据响应)
src
│   ├── com.rito.pojo
│   │   └── vo
│   │       └── MessageModel
│   │   └── User
 *	状态码
 * 		1=成功,0=失败
 *	提示信息
 *		字符串
 *	回显数据
 * 		objecty象
 */
public class MessageModel {
	private Integer code=1;//状态码(1=成功,0=失败)
	private String msg ="成功!";// 提示信息
	private Object object;// 回显对象 (基本数据类型、字符串类型、List、Map等)
    // 构造方法、getter/setter、toString 略
}

【5】Servlet 实现:LoginServlet.java

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uname = req.getParameter("uname");
        String pwd = req.getParameter("pwd");
        UserDao userDao = new UserDao();
        User user = userDao.selectOne(uname, pwd);
        if (user != null) {
            resp.sendRedirect(req.getContextPath() + "/index.jsp");
        } else {
            req.setAttribute("failmsg", "登录失败");
            req.getRequestDispatcher("/login.jsp").forward(req, resp);
        }
    }
}

【6】DAO 层:

UserDao.java
public class UserDao {
    User selectOneUser(String uname, String pwd);
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
    public User selectOneUser(String uname, String pwd) {
        // JDBC 查询数据库,返回 User 对象
    }
}

【7】Service 层

UserService.java
public interface UserService {
    User selectUserInfo(String uname, String pwd);
}
UserServiceImpl.java
public class UserServiceImpl implements UserService {
    UserDao userDao = new UserDaoImpl();
    public User selectUserInfo(String uname, String pwd) {
        return userDao.selectOneUser(uname, pwd);
    }
}

过滤器(Filter)

基本概念

  • 定义Filter 是 Servlet 规范中的一种组件,用于在 Servlet 之外对 RequestResponse 进行修改
  • 作用时机
    • 对用户请求进行预处理
    • 对服务器响应进行后处理
  • 过滤器链:多个 Filter 组合形成链式处理
    • 请求时:按配置顺序执行
    • 响应时:按相反顺序执行

生命周期方法

public interface Filter {
    void init(FilterConfig filterConfig);    // 初始化
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain); // 过滤处理
    void destroy();                         // 销毁
}

步骤

  1. 编写 Java 类实现 Filter 接口,并实现其doFilter()方法
  2. 通过编写@WebFilter("/*")注解设置它所能拦截的资源

核心机制

  • FilterChain.doFilter(request,response):决定是否放行请求到下一个过滤器或目标资源
  • 调用 doFilter():请求到达目标资源
  • 不调用 doFilter():拦截请求,资源无法被访问
  1. @WebFilter("/s01”)配置拦截的资源路径
  2. doFilter()方法中需要设置放行,否则请求无法到达资源( filterchain,dofilter(request,response)😉
  3. 如果是过滤器链,则先配置的先执行(首字母在前的先执行);响应时顺序反过来

过滤器应用场景

  • 身份认证和授权检查
  • 日志记录和审计
  • 数据压缩和加密
  • 字符编码转换
  • 输入验证和XSS防护

过滤器实例

请求乱码处理

@WebFilter("/*")//拦截所有
public class AEncodingFilter implements Filter {
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {
        //基于HTTP
        HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        
        // 处理POST请求乱码
        request.setCharacterEncoding("UTF-8");
        
        // 处理GET请求(Tomcat8以下版本)
        String method = request.getMethod();
        if ("GET".equalsIgnoreCase(method)) {
            String serverInfo = request.getServletContext().getServerInfo();
            //得到具体的版本号
            String versionStr = serverInfo.substring(serverInfo.indexOf("/")+1, serverInfo.indexOf("."));
            //判断服务器版本是否小于8
            if (Integer.parseInt(versionStr) < 8) {
                //得到自定义类 (MyWapper继承了HttpservletRequestWapper对象,而HttpservletRequestWapper对象实现了HttpservletRequest接口,所以Mywapper的本质也是request对象)
                HttpServletRequest myRequest = new MyMapper(request);
                chain.doFilter(myRequest, response);
                return;
            }
        }
        chain.doFilter(request, response);
    }
    
    public void init(FilterConfig fConfig) throws ServletException {}
    
    /**
	* 定义内部类,继承HttpServletRequestWrapper包
	装类对象,重写getParameter()方法
	*/
	class MyWapper extends HttpServletRequestWrapper {
		// 定义成员变量,提升构造器 中的request对象的范围
		private HttpServletRequest request;
		public MyWapper(HttpServletRequest request) {
			super(request);
			this.request = request;
		}
		/**
		* 重写getParameter()方法
		*/
		@Override
		public String getParameter(String name) {
			String value = request.getParameter(name);
            if (value != null && !"".equals(value.trim())) {
				try {
					// 将默认ISO-8859-1编码的字符转换成UTF-8
					value = new
					String(value.getBytes("ISO-8859-1"),"UTF-8");
				} catch(UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			return value;
		}
	}
}

用户非法访问拦截

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* 非法访问拦截(当用户未登录时,拦截请求到登录页面)
* 	拦截的资源:
* 		拦截所有资源 /*
* 	需要被放行的资源:不需要登录即可访问的资源
* 		1、放行指定页面,不需要登录可以访问的页面(例如:登录页面、注册页面等)
* 		2、放行静态资源(例如:css、js、image等资源)
*		3、放行指定操作,不需要登录即可执行的操作(例如:登录操作、注册操作等)
* 		4、登录状态放行 (如果存在指定sessuin对象,则为登录状态) 
*
*		其他请求需要被拦截跳转到登录界面
*/		
@WebFilter("/*")
public class AuthFilter implements Filter {

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {
        // 基于HTTP
        HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;

        // 得到请求的路径
        String path = request.getRequestURI(); // 站点名/资源路径

        // 1、放行指定页面,不需要登录可以访问的页面(例如:登录页面、注册页面等)
        if (path.contains("/login.jsp") || path.contains("/register.jsp")) {
            chain.doFilter(request, response);
            return;
        }

        // 2、放行静态资源(例如:css、js、image等资源)
        if (path.contains("/js")) {
            chain.doFilter(request, response);
            return;
        }

        // 3、放行指定操作,不需要登录即可执行的操作(例如:登录操作、注册操作等)
        if (path.contains("/loginServlet")) {
            chain.doFilter(request, response);
            return;
        }

        // 4、登录状态放行 (如果存在指定sessuin对象,则为登录状态)
        // 得到session域对象
        String uname = (String) request.getSession().getAttribute("user");

        // 如果session域对象不为空,则为登录状态,放行资源
        if (uname != null && !"".equals(uname.trim())) {
            chain.doFilter(request, response);
            return;
        }

        // 若以上条件均不满足,拦截跳转到登录页面
        response.sendRedirect("login.jsp");
        return;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化方法,可以留空
    }

    @Override
    public void destroy() {
        // 销毁方法,可以留空
    }
}

监听器(Listener)

分类(三类8种)

监听生命周期
  • ServletRequestListener:监听Request对象的创建和销毁
  • HttpSessionListener:监听Session对象的创建和销毁
  • ServletContextListener:监听Application对象的创建和销毁
监听值的变化
  • ServletRequestAttributeListener:监听Request域中属性的变化
  • HttpSessionAttributeListener:监听Session域中属性的变化
  • ServletContextAttributeListener:监听Application域中属性的变化
针对Session中的对象
  • 监听Session中的JavaBean对象

监听器应用场景

  • 统计在线用户数
  • 系统初始化工作
  • 统计网站访问量
  • 监听用户行为
  • 资源清理工作

在线人数统计实例

@WebListener
public class OnlineListener implements HttpSessionListener {
    private Integer onlinenumber = 0;
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        onlinenumber++;
        // 存储到application作用域
        se.getSession().getServletContext().setAttribute("onlinenumber", onlinenumber);
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        onlinenumber--;
        se.getSession().getServletContext().setAttribute("onlinenumber", onlinenumber);
    }
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐