javaweb三大组件,Servlet、Filter和Listener。这三者都需要配置,都可以通过web.xml或者注解来配置
一、Filter过滤器
1.1 概念
生活中的过滤器:如净水器、空气净化器,打劫。
web中的过滤器:在浏览器到服务器的请求之间,或者服务器对浏览器的响应之间,进行拦截,以此来完成特殊的功能。
过滤器的作用:一般用于完成通过的操作。如登录验证、统一编码处理、敏感字符过滤
1.2 快速入门
步骤:
- 定义一个类,实现Filter接口
- 重写方法
- 配置拦截路径
两种配置方式:
- web.xml配置(参照1.3)
- 注解配置(下面使用的就是注解配置)
在过滤器中,要考虑是否放行,如果不放行,访问的资源内容就不会被加载
放行执行的代码filterChain.doFilter(servletRequest, servletResponse); 将request跟response传进去。
jsp
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 测试过滤器.. </body> </html>
|
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
@WebFilter("/*") public class Demo01Filter implements Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filter被执行了...");
filterChain.doFilter(servletRequest, servletResponse); }
@Override public void destroy() {
} }
|
1.3 过滤器细节
web.xml配置
此处带有之前的servlet的配置,我就不删掉了。为了方便阅读,删掉其实比较好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>demo01</servlet-name> <servlet-class>demo01_servlet.Demo01Servlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>demo01</servlet-name> <url-pattern>/demo01</url-pattern> </servlet-mapping>
<servlet> <servlet-name>demo02</servlet-name> <servlet-class>demo01_servlet.Demo02Servlet</servlet-class>
<load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>demo02</servlet-name> <url-pattern>/demo02</url-pattern> </servlet-mapping>
<filter> <filter-name>filter01</filter-name> <filter-class>demo10_filter.Demo02Filter</filter-class> </filter> <filter-mapping> <filter-name>filter01</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
|
过滤器执行流程
如果去了解整个底层工作原理,会发现,本质就是一个方法的顺序执行。
- 执行过滤器
- 放行
- 执行放行后的资源
- 回来执行过滤器
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @WebFilter("/*") public class Demo03Filter implements Filter { public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("filter03执行..");
chain.doFilter(req, resp);
System.out.println("filter03回来了.."); }
public void init(FilterConfig config) throws ServletException {
}
}
|
jsp
1 2 3 4 5 6 7 8 9 10 11 12
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 测试过滤器.. <% System.out.println("访问到index.jsp"); %> </body> </html>
|
过滤器生命周期方法
- init:在服务器启动后,就会创建Filter对象,然后调用init方法,只执行一次。用于加载资源。
- Servlet中默认是Servlet第一次被访问创建时,仅执行一次。此时Filter的init会比Servlet的先执行
- Servlet也可以配置在启动服务器即执行,一般用于加载资源。经过测试,此时也仍然是Filter的init先执行
- doFilter:每一次请求,被拦截资源时执行。具体的拦截操作逻辑,放在这里
- destroy:在服务器正常关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次,用于释放资源
过滤器配置详解
拦截路径配置
- 具体的资源路径:/index.jsp,只有访问index.jsp资源时,过滤器才会被执行。不过这种情况一般比较少,因为过滤器通常是用来拦截通用操作的。比方说,登录验证、敏感词过滤、统一编码
- 目录拦截:/user/*,访问/user下的所有资源时,过滤器都会被执行
- 后缀名拦截:*.jsp,访问所有后缀名为jsp资源时,过滤器都会被执行
- 拦截所有资源:/*,访问所有资源时,过滤器都会被执行
拦截方式配置:资源被访问的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@WebFilter(value="/filter/index.jsp",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST}) public class Demo05Filter implements Filter { public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("filter05.."); chain.doFilter(req, resp); }
public void init(FilterConfig config) throws ServletException {
}
}
|
过滤器链(配置多个过滤器)
执行顺序
执行顺序:如果有两个过滤器,分别为过滤器1和过滤器2
- 过滤器1
- 过滤器2
- 资源执行
- 过滤器2
- 过滤器1
过滤器先后顺序
注解配置:按照类型的字符串比较规则,值小的先执行。
- 如AFilter和BFilter,A小,故A先执行;Demo6和Demo17比较,Demo17先执行
web.xml配置:<filter-mapping>
哪个定义在上边,哪个先执行
1.4 过滤器案例
登录验证
需求分析
需求:
- 如果登录,则放行
- 如果没有登录,则跳转登录页
分析:
- 判断是否是登录相关资源。如果不排除登录资源,会陷入死循环。
- 判断是否登录
代码实现
LoginFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
@WebFilter("/filter/*") public class LoginFilter implements Filter { public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request= (HttpServletRequest) req;
String requestURI = request.getRequestURI(); if (requestURI.contains("/filter/login.jsp") || requestURI.contains("/filter/loginServlet")||requestURI.contains("/filter/checkCodeServlet")||requestURI.contains("/css/")||requestURI.contains("/js/")) { System.out.println("登录资源,放行"); chain.doFilter(req,resp); }else{ HttpSession session = request.getSession(); if(session.getAttribute("user")!=null){ System.out.println("已登录,放行"); chain.doFilter(req,resp); }else{ request.setAttribute("login_error","您尚未登录"); request.getRequestDispatcher("login.jsp").forward(request,resp); } }
}
public void init(FilterConfig config) throws ServletException {
}
}
|
LoginServlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @WebServlet("/filter/loginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8"); HttpSession session = request.getSession();
String username = request.getParameter("username"); String password = request.getParameter("password"); String checkcode = request.getParameter("checkcode");
String code = (String)session.getAttribute("checkcode"); session.removeAttribute("checkcode"); if(code!=null&&code.equalsIgnoreCase(checkcode)){ if("胡列娜".equals(username)&&"fairy".equals(password)){ session.setAttribute("user",username); response.sendRedirect("/filter/success.jsp"); }else{ request.setAttribute("login_error","用户名或者密码错误"); request.getRequestDispatcher("/filter/login.jsp").forward(request,response); } }else{ request.setAttribute("cc_error","验证码错误"); request.getRequestDispatcher("/filter/login.jsp").forward(request,response); }
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
|
CheckCodeServlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| @WebServlet("/filter/checkCodeServlet") public class CheckCodeServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int width=100; int height=30;
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); Graphics g=image.getGraphics(); g.setColor(new Color(255,255,255,255)); g.fillRect(0,0,width,height); g.setColor(Color.RED); g.drawRect(0,0,width-1,height-1); String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; Random r=new Random(); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 4; i++) { g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,20)); char c = str.charAt(r.nextInt(str.length())); stringBuilder.append(c); g.drawString(String.valueOf(c),width/5*(i+1),height/2+10); } String checkcode=stringBuilder.toString(); request.getSession().setAttribute("checkcode",checkcode); g.setColor(Color.green); for (int i = 0; i < 10; i++) { int x1=r.nextInt(width); int y1=r.nextInt(height); int x2=r.nextInt(width); int y2=r.nextInt(height); g.drawLine(x1,y1,x2,y2);
} ImageIO.write(image,"jpg",response.getOutputStream()); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); } }
|
login.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> <style> * { margin: 0; padding: 0; }
.box { background-color: #74C3B5; color: #fff; width:100vw; height:100vh; position:fixed; } .container { width: 600px; margin: 80px auto; text-align: center; }
.logo { width: 250px; height: 70px; margin: auto; border-radius: 5px; margin-bottom: 40px; font-size:44px; }
input { color: #fff; width: 250px; height: 40px; transition: all ease .4s; background-color: rgba(255, 255, 255, .2); text-align: center; font-size: 20px; border: 2px solid rgba(255, 255, 255, .4); outline: none; border-radius: 5px; } img { outline: 0; border: 1px solid rgba(255, 255, 255, 0.4); background-color: rgba(255, 255, 255, 0.2); width: 250px; height: 70px; border-radius: 3px; padding: 5px 8px; margin: 0 auto 5px auto; display: block; text-align: center; color: white; opacity: .9; box-sizing: border-box; }
input:focus { color: #75DFB7; width: 300px; background-color: #fff; }
button { width: 250px; height: 40px; border: none; background-color: rgba(255, 255, 255, .9); font-size: 20px; color: #75DFB7; cursor: pointer; border-radius: 5px; }
ul { list-style: none; }
li { line-height: 50px; }
form a { text-decoration: none; color: #fff; }
input::placeholder { color:#fff; } .bg-bubbles { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; } .bg-bubbles li { position: absolute; list-style: none; display: block; width: 40px; height: 40px; background-color: rgba(255, 255, 255, 0.15); bottom: -160px; -webkit-animation: square 25s infinite; animation: square 25s infinite; -webkit-transition-timing-function: linear; transition-timing-function: linear; } .bg-bubbles li:nth-child(1) { left: 10%; } .bg-bubbles li:nth-child(2) { left: 20%; width: 80px; height: 80px; -webkit-animation-delay: 2s; animation-delay: 2s; -webkit-animation-duration: 17s; animation-duration: 17s; } .bg-bubbles li:nth-child(3) { left: 25%; -webkit-animation-delay: 4s; animation-delay: 4s; } .bg-bubbles li:nth-child(4) { left: 40%; width: 60px; height: 60px; -webkit-animation-duration: 22s; animation-duration: 22s; background-color: rgba(255, 255, 255, 0.25); } .bg-bubbles li:nth-child(5) { left: 70%; } .bg-bubbles li:nth-child(6) { left: 80%; width: 120px; height: 120px; -webkit-animation-delay: 3s; animation-delay: 3s; background-color: rgba(255, 255, 255, 0.2); } .bg-bubbles li:nth-child(7) { left: 32%; width: 160px; height: 160px; -webkit-animation-delay: 7s; animation-delay: 7s; } .bg-bubbles li:nth-child(8) { left: 55%; width: 20px; height: 20px; -webkit-animation-delay: 15s; animation-delay: 15s; -webkit-animation-duration: 40s; animation-duration: 40s; } .bg-bubbles li:nth-child(9) { left: 25%; width: 10px; height: 10px; -webkit-animation-delay: 2s; animation-delay: 2s; -webkit-animation-duration: 40s; animation-duration: 40s; background-color: rgba(255, 255, 255, 0.3); } .bg-bubbles li:nth-child(10) { left: 90%; width: 160px; height: 160px; -webkit-animation-delay: 11s; animation-delay: 11s; } @-webkit-keyframes square { 0% { -webkit-transform: translateY(0); transform: translateY(0); } 100% { -webkit-transform: translateY(-700px) rotate(600deg); transform: translateY(-700px) rotate(600deg); } } @keyframes square { 0% { -webkit-transform: translateY(0); transform: translateY(0); } 100% { -webkit-transform: translateY(-700px) rotate(600deg); transform: translateY(-700px) rotate(600deg); } } error { color:deeppink; } </style> </head> <body> <% String login_error = (String) request.getAttribute("login_error"); String cc_error = (String) request.getAttribute("cc_error"); %> <div class="box"> <div class="container"> <div class="logo"> 住房公积金 </div> <% if(login_error!=null){ out.write("<error>"+login_error+"</error>"); } if(cc_error!=null){ out.write("<error>"+cc_error+"</error>"); } %> <form action="/filter/loginServlet" method="post"> <ul> <li><input type="text" name="username" placeholder="用户名" autocomplete="off"></li> <li><input type="text" name="password" placeholder="密码" autocomplete="off"></li> <li><input type="text" name="checkcode" placeholder="不区分大小写"></li> <li><img id="checkcode" src="/filter/checkCodeServlet" alt=""></li> <li> <button>登录</button> </li> </ul> </form> </div> <ul class="bg-bubbles"> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </div> <script> document.querySelector("#checkcode").onclick=function (){ let url = "/filter/checkCodeServlet"; let date=new Date().getTime(); this.setAttribute("src", url+"?"+date) } </script> </body> </html>
|
success.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>主页</title> <style> * { margin: 0; padding: 0; }
.box { background-color: #74C3B5; color: #fff; width: 100vw; height: 100vh; position: fixed; } </style> </head> <body> <div class="box"> <% String user = (String) request.getSession().getAttribute("user"); out.write(user+"欢迎你");
%> </div> </body> </html>
|
注意事项:
应当注意,像login.jsp,LoginServlet.java,CheckCodeServlet.java,css,js这些都属于登录资源,访问这些的时候,直接放行。
敏感词过滤验证
需求分析
需求:表单中含有胡列娜
或者江厌离
或者邱若水
就自动转为***
分析:在过滤器中,对request传过来的内容进行修改,将修改内容后的request进行放行(request中并没有setParameter这个方法)
解决方法:
- 对request对象的getParameter方法进行增强(通过getParameter获取到的内容就是已经过滤的了)。产生一个新的request对象
- 放行。将新的request对象传入
重点分析:如何对geParameter方法进行增强?
增强对象的功能的途径:
- 设计模式:一些通用的解决固定问题的方式,参考
- 装饰模式
- 代理模式
- 概念:真实对象(比如小米公司)、代理对象(比如小米旗舰店),代理模式(代理对象代替真实对象,达到增强真实对象的目的)
- 静态代理(有一个类文件描述代理模式)
- 动态代理(在内存中形成代理类)
- 实现步骤
- 代理对象和真实对象实现相同的接口
- 代理对象=Proxy.newProxyInstance()
- 使用代理对象调用方法
- 增强方法
- 增强方式
理解动态代理demo
下面通过动态代理模拟一个现实生活中的例子,买电脑。
接口SellComputer
1 2 3 4 5
| public interface SellComputer { public String sale(double money);
public void show(); }
|
接口SellComputer的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Lenovo implements SellComputer { @Override public String sale(double money) { System.out.println("花钱"+money+"买电脑"); return "联想电脑"; }
@Override public void show() { System.out.println("展示电脑..."); } }
|
动态代理增强对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class ProxyTest { public static void main(String[] args) { Lenovo lenovo = new Lenovo();
SellComputer proxy_lenovo = (SellComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("sale")){ double money = (double)args[0]; money=money*1.2; System.out.println("专车接你..."); System.out.println("免费送货..."); String obj = (String)method.invoke(lenovo, money); return obj+"&&鼠标垫"; }else{ Object obj = method.invoke(lenovo, args); return obj; }
} }); String sale = proxy_lenovo.sale(8000); System.out.println("买到了"+sale); proxy_lenovo.show(); } }
|
代码实现
index.jsp
1 2 3 4 5 6 7 8 9 10 11 12
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="testServlet" method="post"> <textarea name="remark"></textarea> <input type="submit" value="提交"> </form> </body> </html>
|
SensitiveFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| @WebFilter("/filter_proxy/*") public class SensitiveFilter implements Filter { public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { ServletRequest proxy_req = (ServletRequest)Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("getParameter")){
String value= (String)method.invoke(req, args); if(value!=null){ for(String str:list){ if(value.contains(str)){ value=value.replaceAll(str,"***"); } } } return value; }else{ return method.invoke(req,args); } } }); chain.doFilter(proxy_req, resp); }
private List<String> list=new ArrayList<String>(); public void init(FilterConfig config) throws ServletException { try { ServletContext sc = config.getServletContext(); String realPath = sc.getRealPath("/WEB-INF/classes/sensitivewords.txt"); BufferedReader br = new BufferedReader(new FileReader(realPath, StandardCharsets.UTF_8)); String line=null; while((line=br.readLine())!=null){ list.add(line); } System.out.println("加载敏感词"); br.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
}
}
|
TestServlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13
| @WebServlet("/filter_proxy/testServlet") public class TestServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String remark = request.getParameter("remark"); response.getWriter().write("<h1>"+remark+"</h1>"); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
|
二、Listener监听器
2.1 概念
web的三大组件之一。
事件监听机制
- 事件:一件事情。比方说点击按钮
- 事件源:事件发生的地方
- 监听器:一段代码或者一个对象
- 注册监听:将事件、事件源、监听器绑定在一起,当事件源上发生某个事件后,执行监听器代码
2.2 ServletContextListener
这是个接口,用来监听ServletContext对象的创建和销毁
两个方法
- void contextDestroyed(ServletContextEvent sce):ServletConext对象被销毁之前,会调用该方法
- void contextInitialized(ServletContextEvent sce):ServletContext对象被创建之后,会调用该方法
用途:一般用于全局资源文件的加载
步骤
- 定义一个类,实现ServletContextListener接口
- 重写方法
- 配置
- web.xml配置
- 注解配置:直接在类上面添加
@WebListener
web.xml
1 2 3 4 5 6 7 8 9 10
| <listener> <listener-class>demo14_listener.ContextLoaderListener</listener-class> </listener>
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applicationContext.xml</param-value> </context-param>
|
demo演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @WebListener public class ContextLoaderListener implements ServletContextListener {
@Override public void contextInitialized(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); String realPath = servletContext.getRealPath(contextConfigLocation); try { FileInputStream fis = new FileInputStream(realPath); System.out.println(fis); } catch (FileNotFoundException e) { e.printStackTrace(); }
System.out.println("ServletContext对象被创建了"); }
@Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); } }
|