1.Filter(过滤器) 1.1 过滤器的作用 在一个请求去访问某个资源的时候,filter可以在这个请求访问到这个资源之前,把请求拦下,然后做出一系列的处理或者判断(比如编码的转换,信息的过滤、权限的判断、是否已经登录的验证等等),最后filter再决定是否要让这个请求去访问那个资源.
1.2 编写filter过滤器 写一个java类,然后实现javax.Servlet.Filter接口 这个接口中有三个方法: init destroy doFilter init:这个过滤器类被服务器创建对象的时候会调用到这个方法。 destroy:过滤器对象被销毁的时候会调用这个方法。 doFilter:当过滤器拦截到请求的时候,会调用这个doFilter. 其中最重要的doFilter方法有三个参数 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) FilterChain是一个接口,表示过滤器链,这个接口中只有一个方法 public interface FilterChain { public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException; } 同一个请求有可能要依次的通过俩个或者多个过滤器,在web中把这样多个过滤器看做一个过滤器链条对象,就是用这个FilterChain类型的对象来表示。 chain.doFilter(req,res)表示把当前的req和res传给这个过滤器链条中的下一个过滤器进行过滤,如果说链条中已经没有下一个过滤器,那么就把这次访问放行,让它去访问它真正要访问的资源. 1.3 在web.xml中配置filter过滤器 注意<filter>标签中也能使用<init-param>给过滤器传值 例如 <filter> <filter-name>encodingFilter</filter-name> <filter-class>com.briup.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 其中<url-pattern></url-pattern>有四种配置方式 第一种 精确匹配 <url-pattern>/test_servlet</url-pattern> 表示此拦截器只会拦截/test_servlet这一个路径 第二种 扩展名匹配 <url-pattern>*.html</url-pattern> 表示此拦截器只会拦截后缀名是.html的路径 第三种 路径匹配 <url-pattern>/test/*</url-pattern> 表示此拦截器拦截/test路径下的所有资源 注意:路径和扩展名匹配无法同时设置,比如下面的三个<url-pattern>都是非法的,如果设置,启动tomcat服务器会报错。 <url-pattern>/test/*.html</url-pattern> <url-pattern>/*.html</url-pattern> <url-pattern>he*.html</url-pattern> 另外<url-pattern>/aa/*/bb</url-pattern> 这个是精确匹配,url必须是 /aa/*/bb,这里的*不是通配的含义 第四种 匹配任意的url <url-pattern>/*</url-pattern> 注意:<filter-mapping>标签中,有一个子标签<dispatcher>,该标签可以指定,目标资源是以什么方式被访问的时候会被拦截器拦截,默认值是REQUEST: <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <dispatcher>标签中可以填写的值有以下内容: Servlet2.5中 REQUEST——浏览器直接访问目标资源时,filter进行拦截(默认配置) 例如:浏览器地址栏中直接输入地址进行访问 FORWARD——目标资源是通过RequestDispatcher的forward访问时,filter进行拦截 例如:浏览器地址栏中输入地址访问servletA,然后在servletA中进行forward跳转到servletB中,这时对servletB的访问就会被filter拦截 注意1:这时候最终返回的响应中的响应体的内容完全是由servletB进行设置的,例如在servletB中写进去一句字符串helloworld,或者是一个页面。 注意2:如果servletA中,在跳转至servletB之前,也设置了响应了内容,那么这个内容是会被忽略掉的 注意3:out.flush()方法之后就不能再调用forward进行跳转了,否则会有异常: Cannot forward after response has been committed INCLUDE——目标资源是通过RequestDispatcher的include方法调用时,filter进行拦截 例如:浏览器地址栏中输入地址访问servletA,然后在servletA中进行include方法把请求转发到servletB中,这时对servletB的访问就会被filter拦截 注意1:这时候最终返回的响应中的响应体的内容是有servletA和servletB俩个servlet共同设置的,例如servletA中写进去一句字符串hello,然后servletB中又写进去一个world 注意2:out.flush()方法之后可以调用include进行跳转 ERROR——目标资源是通过声明式异常处理机制时,过滤器将被调用,filter进行拦截 例如:如果出现404异常则跳转到/404.html页面中 <error-page> <error-code>404</error-code> <location>/404.html</location> </error-page> 或者 <error-page> <error-type>java.io.FileNotFoundException</error-type> <location>/not_found.html</location> </error-page> 1.4 多个filter过滤器的执行顺序 如果有多个过滤器,并且多个过滤器拦截的路径有相同的部分,就有一些路径被会这多个过滤器共同拦截,那么过滤器的顺序是按照web.xml中配置的顺序从上到下执行的 注意:一个<filter>标签可以多个<filter-mapping>标签 例如: <filter> <filter-name>test_filter</filter-name> <filter-class>com.briup.web.filter.TestFilter</filter-class> </filter> <filter-mapping> <filter-name>test_filter</filter-name> <url-pattern>/hello.html</url-pattern> </filter-mapping> <filter-mapping> <filter-name>test_filter</filter-name> <url-pattern>/first.html</url-pattern> </filter-mapping> 如果是使用的注解@WebFilter的形式来配置的过滤的话,那么多个过滤器拦截同一个路径的执行顺序是过滤器类名的首字母的顺序来执行的2.监听器(Listener) 2.1 监听器的作用 监听器用于监听web程序中的事件,例如创建、修改、删除Session、request、application等,并触发响应的事件。 监听器的设计使用的是观察者模式,事件发生的时候会自动触发该事件对应的Listener。 监听器主要用于对 Session、request、application进行监控。
2.2 编写Listener监听器 编写一个特定功能监听器类需要实现指定接口. servlet规范中共有 8 种监听器(接口),可分为三类: 分类一: 监听 request、Session、application 的创建和销毁,分别为: ServletRequestListener HttpSessionListener ServletContextListener 例如: public class ListenerTest implements HttpSessionListener, ServletContextListener, ServletRequestListener { // 创建 session public void sessionCreated(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("新创建一个session, ID为: " + session.getId()); } // 销毁 session public void sessionDestroyed(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("销毁一个session, ID为: " + session.getId()); } // 加载 application public void contextInitialized(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); System.out.println("即将启动" + servletContext.getContextPath()); } // 卸载 application public void contextDestroyed(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); System.out.println("即将关闭" + servletContext.getContextPath()); } // 创建 request public void requestInitialized(ServletRequestEvent sre) { HttpServletRequest request = (HttpServletRequest) sre.getServletRequest(); String uri = request.getRequestURI(); uri = request.getQueryString() == null ? uri : (uri + "?" + request.getQueryString()); request.setAttribute("dateCreated", System.currentTimeMillis()); System.out.println("IP " + request.getRemoteAddr() + " 请求 " + uri); } // 销毁 request public void requestDestroyed(ServletRequestEvent sre) { HttpServletRequest request = (HttpServletRequest) sre.getServletRequest(); long time = System.currentTimeMillis() - (Long) request.getAttribute("dateCreated"); System.out.println(request.getRemoteAddr() + "请求处理结束, 用时" + time + "毫秒. "); } } 分类二: 监听request、session、application三个对象中属性变化,分别为: ServletRequestAttributeListener HttpSessionAttributeListener ServletContextAttributeListener 例如:三种监听器中的方法是一样的,这里以session为例 public class SessionAttributeListenerTest implements HttpSessionAttributeListener { // 添加属性 public void attributeAdded(HttpSessionBindingEvent se) { HttpSession session = se.getSession(); String name = se.getName(); Object value = se.getValue(); System.out.println("新建session属性:" + name + ", 值为:" + value); } // 删除属性 public void attributeRemoved(HttpSessionBindingEvent se) { HttpSession session = se.getSession(); String name = se.getName(); Object value = se.getValue(); System.out.println("删除session属性:" + name + ", 值为:" + value); } // 修改属性 public void attributeReplaced(HttpSessionBindingEvent se) { HttpSession session = se.getSession(); String name = se.getName(); Object oldValue = se.getValue(); Object newValue = session.getAttribute(name); System.out.println("修改session属性:" + name + ", 原值:" + oldValue + ", 新值:" + newValue); } } 分类三: 监听Session对象里面存放着的其他对象,例如,把User对象存到了session中,下面监听器监听的是User对象,它们分别为: HttpSessionBindingListener接口 //对象被放进session前调用 valueBound(HttpSessionBindingEvent event); //对象从session中移除后调用 void valueUnbound(HttpSessionBindingEvent event); HttpSessionActivationListener接口 //即将被持久化到硬盘时 sessionWillPassivate(HttpSessionEvent se); //从硬盘加载后调用 sessionDidActivate(HttpSessionEvent se); 注意:这两类 Listener 监听的是Session 内的对象,而非 Session 本身,接口是让实体类进行实现的(例如User类或者Book类等),使用这种情况不需要在 web.xml中配置。 思考:这种监听器和上面的HttpSessionAttributeLister的区别是什么? 例如: public class User implements HttpSessionBindingListener,HttpSessionActivationListener,Serializable{ private static final long serialVersionUID = 1L; private long id; private String name; // 从硬盘加载后 @Override public void sessionDidActivate(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println(this + "已经成功从硬盘中加载。sessionId: " + session.getId()); } // 即将被持久化到硬盘时 @Override public void sessionWillPassivate(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println(this + "即将保存到硬盘。sessionId: " + session.getId()); } // 被放进session前 @Override public void valueBound(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName(); System.out.println(this + "被绑定到session \"" + session.getId() + "\"的" + name + "属性上"); } // 从session中移除后 @Override public void valueUnbound(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName(); System.out.println(this + "被从session \"" + session.getId() + "\"的" + name + "属性上移除"); } public User() { } public User(long id, String name) { this.id = id; this.name = name; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User [id=" + id + ", name=" + name + "]"; } } 2.3 在web.xml中配置Listener监听器 <listener> <listener-class>com.briup.listener.TestListener</listener-class> </listener> 或者使用注解@WebListener也可以3.web项目中的WEB-INF目录
WEB-INF是Java的WEB应用的安全目录。安全目录指的是客户端无法访问,只有服务端可以访问的目录。 例如,如果将hello.html页面存放在WEB-INF下,那么浏览器中是无法通过以下地址【直接】访问到该页面的: http://127.0.0.1:8080/xxx/WEB-INF/hello.html 但是,servlet是可以通过服务器【内部跳转】来访问到WEB-INF目录下的资源的。 所以,浏览器可以访问servlet,让servlet再通过服务器内部跳转的方式来访问到WEB-INF下的hello.html,从而提高页面的安全性。 并且在servlet中如果用户请求验证不通过,那么可以不进行服务器内部跳转到hello页面,而是返回一个错误信息。4.上传和下载 参照文档中 上传和下载 俩个目录的内容。
5.web项目的三层架构 web层: 1.接收客户端传来的参数 2.把参数封装成对象 3.把封装好的对象/数据传给service 4.根据service层的处理结果决定把那个页面返回给客户端
service层 需要定义接口 1.接收web层传过来的对象/数据(如果有的话) 2.根据这些信息进行业务逻辑处理 例如:完成一个登录功能,web层接收到用户名和密码之后,把数据传给service层,service层就要根据这些信息来判断用户名是否存在、密码是否正确、用户是否有权限、用户状态当前是否可用、用户是否推送信息、是否给用户相关提示等,这些都属于登录的业务逻辑处理 3.service在处理业务逻辑的时候,常常需要和数据库交互,这时候就需要调用dao层的相关方法来完成 4.service层根据dao层方法的返回结果,进一步的完成业务逻辑处理 5.业务逻辑处理完之后,要通过一定的方式把业务逻辑处理的结果返回给web层,以便让web层决定接下来该那个页面呈现给用户 dao层 需要定义接口 1.接受service传的参数(如果有的话) 2.和数据库进行交互 3.把交互结果返回给service层