Servlet
1.什么是Servlet
Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中(如Tomcat Servlet容器负责Servlet和客户的通信以及调用Servlet的方法)。
Servlet可完成如下功能:
创建并返回基于客户请求的动态HTML页面
创建可嵌入到现有HTML页面中的部分HTML页面
与其他服务器资源(java程序、数据库)进行通信
2.Servlet的HelloWorld
(1)新建一个java类实现Servlet
public class HelloServlet implements Servlet{
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
@Override
public ServletConfig getServletConfig() {
System.out.println("getServletConfig");
return null;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("service");
}
@Override
public String getServletInfo() {
System.out.println("getServletInfo");
return null;
}
@Override
public void destroy() {
System.out.println("destroy");
}
} </span>
(2)web.xml中注册和映射
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ithings.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
3.Servlet的生命周期
(1)构造器:只被调用一次。第一次请求Servlet时创建Servlet的实例,调用构造器。Servlet是单例的(2)init:只被调用一次。在创建好实例后立即被调用。
(3)service:每次请求都会调用,实际用于响应请求
(4)destory:只被调用一次。当前Servlet所在的web应用被卸载前调用,用于释放当前Servlet所占用的资源
4.load-on-startup参数
用以指定Servlet被创建的时机。若为负数,则在第一次请求时被创建。若为0或正数,则在当前web应用被Servlet容器加载时创建,且数组越小越早被创建。
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ithings.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
5.<serlvet-mapping>通配符
可以用*匹配符配置<serlvet-mapping>,但是要注意,必须是*.do或者/开头的以/*结束的路径。/*.jsp不合法对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题:
当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应.
【Servlet引擎将调用Servlet1。】
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应.
【Servlet引擎将调用Servlet3。】
当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应.
【Servlet引擎将调用Servlet1。】
当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应.
【Servlet引擎将调用Servlet2。】
当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应.
【Servlet引擎将调用Servlet2。】
6.ServletConfig
(1)getInitParameter(String name) 获取Servlet中配置的初始化参数
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ithings.servlet.HelloServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>Tom</param-value>
</init-param>
</servlet>
String username = servletConfig.getInitParameter("username");
(2)getInitParameterNames() 获取Servlet初始化参数的名称列表
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ithings.servlet.HelloServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>Tom</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123</param-value>
</init-param>
</servlet>
Enumeration<String> names = servletConfig.getInitParameterNames();
while(names.hasMoreElements()){
String paramName = names.nextElement();
System.out.println(paramName);
}
(3)getServletContext() 返回ServletContext(当前web应用)
(4)getServletName() 返回servlet-name
7.ServletContext
(1)可由ServletConfig获取
servletCOnfig.getServletContext();
(2)代表当前web应用,可从中获取当前web应用的各个信息
1)getInitParameter(String name) getInitParameterNames() 获取当前web应用的初始化参数
<span style="white-space:pre"> </span><context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
ServletContext servletContext = servletConfig.getServletContext();
String driverName = servletContext.getInitParameter("driver");
Enumeration names = servletContext.getInitParameterNames();
while(names.hasMoreElements()){
String paramName = (String) names.nextElement();
System.out.println(paramName);
}
2)getRealPath(String path) 获取部署在服务器上的绝对路径,而不是部署前的路径
3)getContextPath() 获取当前web应用的名称
4)获取当前web应用的某一个文件对应的输入流
InputStream in = servletContext.getResourceAsStream("db.properties");
//使用classloader
ClassLoader classLoader = getClass().getClassLoader();
InputStream in = classLoader.getResourceAsStream("db.properties");
System.out.println(in);
8.ServletRequest & ServletResponse
ServletRequest
(1)获取请求参数
1)String getParameter(String name)
2)String[] getParameterValues(String name)
3)Map getParameterMap()
Map<String,String[]> map = request.getParameterMap();
for(Map.Entry<String,String[]> entry : map.entrySet()){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
4)Enumeration getParameterNames()
Enumeration names = request.getParameterNames();
while(names.hasMoreElements()){
String name = (String) names.nextElement();
}
(2)获取请求的URI
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
String requestURI = httpServletRequest.getRequestURI();
System.out.println(requestURI); // /HelloFilter/hello
(3)获取请求方式 GET/POST
String method = httpServletRequest.getMethod();
System.out.println(method);// POST
(4)针对GET请求,获取?后的字符串
String queryString = httpServletRequest.getQueryString();
System.out.println(queryString);// username=aa&password=123
(5)获取请求的servlet的路径
String servletPath = httpServletRequest.getServletPath();
System.out.println(servletPath);// /hello
ServletResponse
(1)getWriter()
返回PrintWriter对象,调用该对象的print() 方法,把print()中的参数直接打印到客户的浏览器上
(2)setContentType()
设置相应的内容类型
application/msword:word文档
9.GenericServlet
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable
(1)是Servlet接口和ServletConfig接口的实现类,但是一个抽象类,其中的service方法为抽象方法
(2)如果新建的Servlet程序直接继承GenericServlet会使开发更简洁
(3)具体实现:
1)在GenericServlet中声明了一个ServletConfig类型的成员变量,在init(ServletConfig)方法中对其进行了初始化
2)利用ServletConfig成员变量的方法实现了ServletConfig接口的方法
3)还定义了一个init()方法,在init(ServletConfig)方法中对其调用,子类可以直接覆盖init(),在其中实现对Servlet的初始化
4)不建议直接覆盖init(ServletConfig),因为如果忘记写super(ServletConfig),则会出现空指针异常
10.HttpServlet
(1)继承自GenericServlet
(2)在service方法中直接把ServletRequest和ServletResponse转为HttpServletRequest和HttpServletResponse,并调用重载了的service(HttpServletRequest, HttpServletResponse);
(3)实际应用中,直接继承HttpServlet即可
Filter
1.什么是Filter
(1)Filter可以对发送到Servlet的请求进行拦截,并对响应进行拦截
(2)Filter程序是一个实现了Filter接口的Java类,与Servlet程序相似,它由Servlet容器进行调用和执行(Servlet API中定义了三个接口类:Filter、FilterChain、FilterConfig)
(3)Filter程序需要在web.xml文件中进行注册和设置它要拦截的资源(JSP、Servlet、静态图片文件和静态html文件)
2.Filter的HelloWorld
(1)新建一个java类实现Filter
public class FirstFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("do");
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("destory");
}
}
(2)web.xml中注册和映射
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<filter>
<filter-name>firstFilter</filter-name>
<filter-class>com.ithings.FirstFilter</filter-class>
<init-param>
<param-name>param</param-name>
<param-value>value</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/*.jsp</url-pattern>
</filter-mapping>
</web-app>
3.Filter的API
(1)public void init(FilterConfig filterConfig)
1)在创建Filter对象被加载时立即被调用,只调用一次。Filter实例是单例的。
2)FilterConfig相当于ServletConfig
3)可以在web.xml中配置当前Filter的初始化参数。配置方式也和Servlet类似。
<filter>
<filter-name>filterTest</filter-name>
<filter-class>com.ithings.FilterTest</filter-class>
<init-param>
<param-name>param</param-name>
<param-value>value</param-value>
</init-param>
</filter>
(2)public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
1)真正的Filter拦截的代码写在该方法中,每次拦截都会调用该方法
2)FilterChain:Filter链,多个Filter组成一个Filter链
doFilter(ServletRequest request, ServletResponse response)
把请求传给Filter链的下一个Filter,若当前Filter是Filter链的最后一个Filter,把请求给目标Servlet或JSP
多个Filter拦截的顺序和web.xml文件中配置的filter-mapping的顺序有关
(3)public void destroy()
4 dispatcher元素:指定过滤器所拦截的资源被Servlet容器调用的方式
可以是REQUEST、 INCLUDE、 FORWARD、 ERROR之一,默认REQUEST。可以设置多个<dispatcher>子元素用来指定Filter对资源的多种调用方式进行拦截
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/hello.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
(1)REQUEST:通过GET或POST请求直接访问
(2)FORWARD:通过RequestDispatcher的forward()方法访问
(3)INCLUDE:通过RequestDispatcher的include()方法访问
(4)ERROR:如果目标资源是通过声明式异常处理机制调用时,该过滤器被调用
处理异常的几种方式:
1)page指令:<%@ page errorPage="error.jsp" >
2)web.xml中配置
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/error.jsp</location>
</error-page>
3)dispatcher
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/error.jsp</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
5 自定义HttpFilter
public abstract class HttpFilter implements Filter{
private FilterConfig filterConfig;
/*
* 不建议子类直接覆盖,会造成filterConfig初始化失败
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
/*
* 供子类继承的初始化方法
*/
protected void init(){}
/*
* 直接返回init(FilterConfig filterConfig)的filterConfig对象
*/
public FilterConfig getFilterConfig() {
return filterConfig;
}
/*
* 原生的doFilter方法,不建议子类直接覆盖
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException{
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.doFilter(request, response, chain);
}
/*
* 抽象方法,需要子类实现
*/
public abstract void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException;
@Override
public void destroy() {
}
}
6. Filter实例之禁用浏览器缓存
public class NoCacheHttpFilter extends HttpFilter{
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
response.setDateHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
chain.doFilter(request, response);
}
}
7. Filter实例之设置字符集
public class EncodingFilter extends HttpFilter{
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//从初始化参数中获取Encoding的值
String encoding = getFilterConfig().getServletContext().getInitParameter("encoding");
//设置字符集
request.setCharacterEncoding(encoding);
//放行
chain.doFilter(request, response);
}
}
8.Filter实例之登陆验证
web.xml
<!--用户信息放入session中的名称-->
<context-param>
<param-name>userSessionKey</param-name>
<param-value>USERSESSIONKEY</param-value>
</context-param>
<!--未登录,重定向的页面-->
<context-param>
<param-name>redirectPage</param-name>
<param-value>/login/login.jsp</param-value>
</context-param>
<!--不需要拦截的URL列表-->
<context-param>
<param-name>uncheckedUrls</param-name>
<param-value>/login/a.jsp,/login/list.jsp,/login/login.jsp,/login/doLogin.jsp</param-value>
</context-param>
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.ithings.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/login/*</url-pattern>
</filter-mapping>
LoginFilter.java
<span style="white-space:pre"> </span>public class LoginFilter extends HttpFilter{
private String sessionKey = null;
private String redirectPage = null;
private String uncheckedUrls = null;
@Override
protected void init(){
//从初始化参数中获取sessionKey,redirectPage,uncheckedUrls
ServletContext servletContext = getFilterConfig().getServletContext();
sessionKey = servletContext.getInitParameter("userSessionKey");
redirectPage = servletContext.getInitParameter("redirectPage");
uncheckedUrls = servletContext.getInitParameter("uncheckedUrls");
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//1.判断ServletPath是否为不需要检查的url,是则直接放行
String servletPath = request.getServletPath();
List<String> urls = Arrays.asList(uncheckedUrls.split(","));
if(urls.contains(servletPath)){
chain.doFilter(request, response);
return;
}
//2.从session中获取sessionKey对应的值,若值不存在,则重定向到redirectPage,若存在,则放行
Object user = request.getSession().getAttribute(sessionKey);
if(user == null){
response.sendRedirect(request.getContextPath() + redirectPage);
}else{
chain.doFilter(request, response);
}
}
}
9.HttpServletRequestWrapper
(1)包装类
public class MyHttpServletRequest extends HttpServletRequestWrapper{
public MyHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name){
String val = super.getParameter(name);
if(val != null && val.contains(" fuck ")){
val.replace(" fuck ", " **** ");
}
return val;
}
}
(2)使用
HttpServletRequest req = new MyHttpServletRequest(request);
chain.doFilter(req, response);
Listener
1.监听域对象自身的创建和销毁的事件监听器 ServletContextListener、HttpSessionListener、HttpServletRequest
(1)ServletContextListener 创建类,实现ServletContextListener接口
public class HelloServletContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContextListener..init..");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContextListener..destory..");
}
}
(2)web.xml中注册
<listener>
<listener-class>com.ithings.listener.HelloServletContextListener</listener-class>
</listener>
(3)应用
1)创建数据库连接池
2)创建Spring的IOC容器
3)读取当前WEB应用的初始化参数
(4)API
//ServletContext对象被创建即当前web应用被加载时,Servlet容器调用该方法
public void contextInitialized(ServletContextEvent sce);
//ServletContext对象被销毁即当前web应用被销毁时,Servlet容器调用该方法
public void contextDestroyed(ServletContextEvent sce);
(5)ServletRequestListener、HttpSessionListener
public class HelloServletContextListener implements ServletContextListener,ServletRequestListener,HttpSessionListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext 被创建");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext 被销毁");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("ServletRequest 被创建");
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("ServletRequest 被销毁");
}
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("HttpSession 被创建");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("HttpSession 被销毁");
}
}
2.监听域对象中属性的增加和删除的事件监听器 ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener
public class HelloAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener,ServletRequestAttributeListener{
//添加属性时调用
public void attributeAdded(ServletContextAttributeEvent event) {}
//移除属性时调用
public void attributeRemoved(ServletContextAttributeEvent event) {}
//替换属性时调用
public void attributeReplaced(ServletContextAttributeEvent event) {}
public void attributeAdded(HttpSessionBindingEvent event) {}
public void attributeRemoved(HttpSessionBindingEvent event) {}
public void attributeReplaced(HttpSessionBindingEvent event) {}
//可以获取到添加的属性名和属性值
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("向request中添加了一个属性 " + srae.getName() +" : " + srae.getValue());
}
public void attributeRemoved(ServletRequestAttributeEvent srae){}
public void attributeReplaced(ServletRequestAttributeEvent srae) {}
}
在web.xml中注册
<listener>
<listener-class>com.ithings.listener.HelloAttributeListener</listener-class>
</listener>
3.监听绑定到HttpSession域中的某个对象的状态的事件监听器 HttpSessionBindingListener、HttpSessionActivationListener
(1)该监听器不需在web.xml中注册。注:该监听器很少被使用
(2)HttpSessionBindingListener
public class Customer implements HttpSessionBindingListener{
/*
* 当前对象被绑定到session时调用该方法
*/
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("绑定到session");
//提供的三个方法
Object value = event.getValue();
String sessionName = event.getName();
HttpSession session = event.getSession();
}
/*
* 当前对象从session解除时调用该方法
*/
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("从session移除");
}
}
<%
Customer customer = new Customer();
session.setAttribute("customer",customer);
System.out.println("---------------------");
session.removeAttribute("customer");
%>
(3)HttpSessionActivationListener
监听实现了该接口和Serializable的java类。session对象存储在tomcat服务器的work\Catalina\localhost\contextPath目录下
public class HelloHttpSessionBindingListener implements HttpSessionActivationListener,Serializable{
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("向磁盘中写入session对象");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("从磁盘中读取session对象");
}
}