Listener&Filter
Listener&Filter
1.Listener
監聽器,用於監聽某一個事件的發生。
監聽器的內部機制:介面回撥。
1.1介面回撥
需求:
A類執行迴圈,當迴圈次數為5時,通知B。
-
傳統做法
傳遞B的例項給A。
public class A { public static void loop(){ for(int i=0;i<10;i++){ if(i==5){ B b=new B(); b.print(); } } } } public class B { public void print(){ System.out.println("A迴圈了5次,通知到B"); } } public class Test1 { public static void main(String[] args) { A a=new A(); a.loop(); } }
-
介面回撥做法
傳遞一個介面PrintListener給A,B實現了該介面,因為A可能在B之前就寫好了,此時A並不知道有B的存在,也就更無法直接接收B的例項了,所以可以利用介面回撥解決。
public class A { public static void loop(PrintListener printListener){ for(int i=0;i<10;i++){ if(i==5){ printListener.print(); } } } } public class B implements PrintListener{ public void print(){ System.out.println("A迴圈了5次,通知到B"); } } public interface PrintListener { public void print(); } public class Test1 { public static void main(String[] args) { A a=new A(); a.loop(new B());//A中的loop方法接收的是PrintListener,而這裡可以傳遞PrintListener的實現類的例項物件,這是多型的體現。 } }
1.2Web監聽器
總共有8個,分為三種類型。
使用步驟都一致:
- 定義一個類,實現監聽器介面
- 註冊/配置監聽器
1.2.1監聽作用域的建立和銷燬
作用域共有4個:page,request,session,application,監聽作用域的建立和銷燬是監聽request,session,application三個作用域的建立和銷燬。
- ServletRequestListener
監聽request作用域物件的建立和銷燬。
request對應型別為HttpServletRequest。
reqeust建立:訪問伺服器上的任意資源時,如html,jsp,servlet;
request銷燬:伺服器已經對這次請求做出了響應後。
@WebListener//註冊監聽器,相當於在web.xml中配置的那一段
public class MyServletRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("request銷燬了");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("request初始化了");
}
}
在web.xml中註冊監聽器:
<listener>
<listener-class>com.itheima.listener.MyServletRequestListener</listener-class>
</listener>
- ServletContextListener
監聽application作用域物件的建立和銷燬。
application對應型別為ServletContext。
ServletContext建立:伺服器啟動時;
ServletContext銷燬:正常關閉伺服器。
作用:完成初始化工作,執行自定義任務排程。
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("servletContext初始化了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("servletContext銷燬了");
}
- HttpSessionListener
監聽session作用域物件的建立和銷燬。
session對應型別為HttpSession。
session建立:呼叫request.getSession(),jsp中預設建立了session;
session銷燬:關閉伺服器(包括正常和非正常);Session會話時間過期,預設是30分鐘。
作用:可以統計線上人數。
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("session建立了");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session銷燬了");
}
}
1.2.2監聽作用域屬性狀態變更
監聽在servletContext,request,session作用域中屬性的新增,替換,移除動作。
- ServletContextAttributeListener
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("servletContext屬性添加了");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("servletContext屬性移除了");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("servletContext屬性替換了");
}
}
<body>
<%
//新增name屬性
application.setAttribute("name","zhangsan");
//替換name屬性
application.setAttribute("name","wanger");
//移除name屬性
application.removeAttribute("name");
%>
</body>
- ServletRequestAttributeListener
- HttpSessionAttributeListener
1.2.3監聽HttpSession裡存值的狀態的變更
此類監聽器無須註冊。
- HttpSessionBindingListener
監聽物件與session繫結和解除繫結的動作,此物件要實現HttpSessionBindingListener。
public class Student implements HttpSessionBindingListener {
private int id;
private String name;
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("Student物件被繫結到session了");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("Student物件從session中解除綁定了");
}
...
}
<%
session.setAttribute("obj1",new Student(1,"小王"));
session.removeAttribute("obj1");
%>
- HttpSessionActivationListener
用於監聽現在session的值是鈍化(序列化)還是活化(反序列化)的動作。
鈍化:把記憶體中的資料儲存到硬碟上;
活化:把硬碟中的資料讀取到記憶體中。
鈍化活化作用:session中的值可能會很多,並且可能很長一段時間不使用這個記憶體中的值,那麼可以考慮把session的值可以儲存到硬碟上【鈍化】,等下一次使用時,再從硬碟上提取出來【活化】。
- Student2.java
public class Student2 implements HttpSessionActivationListener, Serializable {
private int id;
private String name;
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("session被鈍化了");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("session被活化了");
}
...
}
-
index2.jsp
index2.jsp將Student2存入到session,然後正常關閉伺服器,session 將被鈍化。
<%
//存入到session
session.setAttribute("obj2",new Student2(1,"coco"));
%>
-
index3.jsp
重啟伺服器後,session將被活化,此時訪問index3.jsp可以取到值。
<%--取值--%>
${obj2.name}
- 配置鈍化活化
配置讓session在一定時間內鈍化。
三種方式:
- 在tomcat的conf/context.xml裡面配置;
對所有的執行在這個伺服器的專案生效。
- 在conf/Catalina/localhost/context.xml配置;
對localhost生效。 localhost:8080
- 在自己的web工程專案中的 META-INF/context.xml配置;
只對當前的工程生效。
maxIdleSwap:1分鐘不用就鈍化
directory:鈍化後的那個檔案存放的目錄位置。D:\tomcat\apache-tomcat-7.0.52\work\Catalina\localhost\ListenerDemo\itheima
//itheima為相對路徑,存放到了D:\tomcat\apache-tomcat-7.0.52\work\Catalina\localhost\ListenerDemo\itheima
2.Filter
過濾器,其實就是對客戶端發出來的請求進行過濾,起到攔截作用。
作用:過濾敏感詞彙;統一設定編碼;自動登入等。
2.1如何使用Filter
- 定義一個類,實現Filter(注意是javax.servlet.Filter)
@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")//可簡寫成下面這行
//@WebFilter("/*")
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("過濾器1初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("來到了過濾器1");
//放行,請求會到達下一個目標
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("過濾器1被銷燬了");
}
}
- 註冊過濾器
兩種註冊方式:在web.xml中註冊,或在類上用註解註冊,見上。
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.itheima.Filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.2Filter的生命週期
- 建立
在伺服器啟動時就建立。
- 銷燬
在伺服器關閉時就銷燬。
2.3Filter的執行順序
- 客戶端發出請求,先經過過濾器,如果過濾器放行,那麼才能到servlet;
- 如果有多個過濾器,請求過來時,會按照在web.xml中註冊的對映順序(
)依次通過,只要其中任意一個不放行,那麼後面的過濾器及servlet都不會收到請求,伺服器響應回去時也會依次倒著經過過濾器。
2.4Filter細節
-
init方法中的FilterConfig引數可以用於獲取Filter註冊的名字及初始化引數,與ServletConfig類似;
-
Filter的攔截路徑
,寫法與servlet的一樣: -
全路徑匹配,以/開始
如/LoginServlet
-
目錄匹配,以/開始,以*結束
/demo01/*
-
字尾名匹配,以*開始,以後綴名結束
.jsp,.html,*.do等
-
-
攔截規則也可針對dispatcher設定
filter-mapping> <filter-name>FilterDemo1</filter-name> <dispatcher>REQUEST</dispatcher> <!--只要是請求過來都攔截,預設就是REQUEST--> <dispatcher>FORWARD</dispatcher> <!--只要是轉發都攔截--> <dispatcher>ERROR</dispatcher> <!--只要是頁面出錯跳轉時就攔截--> <dispatcher>INCLUDE</dispatcher> <!--包含頁面時就攔截--> </filter-mapping>
2.5自動登入案例
訪問login.jsp頁面不攔截,訪問index2.jsp攔截,依次判斷session和cookie,實現自動登入。
- login.jsp
<form action="LoginServlet" method="post" >
賬號:<input type="text" name="username"><br/>
密碼:<input type="text" name="password"><br/>
<input type="checkbox" name="autoLogin">自動登入<br/>
<input type="submit" value="登入">
</form>
- LoginServlet
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDao userDao=new UserDaoImpl();
User user = userDao.loginCheck(username, password);
if(user!=null){
//判斷是否勾選了自動登入
String autoLogin = request.getParameter("autoLogin");
if("on".equals(autoLogin)){
//勾選了自動登入
Cookie cookie=new Cookie("info",username+"#"+password);
cookie.setMaxAge(60*60*24*7);
//cookie.setPath("/StuSysMvc");
cookie.setPath(request.getContextPath());//當前應用名
//傳送cookie到客戶端
response.addCookie(cookie);
}
//將使用者資訊儲存到session
request.getSession().setAttribute("user",user);
response.sendRedirect("index2.jsp");
}else{
response.sendRedirect("login.jsp");
}
}
}
- index2.jsp
<body>
<c:if test="${not empty sessionScope.user}">
歡迎來自${sessionScope.user.address}的${sessionScope.user.username}!!!
</c:if>
<c:if test="${empty sessionScope.user}">
您好,請<a href="login.jsp">登入</a>!
</c:if>
</body>
- AutoLoginFilter
@WebFilter("/index2.jsp")
public class AutoLoginFilter 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;
//1.先判斷session是否失效,如果失效再判斷cookie
User sessionUser = (User) request.getSession().getAttribute("user");
if (sessionUser == null) {
//2.如果session失效,判斷cookie
Cookie[] cookies = request.getCookies();
Cookie autoLoginCookie = CookieUtils.findCookie(cookies, "info");
if (autoLoginCookie != null) {
//session失效了,但cookie不為空,所以要重新登入
String value = autoLoginCookie.getValue();
String[] split = value.split("#");
UserDao userDao = new UserDaoImpl();
User user = userDao.loginCheck(split[0], split[1]);
//重新登入成功後,再把session放回request
request.getSession().setAttribute("user", user);
}
}
//放行到index2.jsp,在index2.jsp判斷是否登入
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
}
}
2.6BeanUtils
依賴jar包:
對於這種情況,提交上來的引數很多,而這些引數又恰好能封裝成一個JavaBean,可以使用BeanUtils簡化程式碼。
- 傳統做法:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");//只針對post請求有效
String username = request.getParameter("username");
String password=request.getParameter("password");
String address=request.getParameter("addresss");
String phone=request.getParameter("phone");
String birthday=request.getParameter("birthday");
String email=request.getParameter("email");
User user=new User();
user.setUsername(username);
user.setPassword(password);
user.setAddress(address);
user.setPhone(phone);
user.setEmail(email);
try {
user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
} catch (ParseException e) {
e.printStackTrace();
}
}
- BeanUtils做法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//註冊自己的日期轉換器,將String型別的birthday轉換為Date型別
ConvertUtils.register(new MyDateConverter(),Date.class);
User user=new User();
Map<String, String[]> parameterMap = request.getParameterMap();
try {
BeanUtils.populate(user,parameterMap);
} catch (Exception e) {
e.printStackTrace();
}
}
自定義時間轉換器MyDateConverter:
public class MyDateConverter implements Converter {
@Override
// 將value 轉換 c 對應型別
// 存在Class引數目的編寫通用轉換器,如果轉換目標型別是確定的,可以不使用c 引數
public Object convert(Class c, Object value) {
String strVal = (String) value;
// 將String轉換為Date --- 需要使用日期格式化
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = dateFormat.parse(strVal);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}