📄 day05.txt
字号:
Servlet_day05 langna 2007-9-27 星期四
回顾:
session
cookie
将扩展名滤掉;
path=path.subString(0, path.indexOf("."));
一, Fliter : 过滤器
过滤器是用于过滤Servlet的请求和响应,
过滤器是存在于请求和被请求资源之间的。
双向的;
客户端---------------> 过滤器------------------> 服务器(servlet)
客户端 <--------------- 过滤器 <----------------- 服务器(servlet)
过滤器可以调用servlet的方法; servlet返回给过滤器;
用户真正感觉到的是资源,不需要知道过滤器的路径; 过滤器只关心和自己相关的一组路径;
servlet/jsp/html等都是可以被过滤的; 在资源执行前和后都能执行;
可以做一些安全和日志的操作;session的管理;
1,
例子: 如TimeServlet:
假如: 没有servlet的源代码,只有字节码; 要求在页面中加上一行代码;
汇编和反编译都可以;但很复杂;用过滤器即可;
javax.servlet Fliter接口
Filter是一个接口,要写一个自己的Filter就只能实现Filter接口。
Filter也有自己的生命周期,他的生命周期和Servlet比较相似,
也是会先调用init()方法,然后再调用核心的处理过滤的方法doFilter(),
这个方法中可定义了过滤规则,然后是destory()方法销毁Filter对象
void init(FliterConfig){}
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){}
void destroy(){}
public class AppendFliter extends Fliter{
//覆盖三个方法
}
2, 配置文件web.xml
<filter>
<filter-name>AppendFliter</filter-name>
<filter-class>com.tarena.AppendFliter</filter-class>
</filter>
<filter-mapping>
<filter-name>AppendFliter</filter-name>
<url-pattern>/protected/*</url-pattern>
<!--这里的url-pattern就是要过滤的Servlet的url-pattern-->
<!--可以写servlet-name;写/* 表示过滤所有的servlet, session管理,计数器统计等用这个 -->
</filter-mapping>
FilterChain 代表被过滤的资源,要想资源执行,必须调用它的方法:
chain.doFliter(reqest,response);
启动服务器时就创建一个过滤器实例;init();
请求过来时调用doFliter();销毁时调用destroy();
PrinterWriter out = response.getWriter();
out.println("welcom to my page! ");//在前面追加一行;
chain.doFliter(reqest,response);//表明调用被过滤的资源;
out.println("after my page !");
//在后面打不出来;因为在servlet中out被关了;将out.close()注释掉;
response.getWriter();//得到的是一个引用,不是重新打开,状态只有一个,取到的是被关闭的out;
3, 包装器:
MyResponse 继承 HttpServletResponse 的包装类;大部分功能由后者完成;
只是 getWriter方法 是我们需要的;
public class MyResponse extends HttpServletResponseWrapper{
private CharArrayWriter caw = new CharArrayWriter();
public MyResponse(HttpServletResponse response)
{
super(response);
}
public PrintWriter getWriter()
{
return new PrintWriter(caw);
}
public String getServletOutput()
{
return caw.toString();
}
}
MyResponse myResponse=new MyResponse((HttpServletResponse)response);
CharArrayWriter类,是一个将数据保存在字符数组中的输出流,
我们可以使用它来构造一个PrintWriter对象,也就实现了向内存输出。
CharArrayWriter类的toString()和toCharArray()方法就可以取得写入内存中的数据。
注意:CharArrayWriter类是一个不会真正输出的类,他的write()方法
只会将内容写入字符数组,而且这个字符数组是会自动增长的;
servlet :
doGet 中
out.getWriter();//得到的是向内存中输出的流;
out.close();//只是将向内存中输出关闭; 关闭该流, 此方法并不释放缓冲区;仍然可以调用其他方法;
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
MyResponse myResponse=new MyResponse((HttpServletResponse)response);
chain.doFliter(reqest,myResponse);
PrintWriter out=response.getWriter();//在过滤器中得到的是向客户端输出的流;
out.println(myResponse.getServletOutput());
out.println(" after my page");
out.close();
}
不要依赖于过滤器的顺序;相同的功能放到一个过滤器中即可;
会看时序图;关心顺序;
协作图, 能看出谁是核心对象;
画流程图: visio/rose/pd(数据库设计)
注:
servlet2.4 之前请求不是由客户端直接发送的,forward();include();
过滤器只能过滤客户端的请求;
2.4之后在web.xml 的 <filter-mapping> 标签中加上:
<dispatcher>request</dispatcher> <!--客户端的请求 -->
<dispatcher>forward</dispatcher> <!--转发的请求 -->
<dispatcher>include</dispatcher>
<dispatcher>error</dispatcher>
4, 自己写一个HttpFliter; 抽象类;以后可以复用 ;
写一个init(){}
在init(config){this.config=config; init();}
子类中如果覆盖了init()方法;也相当于覆盖了带参的方法;
写一个 public abstract doFliter(HttpServletRequest , HttpServletResponse ,FliterChain );
二, 监听器:
1, 什么是事件监听器;
怎么实现和部署监听器;
过滤器过滤哪些路径可以控制的;
监听器监听的内容是不能控制的, 监听所有请求,会话,上下文;
用来监听ServletContext , HttpSession , ServletRequest ;
都有公共方法: getAttribute, setAttribute
他们的属性都有什么用?和数量:
上下文 会 话 请 求
ServletContext HttpSession ServletRequest
数量: 一个应用一个; 一个用户一个; 所有用户发送的请求的数量;
可见范围: 全局,整个应用中; 当前用户访问到的资源; 当前请求的资源以及相关的资源(过滤或转发时);
生命周期: 和应用一样 一个会话周期; 当前请求的响应生成完毕;
应用启动时自动创建
关闭时自动销毁;
安全性: 必须考虑并发; 必须考虑并发; 线程安全的;
需要加线程同步; 需要加线程同步;
对上下文属性加锁; 同一个用户访问相同的资源;
考虑是否会出现覆盖; 不会和另外用户发生冲突;
多个servlet访问;
2, 监听上下文: (上下文对象代表当前的应用)
ServletContextListener(监听生命周期); ServletContextAttributeListener(监听属性);
服务器启动的时候是单线程;
服务器是事件源; 事件对象 ServletContextEvent ;事件监听器就是ServletContextListener;
tomcat根文件夹下有一个logs文件夹,专门用来记录日志信息的; 继承HttpServlet直接调用log(String);
public class MyContextListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent sce){
sce.getServletContext().log("MyContextListener.contextInitialized()");
// UserService us
}
public void contextDestroyed(ServletContextEvent sce){
sce.getServletContext().log("MyContextListener.contextDestroyed()");
}
}
listener的配置:
<listener>
<listener-class>com.tarena.listener.MyContextListener</listener-class>
<!--listener-class也就是实现Listener接口的类-->
</listener>
3 , HttpSessionListener:
写一个集合,放在session中;
写一个辅助类:容器类;意义上更清楚;实现可序列化接口;
可以转化成字节码网络传输;或保存在磁盘上;
session 的活化,钝化;服务器都支持;
钝化: 很长时间没有人访问,就先把它写到磁盘中,内存空间服务别的用户;
活化(激活):如果再来访问,就把状态从磁盘中取出来;
java语言中的数据结构都是可序列化的;
public class Product{}
public class Item{}//覆盖equals和hashCode方法;
public class ShoppingCart {
private Set items=new HashSet();//一种商品只有一个记录,不能重复;
public void add(Item p){
products.add(p);//首先判断一下是否已经有了这种商品;有的话取出来,数量加一;
}
public void remove(Item p){
products.remove(p);
}
public Iterator getProducts(){//返回迭代器比集合要好,只能迭代内容,不能再增加了;
}
}
利用session的监听器,创建一个购物车放进去;
销毁session 时, 将没有结算的商品放到数据库中保存起来;
也可以在入口创建一个session时,创建一个购物车放进来;
4, RequestListener: 不常用,但是可以统计点击数;
三, 连接池: (看看大概思路就行,不做要求)
数据库中没有连接池,还想提高性能? 自己把连接缓存起来用;
上下文监听器, 登陆, 创建一个数据库连接,放到上下文的属性中, 取出来用,
当应用关闭时,将连接取出来,关闭;
Connection 线程不安全的; 所有servlet只有一个;
public class ConnectionPoolListener implements ServletContextListener{
}
public class ConnectionUsingServlet {
//用完以后不能关闭连接; 但是又不符合编程习惯;写一个包装类覆盖close方法;
}
public class ConnectionWrapper extends Connection {
private Connection con;
private boolean isBusy;
private ConnectionPool pool;
public ConnectionWrapper(Connection con){
this.con=con;
}
// 实现所有的方法
public void close()throws SQLException{
isBusy=false;
pool.notifyThreads();
}
//写一个方法来真正关闭连接
public void realse(){
con.close();
}
public boolean isBusy(){
return isBusy;
}
}
在doGet(){
//对数据库的所有操作都同步;对上下文加锁;
Connection con;
synchronized(getServletContext()){
Statement st=con.createStatement();
ResultSet rs=st.executeQuery("");
}
}
把同步的代码提出来,放到ConnectionPool里; 对getConnection()方法同步;
public class ConnectionPool {
private ConnectionWrapper cw;
public void init(){//初始化连接池
Connection con=JdbcUtil.getConnection();
cw=new ConnectionWrapper(con);
}
public synchronized notifyThreads(){
notifyAll();//必须用在同步块中;拥有对象的锁才能调用;
}
public synchronized Connection getConnection(){
while(cw.isBusy()){
wait(); //必须用在同步块中;拥有对象的锁才能调用;
}
cw.setBusy(true);
return cw;
}
public void destroy(){
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -