注意:
1、路径问题
①开发时建议写“绝对路径”
在由Servlet转发到JSP页面时,此时浏览器显示的是Servlet的路径,而若JSP页面的超链接还是相当于该JSP页面的地址,则可能会出现路径混乱的问题。
②避免问题
---绝对路径:相当于当前WEB应用的根路径的路径【contextPath()】
Http://localhost:8080/contextPath(当前WEB应用的上下文路径)/a.jsp
---如何编写
若/代表的是站点的根目录,在其前面加上contextPath就可以了。而contextPath可以由request或application的getContextPath()方法获取。
<a href="<% reuqest.getContextPath() %>/TestServlet">To B</a>
response.sendRedirect(request.getContextPath() + "/path/c.jsp");
---JavaWeb开发中的/代表
>当前WEB应用的根路径:http://localhost:8080/contextPath/ ---若/需交由Servlet容器来处理
*请求转发时:request.getRequestDispatcher("/path/b.jsp").forward(request, response)
*web.xml文件中映射Servlet访问路径
*各种定制标签上的/
>WEB站点的根路径:http://localhost:8080/ ---若需交由浏览器处理
*超链接 <a href="/TestServlet">To B</a>
*表单中的action:<form action="/login.jsp">
*做请求重定向的时候:response.sendRedirect("/a.jsp");
2、表单重复提交问题
①重复提交的情况
---在表单中提交到一个Servlet,而Servlet又通过请求转发的方式响应一个JSP(HTML)页面,此时地址栏还保留着Servlet的那个路径,在响应页面点击刷新
---在响应页面没有到达时重复点击"提交"按钮
---点击"返回"再点击"提交"
②没有重复提交的情况:点"返回","刷新"原表单页面,再提交
③如何避免表单的重复提交
在表单做一个标记,提交到Servlet时,检查标记是否存在且是否和预定于的标记一致,若一致,则受理请求,并销毁标记,若不一致或没有标记,则直接响应提示信息:“重复提交”。
---仅提供一个隐藏域 不可行
---把标记放在request中 不可行
---把标记放在session中 可行
>在原表单页面生成一个随机值token
>把token值放入session属性中
>把token值放入到隐藏域中
>在目标Servlet中,获取session和隐藏域中的token值
>比较两个值是否一致:若一致,受理请求,且把session域中的token属性清除。
>若不一致,则直接响应页面:“重复提交”。
3、使用HttpSession实现验证码
①基本原理:和表单重复提交一致
---在原表单页面,生成一个验证码图片,同时,把图片中的字符串放入session中。
---在原表单页面,定义一个文本域,用于输入验证码。
---在目标的Servlet中:获取session和表单域中的验证码的值。
---比较两个值是否一致:若一致,受理请求,且把session域中的验证码属性清除。
---若不一致,则直接通过重定向的方式返回原表单页面并提示:“验证码错误”。
七、标签
1、带标签体的自定义标签
①若一个标签有标签体:
<atguigu:TestJspFragment>abcdefg</atguigu:TestJspFragment>
在自定义标签的标签处理器中使用JspFragment对象封装标签体信息
②若配置了标签含有标签体,则JSP引擎会调用setJspBody()方法把JspFragment传递给标签处理器类,在SimpleTagSupport中还定义了一个getJspBody()方法,用于返回JspFragment对象。
③JspFragment的invoke(writer)方法:把标签体内容从writer中输出,若为null,则等同于invoke(getJspContext().getOut()),即直接把标签体内容输出到页面上。
④在tld文件中,使用body-content节点来描述标签体的类型
<body-content>:指定标签体的类型,大部分情况下,取值为scriptless,可能取值有三种。
---empty:没有标签体
---scriptless:标签体可以包含el表达式和JSP动作元素,但不能包含JSP的脚本元素。
---tagdependent:表示标签体交由标签本身去解析处理。若指定tagdependen,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器。
<body-content>tagdependent</body-content>
⑤定义一个自定义标签:<atguigu:print time="10"></atguigu>
2、开发有父标签的标签
①父标签无法获取子标签的引用,父标签仅把子标签作为标签体来使用
②子标签可以通过getParent()方法来获取父标签的引用(需继承SimpleTagSupport或自实现SimpleTag接口的该方法):若子标签确实有父标签,JSP引擎会把代表父标签的引用通过setParent(JspTag.parent)赋给标签处理器
③注意:父标签的类型是JspTag类型的,该接口是一个空接口,但是来统一SimpleTag和Tag的,实际使用需要进行类型的强制转换
④在tld配置文件中,无需为父标签有额外的配置,但子标签是以标签体的形式存在的,所有父标签的<body-content></body-content>需设置为scriptless
⑤实现
---开发3个标签:choose、when、otherwise
---其中when标签有一个boolean类型的属性:test
---choose是when和otherwise的父标签
---when在otherwise之前使用
---在父标签choose中定义一个“全局”的boolean类型的属性flag:用于判断子标签在满足条件的情况下是否执行
>若when的test为true,且when的父标签的flag也为true,则执行when的标签体(正常输出标签体的内容,同时把flag置为false)
>若when的test为true,且when的父标签的flag为false,则不执行标签体
>若flag为true,otherwise执行标签体
3、自定义标签
①HelloWorld
---创建一个标签体处理器类:实现SimpleTag接口
---在WEB-INF文件夹下新建一个.tld文件(标签描述文件)为扩展名的.xml文件,并拷入固定的部分:并对其进行修改
---在tld文件中描述自定义的标签
---在JSP页面上使用自定义标签
>使用taglib指定导入标签库描述文件
>使用自定义标签
②setJspContext:一定会被JSP引擎所调用,先于doTag,把代表JSP引擎的pageContext传给标签处理器类。
③带属性的标签
---先在标签处理类中定义setter方法,建议把所有的属性类型都设为String型
---在tld描述文件中来描述属性(mytag.tld)
---在页面使用属性,属性名同tld文件中定义的名字
<atguigu:hello value="${param.name}" count="10" />
④通常情况下开发简单标签直接继承SimpleTagSupport即可,可以直接调用其对应的getter方法得到对象。
八、过滤器Filter
1、Filter是什么?
①JavaWeb的一个重要组件,可以发送到Servlet的请求进行拦截,对响应也可以。
②Filter是实现了Filter接口的Java类
③Filter需要在web.xml文件中进行配置和映射
2、如何创建一个Filter?
①创建一个Filter类实现Filter接口
②在web.xml文件中配置并映射该Filter,其中url-pattern指定该Filter可以拦截哪些资源,既可以通过哪些url访问到该Filter
注册Filter ——>映射Filter
3、Filter相关的API Filter接口
①public void init(FilterConfig filterConfig)
类似于Servlet的init方法,在创建Filter对象(Filter对象在Servlet容器加载当前应用时即被创建)后,立即被调用,且只被调用一次,该方法用于对当前的Filter进行初始化操作,Filter实例是单例的。
>FilterConfig类似于ServletConfig
>可以在web.xml文件中配置当前的Filter的初始化参数,配置方式和Servlet相似
②public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
真正的Filter的逻辑代码需要编写在该方法中,每次拦截器都会调用该方法。
>FilterChain:Filter链,多个Filter可以构成Filter链
doFilter(ServletRequest request, ServletResponse response):把请求传给Filter的下一个Filter,若当前Filter是Filter链的最后一个Filter,将把请求交给目标Servlet(/JSP页面)
多个Filter拦截的顺序和<Filter-mapping>配置有关,靠前的先被调用
4、<dispatcher>元素:指定过滤器所拦截的资源被Servlet容器调用的方式,可以是REQUEST、INCLUDE、FORWARD和ERROR之一,默认的是REQUEST。可设置多个<dispatcher>子元素来指定Filter对资源的多种调用方式进行拦截。
①REQUEST:用户之间访问页面时,web容器将会调用过滤器,如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,该Filter被调用(使用GET或POST请求直接访问)
②INCLUDE:目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用,除此之外,该Filter不会被调用(或<jsp:forward page="" /> 或通过page指令的errorPage转发页面)
③FORWARD:目标资源通过RequestDispatcher的forward()方法访问时,那么该过滤器被调用,除此之外,该过滤器不被调用。
④ERROR:目标资源是通过声明式异常处理机制调用时,那么该Filter被调用,除此之外不会。
在web.xml文件中通过error-page节点进行声明
<error-page>
<exception-type>java.lang.ArithmrticException</exception-type>
<location>/test.jsp</location>
</error-page>
九、监听器Listener
监听器:专门用于对其他对象身上发生的事件或状态进行监听和相应处理的对象,当被监听的对象发送情况,立即采用相应的行动。
监听器的分类:
*监听域对象(HttpSession、request、application)自身的创建和销毁事件的监听器。
*监听域对象中的属性增加或删除的事件监听器。
*监听绑定到HttpSession域中的某个对象的状态的事件监听器。
1、ServletContextListener
①what:监听ServletContext对象被创建或销毁的Servlet监听器。
②how:
---创建了一个实现ServletContextListener的类,并且实现其中的两个方法
public class HelloServletContextListener implements ServletContextListener
---在web.xml文件中配置Listener
<Listener>
<Listener-class> </Listener-class>
</Listener>
③why:ServletListener是最常用的Listener,可以在当前WEB应用被加载时对当前WEB应用的相关资源进行初始化操作。
---创建数据库连接池
---创建Spring的IOC容器
---读取当前WEB应用的初始化参数
④API
//ServletContext对象被创建(即当前应用被加载时),Servlet容器调用此方法
public void contextInitialized(ServletContextEvent sce)
//ServletContext对象被销毁(即当前应用被卸载时),Servlet容器调用此方法
public void contextDestroyed(ServletContextEvent sce)
ServletContextEvent中的:getServletContext()获取ServletContext
2、ServletRequestListener & HttpSessionListener
①和ServletContextListener类似
②利用ServletRequestListener,HttpSessionListener,ServletContextListener可以把request、session、application的生命周期做进一步了解。
---request:是一个请求,当一个响应返回时,即被销毁,当发送一个请求转发的过程是一个request对象,重定向是两个请求。
---session:当第一次访问WEB应用的一个JSP或Servlet时,且该JSP或Servlet还需要创建session对象,服务器会创建
>session销毁:session过期,直接调用session的invalidate()方法,当前WEB应用被卸载(session可以被持久化)
关闭浏览器不代表session被销毁,还可以通过sessionid找到服务器中的session JSESSIONID
---application:贯穿于当前的WEB应用的生命周期,当前WEB应用被加载时创建application对象,卸载时销毁。
3、XxxAttributeListener
①监听ServletContext、HttpSession、ServletRequest中添加的属性、替换属性、移除属性的事件监听器。
②以ServletRequestAttributeListener为例
public void attributeAdded(ServletRequestAttributeEvent srae){} 添加属性
public void attributeRemoveed(ServletRequestAttributeEvent srae){} 移除属性
public void attributeReplaced(ServletRequestAttributeEvent srae){} 替换属性
③API:
ServletRequestAttributeEvent:
getName():获取属性名
getValue():获取属性值
4、HttpSessionBindingListener
①该监听器实现了该接口的Java对象被绑定到session,或从session中解除绑定的事件
//当前对象被绑定带session时调用该方法
public void valueBound(HttpSessionBindingEvent event){}
//当前对象从session中解除绑定
public void valueUnBound(HttpSessionBingdingEvent event){}
②该监听器不需要在web.xml中配置
③API:getName() getValue() getSession()
5、HttpSessionActivationListener
①该监听器实现了该接口和Serializable接口的Java类的对象,随session钝化和活化事件(若不实现该接口,则只能写到磁盘,但不能读取)
---活化:从磁盘中读取到session对象
---钝化:向磁盘中写入session对象
---session对象存储在tomcat服务器work\catalina\localhost\contextPath目录下,SESSION-SER
②不需要在web.xml中配置
③//在活化之后调用
public void sessionDidActivate(HttpSessionEvent se){}
//在钝化之前调用
public void sessionWillPassivate(HttpSessionEvent se){}