跳转路径问题,Filter,Listener,
一. 跳转路径问题
当我们在servlet中,跳转到其他页面,会使用重定向,或请求转发,这又分为绝对路径(前面带/
),相对路径
(1)绝对路径(前面带/
):
- 请求转发:
request.getRequestDispatcher("/login.jsp").forward(request, response);
则跳转后的路径是:工程目录/longin.jsp
:
例如: http://127.0.0.1:8080/项目名/login.jsp
- 重定向:
response.sendRedirect("/login.jsp");
则跳转后的路径是:IP服务器目录/longin.jsp
:
例如: http://127.0.0.1:8080/login.jsp
- 对于项目名,我们可以通过
String context = request.getContextPath();
来获取:
String context = request.getContextPath();
response.sendRedirect(context+"/login.jsp");
(2) 相对路径:不是/
开头,相对当前访问的目录
对于重定向与请求转发:效果是一样的
response.sendRedirect("login.jsp");
request.getRequestDispatcher("login.jsp").forward(request, response);
当前目录指的是现在访问的路径从后往前的第一个/
:
例如:http://127.0.0.1:8080/项目名/admin/index.jsp
里进行跳转,
跳转后的路径:http://127.0.0.1:8080/项目名/admin/login.jsp
(3)其他,过滤器的过程
- 在一个过滤器里(
@WebFilter("/admin/*")
),我们对这个路径进行过滤, - 没有登录不可以进入这个目录下,并跳转到项目下的login.jsp登录页面:重定向实现
// 对于未登录,进行下面的跳转会造成死循环
resp.sendReidrect("login.jsp")
- 为何会造成死循环,不符合:重定向(
http://127.0.0.1:8080/项目名/admin/login.jsp
),再次被过滤(再次不符合),又一次重定向…
- 假设用请求转发实现,则如果admin/下有login.jsp页面则会跳转到那个页面,如果没有则会报404错误:
- 因为请求转发是服务器内部跳转,而过滤器是过滤客户端访问的web资源,所以通过。
- 跳转到:
http://127.0.0.1:8080/项目名/admin/login.jsp
二. 过滤器Filter
(1)基本配置实现(xml)
- 类实现Filter 接口
public class Demo implements Filter {}
- xml配置
<filter>
<filter-name>filter</filter-name>
<filter-class>fil.Demo</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern> /* </url-pattern>
</filter-mapping>
- 加载时间问题
加载时间:服务器启动时加载 实例化 初始化
如果Servlet和Filter 谁先启动: 先Filter再Servlet - chain.doFilter(request, response);作用是什么?
chain可以被称为放行,它的作用是让其进入下一个过滤器或servlet(调用后续的过滤器或资源)
(2)生命周期
加载和实例化 | 构造方法 | 1次 |
---|---|---|
初始化 | init() | 1次 |
过滤 | doFilter() | 多次 |
销毁 | destroy() | 1次 |
(3)责任链的先后顺序
什么是过滤器链?
很多Filter都是拦截所有的请求,即很多Filter的命中规则都是一样的,那么怎么办?
过滤器链中的过滤器,进入的顺序和配置映射的先后顺序有关。(<filter-mapping>
)
(4)设置编码实例
- 做一个设置编码的过滤器
- xml配置
<filter>
<filter-name>filter</filter-name>
<filter-class>fil.Demo</filter-class>
<init-param>
<param-name>requestCharset</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>responseCharset</param-name>
<param-value>text/html;charset=utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 类
package fil;
import javax.servlet.*;
import java.io.IOException;
public class Demo implements Filter {
private String requestCharset;
private String responseCharset;
@Override
public void init(FilterConfig conf){
requestCharset = conf.getInitParameter("requestCharset");
responseCharset = conf.getInitParameter("responseCharset");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
/*过滤方法 主要是对request和response进行一些处理,
然后交给下一个过滤器或Servlet处理*/
req.setCharacterEncoding(requestCharset);
resp.setContentType(responseCharset);
chain.doFilter(req,resp);
}
}
- 设置一个可以过滤未登录的过滤器
package fil;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(filterName = "Demo2" ,urlPatterns = "/admin/*" )
// 在项目下的admin/*目录是只有登录后才可以访问
public class Demo2 implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
// 获取session参数里的true:没有session会产生一个新的session
// false: 没有session会返回null
HttpSession session = request.getSession(false);
// 登录过的session里面会存储loginuser属性
if (session == null || session.getAttribute("loginuser") == null){
// 如果没有让其跳转到登录页面
String context = request.getContextPath(); // 获取项目名
System.out.println(context);
//response绝对路径 /表示ip地址,从ip开始找路径资源
response.sendRedirect(context+"/login.jsp");
}else {
// 有则放行
chain.doFilter(req,resp);
}
}
}
- 登录控制器
/**
* 控制器 : 登录请求
*/
@WebServlet(value = "/LoginServlet")
public class LoginServlet extends HttpServlet {
private UserService service = new UserService();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接收请求
String uname = request.getParameter("username");
String upass = request.getParameter("password");
//封装实体类
User user = new User();
user.setPassword(upass);
user.setUsername(uname);
//调用业务
User u = service.login(user);
//根据结果 判断 跳转
if(u!=null) {
//登录成功
HttpSession session = request.getSession(true);
//session必须得有session? false
session.setAttribute("loginuser", u);
response.sendRedirect("admin/");
}else {
//登录失败
request.setAttribute("msg", "用户名或密码有误!");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
(5)作用
- 登录权限验证
- 资源访问权限控制
- 敏感词汇过滤
- 字符编码转换等等操作
// 乱码问题:区分请求乱码和响应乱码
request.setCharacterEncoding(charset);
response.setContentType("text/html;charset=utf-8");
用户授权的Filter: Filter负责检查用户请求,根据请求过滤用户非法请求。
日志Filter: 详细记录某些特殊的用户请求。
负责解码的Filter: 包括对非标准编码的请求解码。
Filter可拦截多个请求或响应;一个请求或响应也可被多个请求拦截。
三. 监听器Listener
(1)获取在线人数
- xml配置:
<listener>
<listener-class>listener.LisDemo1</listener-class>
</listener>
- 类,监听session
HttpSessionListener
package listener;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* session生命周期监听器
* 在线统计人数
* 把人数放入application上下文
*
* 当第一次访问jsp页面时候执行这里
*/
@WebListener
// @WebListener 注册监视器,告知容器,观察者模式
public class LisDemo1 implements HttpSessionListener {
/**
* session被创建时候执行这里
*/
@Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println("session创建了---"+session.getId());
// 获取上下文对象
ServletContext application = session.getServletContext();
Integer count = (Integer) application.getAttribute("onlineCount");
if (count == null){
count = 0;
}
count++;
application.setAttribute("onlineCount",count);
}
/**
* session被销毁时候,超时执行这里
*/
@Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println("session销毁了======="+session.getId());
ServletContext application = session.getServletContext();
Integer count = (Integer)application.getAttribute("onlineCount");
if(count != null) {
count--;
}
application.setAttribute("onlineCount", count);
}
}
- 在xml设置全局session超时(失效)时间
<!--设置session最大超时时间,单位为分钟-->
<session-config>
<session-timeout>1</session-timeout>
</session-config>
(2)获取登录人数
实体类实现接口HttpSessionBindingListener
User implements HttpSessionBindingListener
package cn.mvc.entity;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
/**
* valueUnbound的触发条件是以下三种情况:
* 1. 执行session.invalidate()时。
* 2. session超时,自动销毁时。
* 3. 执行session.setAttribute("loginuser", "其他对象");
* 或session.removeAttribute("loginuser");将listener从session中删除时。
*/
public class User implements HttpSessionBindingListener {
private String username;
private String password;
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;
}
// 绑定值时候执行,即登录成功(会员人数)
// session.setAttribute("loginuser", user);
@Override
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
System.out.println("session---放入了值user"+session.getId()+"----"+session.getAttribute("loginuser"));
// 获取上下文对象
ServletContext application = session.getServletContext();
Integer count = (Integer) application.getAttribute("loginuser");
if (count == null){
count = 0;
}
count++;
application.setAttribute("loginuser",count);
}
// 解除值时候执行
// 在执行销毁session时候,就会执行remove,相当于对象不在了,对象的属性也不存在
// session.removeAttribute("loginuser);
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
System.out.println("session销毁了=======放入了值user"+session.getId()+"----"+session.getAttribute("loginuser"));
ServletContext application = session.getServletContext();
Integer count = (Integer)application.getAttribute("loginuser");
if(count != null) {
count--;
}
application.setAttribute("loginuser", count);
}
}
(3) 监听服务器启动和停止
ServletContextListener
/**
* 监听服务器启动和停止
*
*/
@WebListener
public class GlobalListener implements ServletContextListener {
/**
* 服务器停止时 执行这里
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("=======%%%%% 服务器停止 %%%%=========");
}
/**
* 服务器启动时执行这里
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("$$$$$$$$$$$$$ 服务器启动了 $$$$$$$$$$");
//获取上下文
ServletContext application = sce.getServletContext();
//获取上下路径
String contextPath = application.getContextPath();
System.out.println(">>>>==="+contextPath);
application.setAttribute("contextPath", contextPath);
//加载
loadConfig();
}
/**
* 加载配置文件
*/
private void loadConfig() {
InputStream in = this.getClass().getResourceAsStream("/db.properties");
// Properties p = new Properties();
// try {
// p.load(in);
//
// Config.driver = p.getProperty("driver");
// Config.url = p.getProperty("url");
// Config.username = p.getProperty("username");
// Config.password = p.getProperty("password");
//
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
}