一、Listener监听器
1、javaweb开发中的监听器,是用于监听web常见对象
2、HttpServletRequest HttpSession ServletContext
3、监听它们的创建与销毁,属性变化,session绑定javaBean
4、监听机制
事件 就是一个事情
事件源 产生这个事情的源头
监听器 用于监听指定的事件的对象
注册监听 要想让监听器可以监听到事件产生,必须对其进行注册
5、javaweb开发中常见监听器
- 监听域对象的创建与销毁
-
- 监听ServletContext创建与销毁 ServletContextListener
- 监听HttpSession创建与销毁 HttpSessionListener
- 监听HttpServletRequest创建与销毁 ServletRequestListener
- 监听域对象的属性变化
-
- 监听ServletContext属性变化 ServletContextAttributeListener
- 监听HttpSession属性变化 HttpSessionAttributeListener
- 监听HttpServletRequest属性变化 ServletRequestAttributeListener
- 监听Session绑定javaBean
-
- 它是用于监听javaBean对象是否绑定到了session域 HttpSessionBindingListener
- 它是用于监听javaBean对象的活化与钝化 HttpSessionActivationListener (服务器停止后Session数据内否恢复)
6、监听器的快速入门
- 关于创建一个监听器的步骤
-
- 创建一个类,实现指定的监听器接口
- 重写接口中的方法
- 在web.xml文件中对监听器进行注册
7、关于域对象创建与销毁的演示
1、ServletContext对象的创建与销毁
这个对象是在服务器启动时创建的,在服务器关闭时销毁的
<listener>
<listener-class>com.itheima.listener.MyServletContextListener</listener-class>
</listener>
public
class
MyServletContextListener
implements
ServletContextListener{
public
void
contextInitialized(ServletContextEvent
sce
) {
System.
out
.println(
"ServletContext对象创建了"
);
}
public
void
contextDestroyed(ServletContextEvent
sce
) {
System.
out
.println(
"ServletContext对象销毁了"
);
}
}
2、HttpSession对象的创建与销毁
HttpSession session = request.getSession();
Session 销毁:
1、默认超时 30分钟
2、关闭服务器
3、invalidate()方法
4、setMaxInactiveInterval(int interval) 可以设置超时时间
问题:直接访问一个jsp页面时,是否会创建session?
会创建,因为我们默认情况下时可以在jsp页面中直接使用session内置对象的
<listener>
<listener-class>com.itheima.listener.MyHttpSessionListener</listener-class>
</listener>
public
class
MyHttpSessionListener
implements
HttpSessionListener {
public
void
sessionCreated(HttpSessionEvent
se
) {
System.
out
.println(
"HttpSession对象创建了"
);
}
public
void
sessionDestroyed(HttpSessionEvent
se
) {
System.
out
.println(
"HttpSession对象销毁了"
);
}
}
3、HttpServletRequest创建与销毁
Request对象是发送请求服务器就会创建它,当响应产生时,request对象就会销毁
<listener>
<listener-class>com.itheima.listener.MyServletRequestListener</listener-class>
</listener>
public
class
MyServletRequestListener
implements
ServletRequestListener {
public
void
requestDestroyed(ServletRequestEvent
sre
) {
System.
out
.println(
"ServletRequest销毁了"
);
}
public
void
requestInitialized(ServletRequestEvent
sre
) {
System.
out
.println(
"ServletRequest创建 了"
);
}
}
8、演示Request域对象中属性变化
在java的监听机制中,它的监听器中的方法都是有参数的,参数就是事件对象,而我们可以通过事件对象直接获取事件源。
public
class
MyServletRequestListener
implements
ServletRequestAttributeListener {
public
void
attributeAdded(ServletRequestAttributeEvent
srae
) {
System.
out
.println(
"ServletRequest添加属性了"
);
}
public
void
attributeRemoved(ServletRequestAttributeEvent
srae
) {
System.
out
.println(
"ServletRequest移除属性了"
);
}
public
void
attributeReplaced(ServletRequestAttributeEvent
srae
) {
//参数代表事件源对象
System.
out
.println(
"ServletRequest替换属性了"
);
System.out.println(srae.getName()+"\t"+srae.getValue());
}
}
9、演示session绑定javaBean
1、javaBean对象自动感知被绑定到session中
HttpSessionBindingListener 这个接口是由javaBean实现的,并且不需要在web.xml文件中注册
public
class
User
implements
Serializable, HttpSessionBindingListener{
private
String
name
;
private
int
age
;
public
void
valueBound(HttpSessionBindingEvent
event
) {
System.
out
.println(
"user对象被绑定了"
);
}
public
void
valueUnbound(HttpSessionBindingEvent
event
) {
System.
out
.println(
"user对象解除被绑定了"
);
}
}
2、javabean对象可以活化或钝化到session中
HttpSessionActivationListener 如果javaBean实现了这个接口,那么当我们正常关闭服务器时,session中javaBean对象就回被钝化到我们指定的文件中。当下一次再启动服务器,因为我们已经将对象写入到文件中,这是就会自动将javaBean对象活化到session中
<%
//session.invalidate();//使session销毁
/*request.setAttribute("name", "tom");
request.setAttribute("name", "jerry");
request.removeAttribute("name"); */
session.setAttribute(
"u"
,
new
User());
session.removeAttribute(
"u"
);
%>
我们还需要个context.xml文件来配置钝化时存储的文件
在meta-inf目录下创建一个context.xml文件
<Context>
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore" directory="it315"/>
</Manager>
</Context>
10、定时销毁session
1、怎样可以将每一个创建的session全都保存起来?
我们可以做一个HttpSessionListener,当session对象创建时,就将这个session对象装入到一个集合中
将集合List<HttpSession>保存到ServletContext域中
2、怎么样可以判断session过期了?
在HttpSession中有一个方法public long getLastAccessedTime()
它可以得到session对象最后使用的时间
可以使用invalidate方法销毁
我们上面的操作需要使用任务调度功能
在java中有一个Timer定时器类
public
class
TestTimer {
public
static
void
main(String[]
args
) {
//创建一个计时器对象
Timer
t
=
new
Timer();
//调度任务
t
.schedule(
new
TimerTask() {
//要执行的任务
@Override
public
void
run() {
System.
out
.println(
new
Date().toLocaleString());
}
}, 2000, 1000); //2000 执行任务前的延迟时间 1000 执行各后续任务之间的时间间隔
}
}
程序在使用时,需要考虑并发问题,因为我们在web中,它一定是一个多线程的,那么我们的程序对集合进行了添加,还有移除操作。
public
class
MyServletContextListener
implements
ServletContextListener {
public
void
contextInitialized(ServletContextEvent
sce
) {
//通过事件源对象得到事件源(ServletContext)
ServletContext
application
=
sce
.getServletContext();
//创建一个集合用于存储所有session对象
final
List<HttpSession>
list
= Collections.synchronizedList(
new
ArrayList<HttpSession>());
//把集合放到application域中
application
.setAttribute(
"sessions"
,
list
);
//创建一个计时器对象
Timer
t
=
new
Timer();
t
.schedule(
new
TimerTask() {
@Override
public
void
run() {
System.
out
.println(
"开始扫描了。。。"
);
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
HttpSession
session = (HttpSession) iterator.next();
long
l
= System.currentTimeMillis()-
session
.getLastAccessedTime();
if
(
l
>5000){
//如果时间大于5秒,把session销毁
System.
out
.println(
"session移除了"
+
session
.getId());
session
.invalidate();
//把session销毁
//list.remove(session);//从集合中移除
iterator.remove();
}
}
/*for (HttpSession session : list) {
long l = System.currentTimeMillis()-session.getLastAccessedTime();
if(l>5000){//如果时间大于5秒,把session销毁
session.invalidate();//把session销毁
list.remove(session);//从集合中移除
}
}*/
}
}, 2000, 5000);
//延迟2秒后执行,每间隔5秒执行一次
}
public
void
contextDestroyed(ServletContextEvent
sce
) {
}
}
public
class
MySessionListener
implements
HttpSessionListener {
public
void
sessionCreated(HttpSessionEvent
se
) {
HttpSession
session
=
se
.getSession();
//得到application对象中的list集合
ServletContext
application
=
session
.getServletContext();
//得到session对象,并放入到list集合中
List<HttpSession>
list
= (List<HttpSession>)
application
.getAttribute(
"sessions"
);
list
.add(
session
);
System.
out
.println(
"添加了"
+
session
.getId());
}
public
void
sessionDestroyed(HttpSessionEvent
se
) {
//
TODO
Auto-generated method stub
}
}
二、Filter过滤器(重要)
1、javaweb中的过滤器可以拦截所有访问web资源的请求或响应操作
2、Filter快速入门
- 步骤
-
- 创建一个类实现Filter接口
- 重写接口中方法,doFilter方法是真正过滤的
- 在web.xml文件中配置
- 注意:在Filter的doFilter方法内如果没有执行chain.doFilter(request,response)那么资源是不会被访问到的
public
class
MyFilter
implements
Filter{
public
MyFilter(){
System.
out
.println(
"MyFilter实例化了"
);
}
public
void
init(FilterConfig
filterConfig
)
throws
ServletException {
System.
out
.println(
"MyFilter初始化了"
);
}
public
void
doFilter(ServletRequest
request
, ServletResponse
response
,
FilterChain
chain
)
throws
IOException, ServletException {
System.
out
.println(
"MyFilter1拦截开始了"
);
//放行
chain
.doFilter(
request
,
response
);
System.out.println("拦截结束了");
}
public
void
destroy() {
System.
out
.println(
"MyFilter销毁了"
);
}
}
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.itheima.filter.MyFilter</filter-class>
<filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- FilterChain
-
- FilterChain是servlet容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用FilterChain调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。
- 问题:怎么样可以形成一个Filter链?只要多个Filter对同一资源进行拦截就可以形成Filter链
- 问题:怎样确定Filter的执行顺序?由<filter-mapping>来确定
- Filter生命周期
-
- Servlet生命周期:实例化、初始化、服务、销毁
- Filter生命周期:
-
- 当服务器启动,会创建Filter对象,并调用init方法,只调用一次
- 当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法
- 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作
- FilterConfig
-
- 在Filter的init方法上有一个参数,类型就是FilterConfig
- FilterConfig它是Filter的配置对象,它可以完成下列功能
-
- 获取Filter名称
- 获取Filter初始化参数
- 获取ServletContext对象
- String getFilterName()
- String getInitParameter(String name)
- Enumeration getInitParameterNames()
- ServletContext getServletContext()
- 怎样在Filter中获取一个FilterConfig对象
-
public class MyFilterConfigTest implements Filter{private FilterConfig filterConfig ;public void init(FilterConfig filterConfig ) throws ServletException {this . filterConfig = filterConfig ;}public void doFilter(ServletRequest request , ServletResponse response ,FilterChain chain ) throws IOException, ServletException {//通过FilterConfig对象获取到配置文件中的初始化信息String encoding = filterConfig .getInitParameter( "encoding" );System. out .println( encoding );request .setCharacterEncoding( encoding );//放行chain .doFilter( request , response );}public void destroy() {// TODO Auto-generated method stub}}
-
<filter><filter-name>MyFilterConfigTest</filter-name><filter-class>com.itheima.filter.MyFilterConfigTest</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><filter-mapping><filter-name>MyFilterConfigTest</filter-name><servlet-name>ServletDemo2</servlet-name></filter-mapping>
- Filter配置
-
- 基本配置
-
<filter><filter-name>filter名称</filter-name><filter-class>Filter类的包名.类名</filter-class></filter>
-
<filter-mapping><filter-name>filter名称</filter-name><url-pattern>路径</url-pattern></filter-mapping>
- 其他配置
1、<url-pattern>
完全匹配 以"/demo1"开始,不包含通配符*
目录匹配 以"/"开始 以*结束
扩展名匹配 *.xxx 不能写成/*.xxx
2、<servlet-name>
它是对指定的servlet名称的servlet进行拦截的
3、<dispatcher>
可以取的值有 REQUEST FORWARD ERROR INCLUDE
它的作用是:当以什么方式去访问web资源时,进行拦截操作
1、REQUEST 当是从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置的,它也是默认值
2、FORWARD 它描述的是请求转发的拦截方式配置
3、ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用,除此以外,过滤器不会被调用
4、INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用,除此以外,过滤器不会被调用
三、自动登录
1、创建库与表
create database day17
use day17
create table user{
id int primary key auto_increment
username varchar(100)
password varchar(100)
}
insert into user values(null,"tom","123");
2、自动登录功能实现
1、当用户登录成功后,判断是否勾选了自动登录,如果勾选了,就将用户名与密码持久化存储到cookie中
2、做一个Filter,对需要自动登录的资源进行拦截
3、问题
1、如果用户想要登录操作,还需要自动登录吗?
2、如果用户已经登陆了,还需要自动登录吗?
4、AutoFilter.java
public
class
AutoLoginFilter
implements
Filter {
public
void
init(FilterConfig
filterConfig
)
throws
ServletException {
//
TODO
Auto-generated method stub
}
public
void
doFilter(ServletRequest
request
, ServletResponse
response
,
FilterChain
chain
)
throws
IOException, ServletException {
// 1、转换两个对象HttpServletRequest,HttpServletResponse
HttpServletRequest
req
= (HttpServletRequest)
request
;
HttpServletResponse
resp
= (HttpServletResponse)
response
;
String
uri
=
req
.getRequestURI();
// /day17_02_autologin/login.jsp
String
path
=
req
.getContextPath();
// /day17_02_autologin
path
=
uri
.substring(
path
.length());
// /login.jsp
//如果请求的资源不是login.jsp,也不是/servlet/loginServlet,才往下执行
if
(!(
"/login.jsp"
.equals(
path
) ||
"/servlet/loginServlet"
.equals(
path
))) {
User
user
= (User)
req
.getSession().getAttribute(
"user"
);
//如果session得到了user对象,说明已经登录过或自动登录过。
//那么请求下一个资源时就不用执行自动登录了。
//用户没有登录过,我们才执行自动登录
if
(
user
==
null
) {
System.
out
.println(
"aaaaaaaaaaaaaaa"
);
// 2、处理业务
// 得到cookies数组
Cookie[]
cookies
=
req
.getCookies();
String
username
=
""
;
String
password
=
""
;
// 从数组中找到想要的user对象的信息
for
(
int
i
= 0;
cookies
!=
null
&&
i
<
cookies
.
length
;
i
++) {
if
(
"user"
.equals(
cookies
[
i
].getName())) {
String
value
=
cookies
[
i
].getValue();
// tom&123
String[]
values
=
value
.split(
"&"
);
username
=
values
[0];
password
=
values
[1];
}
}
// 登录操作
UserService
us
=
new
UserService();
User
u
=
us
.findUser(
username
,
password
);
// 如果登录成功,把用户信息存到session中
if
(
u
!=
null
) {
req
.getSession().setAttribute(
"user"
,
u
);
}
}
}
// 3、放行
chain
.doFilter(
request
,
response
);
}
public
void
destroy() {
//
TODO
Auto-generated method stub
}
}
5、AutoLoginServlet.java
public
class
LoginServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest
request
, HttpServletResponse
response
)
throws
ServletException, IOException {
String
username
=
request
.getParameter(
"username"
);
String
password
=
request
.getParameter(
"password"
);
//MD5加密
password
= MD5Utils.md5(
password
);
UserService
us
=
new
UserService();
User
user
=
us
.findUser(
username
,
password
);
if
(
user
!=
null
){
String
autologin
=
request
.getParameter(
"autologin"
);
Cookie
cookie
=
new
Cookie(
"user"
,
user
.getUsername()+
"&"
+
user
.getPassword());
cookie
.setPath(
"/"
);
if
(
autologin
!=
null
){
//要把用户信息保存到cookie中
cookie
.setMaxAge(60*60*24*7);
}
else
{
//要清除cookie对象的数据
cookie
.setMaxAge(0);
}
response
.addCookie(
cookie
);
//把cookie对象保存到客户端
request
.getSession().setAttribute(
"user"
,
user
);
request
.getRequestDispatcher(
"/home.jsp"
).forward(
request
,
response
);
}
else
{
request
.setAttribute(
"msg"
,
"用户名或密码错误,请重新登录!"
);
request
.getRequestDispatcher(
"/login.jsp"
).forward(
request
,
response
);
}
}
public
void
doPost(HttpServletRequest
request
, HttpServletResponse
response
)
throws
ServletException, IOException {
doGet(
request
,
response
);
}
}
四、MD5加密
1、在mysql中可以对数据进行md5加密
md5(字段)
update user set password = md5(password);
2、在java中也提供了md5加密
public
class
MD5Utils {
/**
* 使用md5的算法进行加密
*/
public
static
String md5(String
plainText
) {
byte
[]
secretBytes
=
null
;
try
{
secretBytes
= MessageDigest.getInstance(
"md5"
).digest(
plainText
.getBytes());
}
catch
(NoSuchAlgorithmException
e
) {
throw
new
RuntimeException(
"没有md5这个算法!"
);
}
String
md5code
=
new
BigInteger(1,
secretBytes
).toString(16);
for
(
int
i
= 0;
i
< 32 -
md5code
.length();
i
++) {
md5code
=
"0"
+
md5code
;
}
return
md5code
;
}
}
五、全局的编码过滤器
1、分析:我们之前做的操作,只能对post请求是满足的
req.setCharacterEncoding("utf-8");//只对post方式满足
2、怎么可以做成一个通用的,可以处理post,get所有的请求的?
在java中怎样可以对一个方法进行功能增强?
1、继承
2、装饰设计模式
1、创建一个类让它与被装饰类实现同一个接口或继承同一个父类
2、在装饰类中持有一个被装饰类的引用
3、重写要增强的方法
问题:我们获取请求参数有以下方法:
1、getParameter
2、getParameterValues
3、getParameterMap
这三个方法都可以获取请求参数
分析后,我们知道getParameter与getParameterValue方法可以依赖于getParamterMap方法来实现
3、
// 实现与被包装对象相同的接口
// 定义一个与被包装类相对象的引用
// 定义一个构造方法,把被包装对象传过来
// 对于不需要改写方法,直接调用
// 对于无需要改写方法,写自己的方法体
class
MyRequest
extends
HttpServletRequestWrapper {
HttpServletRequest
request
;
public
MyRequest(HttpServletRequest
request
) {
super
(
request
);
this
.
request
=
request
;
}
/*
* @Override public String getParameter(String name) { name =
* request.getParameter(name);//乱码 try { return new
* String(name.getBytes("iso-8859-1"),"UTF-8"); } catch
* (UnsupportedEncodingException e) { e.printStackTrace(); } return null; }
*/
@Override
public
String getParameter(String
name
) {
Map<String, String[]>
map
= getParameterMap();
return
map
.get(
name
)[0];
}
@Override
public
String[] getParameterValues(String
name
) {
Map<String, String[]>
map
= getParameterMap();
return
map
.get(
name
);
}
private boolean flag = true;
@Override
public
Map<String, String[]> getParameterMap() {
Map<String, String[]>
map
=
request
.getParameterMap();
// 乱码
if (flag
) {
for
(Map.Entry<String, String[]>
m
:
map
.entrySet()) {
String[]
values
=
m
.getValue();
for
(
int
i
= 0;
i
<
values
.
length
;
i
++) {
try
{
values
[
i
] =
new
String(
values
[
i
].getBytes(
"iso-8859-1"
),
"UTF-8"
);
}
catch
(UnsupportedEncodingException
e
) {
e
.printStackTrace();
}
}
}
flag = false;
}
return
map
;
}
}
4、过滤器
public
class
MyFilter
implements
Filter {
public
void
init(FilterConfig
filterConfig
)
throws
ServletException {
}
public
void
doFilter(ServletRequest
request
, ServletResponse
response
,
FilterChain
chain
)
throws
IOException, ServletException {
HttpServletRequest
req
= (HttpServletRequest)
request
;
// 解决post方式
// req.setCharacterEncoding("UTF-8");
req
=
new
MyRequest(
req
);
chain
.doFilter(
req
,
response
);
}
public
void
destroy() {
}
}