JavaWeb三大组件(Servlet、Filter、Listener)和JSP相关知识,看这篇就够了

Servlet概述:

Servlet是JavaWeb三大组件之一,是基础中的基础,核心中的核心,我们必须要掌握它,其他两个组件是Filter(过滤器)、Listener(监听器),之后会进行介绍。

Servlet的直译就是小型应用程序,正如字面意思,Servlet是运行在Web服务器(如Tomcat)中的小型Java程序。当客户端发送请求到Tomcat时,由Tomcat去找到处理对应请求的Servlet进行处理。

我们编写的Servlet程序,需要直接(实现Servlet)或间接(继承HttpServlet,其父类GenericServlet实现了Servlet)的实现Servlet接口,并在web.xml中进行配置,而且必须依赖于Tomcat的jar包,才能找到Servlet类。

Web.xml配置:

<?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_3_1.xsd"
         version="3.1">

    <!--处理请求的Servlet,web.xml中可以有多个-->
    <servlet>
        <!--Servlet名字-->
        <servlet-name>demo1Servlet</servlet-name>
        <!--对应Servlet类的路径-->
        <servlet-class>com.cx.servlet.Demo1Servlet</servlet-class>
        <!--若这样配置,在启动服务器时就会调用init创建对象。数值越小(012,3自然数),创建对象的优先级越高-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!--Servlet映射,多个映射可以对应一个Servlet,但一般都是一对一的映射关系-->
    <servlet-mapping>
        <!--对应处理请求的Servlet名字-->
        <servlet-name>demo1Servlet</servlet-name>
        <!--访问路径-->
        <url-pattern>/servlet1</url-pattern>
    </servlet-mapping>

</web-app>

实现Servlet接口的方式编写Servlet程序:

直接实现Servlet接口,我们一般实现其中的三个方法:

public class Demo1Servlet implements Servlet{

    /**
     * 初始化
     * 调用init方法进行初始化,创建Servlet对象
     * 启动服务器时,init方法不会调用,第一次发送请求时,Servlet才会被创建(初始化操作只有一次)
     * 如果配置load-on-startup,在启动服务器时就会调用init创建对象-->
     * */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("Demo1Servlet---init方法调用,进行初始化");
    }

    
    /**
     * 提供服务
     * 每次请求,service都会进行相应
     * */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Demo1Servlet---service方法响应成功...");
    }

  
    /**
     * 销毁
     * 服务器关闭时调用其方法
     * */
    @Override
    public void destroy() {
        System.out.println("Demo1Servlet---destroy方法进行销毁...");
    }
}

继承HttpServlet类的方式编写Servlet程序:

继承HttpServlet时,一般重写2两个方法(因为绝大多数请求都是这两种,有例外时在重写其他方法):

/**
 *  从Servlet3.1开始,可以使用右键新建Servlet模板注解方式(@WebServlet)代替web.xml配置文件中的servlet和servlet-mapping
 * */
@WebServlet(name = "Demo2Servlet", urlPatterns = "/servlet2", loadOnStartup = 1)
public class Demo2Servlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Demo2Servlet---doGet方法被调用");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

}

Servlet执行流程:

1、启动Tomcat服务器,加载web.xml配置文件,如果有servlet配置了load-on-startup,启动时就会创建其对象。
2、在浏览器中发送请求。
3、请求到达Tomcat服务器,解析请求路径,找到对应servlet-mapping中的url-pattern,再根据servlet-mappingservlet-name,找到servlet中的servlet-name所对应的servlet-class
4、获取到servlet-class的全路径,使用反射的方式创建对应Servlet对象。
5、让Servlet对象中的service方法执行(根据请求方式,判断调用doGet、doPost等等方法)
在这里插入图片描述

ServletConfig对象及方法:

ServletConfig对象可以通过方法,获取单个servlet内的初始化参数,操作如下:

<servlet>
        <servlet-name>demo3Servlet</servlet-name>
        <servlet-class>com.cx.servlet.Demo3Servlet</servlet-class>
        <!--初始化参数-->
        <init-param>
            <param-name>username</param-name>
            <param-value>20214</param-value>
        </init-param>

        <init-param>
            <param-name>password</param-name>
            <param-value>qweasd4</param-value>
        </init-param>
</servlet>
/**
 *  ServletConfig(web.xml配置文件中servlet的信息)
 *  getServletConfig():获取servlet对象
 *  getServletName():配置Servlet的名称
 *  getInitParameter():获取初始化参数,单个返回
 *  getInitParameterNames():获取初始化参数的键集合,再通过迭代方式获取值
 * */
public class Demo3Servlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //直接调用父类方法获取ServletConfig对象
        ServletConfig servletConfig = getServletConfig();
        
        //获取Web.xml配置文件中<servlet-name>的值
        String servletName = servletConfig.getServletName();
        System.out.println("servletName:"+servletName);
        
        //根据其属性名获取单个values值
        String username = servletConfig.getInitParameter("username");
        String password = servletConfig.getInitParameter("password");
        System.out.println(username+":"+password);

        //获得枚举类
        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();

        //遍历
        while (initParameterNames.hasMoreElements()){
            //获取键值
            String elment = initParameterNames.nextElement();
            //根据键值获取values
            System.out.println(servletConfig.getInitParameter(elment));
        }
    }
}

ServletContext对象及方法:

Tomcat服务器启动后,会为每个Web项目创建一个ServletContext对象(单例的),且每个ServletContext对象彼此独立,每个项目中的Servlet对象可以共享ServletContext对象,我们可以在ServletContext对象中存入值,并且每个Servlet对象,也可以同步更新取值,操作如下:

/**
 * 通过ServletContext实现访问次数的累加
 * 
 * ServletContext:Tomcat服务器启动后,会为每一个Servlet项目创建一个ServletContext对象,彼此之间互相独立
 *  getServletContext():获取ServletContext对象
 *  getAttribute() 取值
 *  setAttribute() 存值
 *  
 * */
@WebServlet(name = "Demo4Servlet", urlPatterns = "/servlet4")
public class Demo4Servlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = getServletContext();
        //取值
        int count = (int) servletContext.getAttribute("count");
        count++;
        servletContext.setAttribute("count", count);
        System.out.println(count);
    }

    @Override
    public void init() throws ServletException {
        //获取ServletContext对象
        ServletContext servletContext = getServletContext();
        //在ServletContext对象中存入值
        servletContext.setAttribute("count",0);
    }
}

request对象和response对象:

requestresponse就是请求和响应对象,全称分别是HttpServletRequestHttpServletResponse,是service和doGet等方法的重要参数。

request对象所具的功能:

封装了浏览器发送请求的相关信息。(实际开发中使用频率较少)
@WebServlet(name = "Demo1Request", urlPatterns = "/request1")
public class Demo1Request extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()){
            //获取头名称
            String s = headerNames.nextElement();
            //获取对应信息
            String header = req.getHeader(s);
            System.out.println(s+":"+header);
        }
        //获取请求正文的字节数,GET请求没有正文,没有正文返回-1;
        int contentLength = req.getContentLength();
        System.out.println("正文字节数:"+contentLength);

        //获取请求方法
        String method = req.getMethod();
        System.out.println("请求方法:"+method);

        //获取URL路径
        String requestURI = req.getRequestURI();
        System.out.println("URL路径:" + requestURI);
	
        System.out.println("request.getContentLength(): " + req.getContentLength());
        System.out.println("request.getContentType(): " + req.getContentType());
        System.out.println("request.getContextPath(): " + req.getContextPath());
        System.out.println("request.getMethod(): " + req.getMethod());
        System.out.println("request.getLocale(): " + req.getLocale());
        System.out.println("request.getQueryString(): " + req.getQueryString());
        System.out.println("request.getRequestURI(): " + req.getRequestURI());
        System.out.println("request.getRequestURL(): " + req.getRequestURL());
        System.out.println("request.getServletPath(): " + req.getServletPath());
        System.out.println("request.getRemoteAddr(): " + req.getRemoteAddr());
        System.out.println("request.getRemoteHost(): " + req.getRemoteHost());
        System.out.println("request.getRemotePort(): " + req.getRemotePort());
        System.out.println("request.getScheme(): " + req.getScheme());
        System.out.println("request.getServerName(): " + req.getServerName());
        System.out.println("request.getServerPort(): " + req.getServerPort());


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
}
封装了请求参数。
/**
 * 获取get请求参数
 * getParameter()
 * getParameterNames()  同ServletConfig的枚举方法
 * 前台传递的数据,到后台全部是字符串类型
 *
 * 获取post请求参数
 * getParameter()
 * getParameterValues() 前台传多个值,就用这个方法
 * getParameterMap()获取请求参数的的K和V,返回一个键为String值为String[]类型的Map集合
 *
 * 解决请求参数中文乱码
 * 原因:前端页面编码为utf-8,提交到tomcat服务器,其默认编码为ISO-8859-1,所以导致乱码
 * 在servlet中要先使用ISO-8859-1解码,然后再使用utf-8解码,方可解决
 * post方式解决乱码:setCharacterEncoding("utf-8")
 * get方式解决乱码:当tomcat版本在8.5以上,get方式基本都不会出现乱码。如果版本低使用:name = new String(name.getBytes("ISO-8859-1"),"UTF-8");
 * */
@WebServlet(name = "Demo2Request", urlPatterns = "/request2")
public class Demo2Request extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      
        request.setCharacterEncoding("utf-8");
        
        //通过指定名称获取参数值数组,有可能一个名字对应多个值,例如表单中的多个复选框使用相同的name时
        String[] hobbies = request.getParameterValues("hobby");
        System.out.println(Arrays.toString(hobbies));

        //获取所有参数对应的Map,其中key为参数名,value为参数值
        Map<String, String[]> parameterMap = request.getParameterMap();

        //获得键集合
        Set<String> keys = parameterMap.keySet();

        //遍历
        for (String key : keys) {
            //根据键获取对应值
            String[] values = parameterMap.get(key);
            System.out.println(key+":"+Arrays.toString(values));
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        //通过指定名称获取参数值
        String username = request.getParameter("username");
        //如果tomcat版本低,以此方式解决中文乱码问题
        //username = new String(username.getBytes("ISO-8859-1"),"UTF-8");
        System.out.println("usernmae:"+username);

        //获取所有参数的名字
        Enumeration<String> parameterNames = request.getParameterNames();
        while(parameterNames.hasMoreElements()){
            //参数名字
            String name = parameterNames.nextElement();
            //根据参数名字,获取参数值
            String values = request.getParameter(name);
            System.out.println(name+":"+values);
        }

    }
}

是一个域对象,可以添加获取并获取数据。
@WebServlet(name = "Demo3Request", urlPatterns = "/request3")
public class Demo3Request extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        //们可以给request来设置当前域中的属性值,在该域之内(当前请求完成之前)都能获得到该属性值。
        request.setAttribute("name", "xxx");
        request.getAttribute("name");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
    }
}

请求转发。(最重要)
/**
 * request请求转发(只能在项目内部进行转发,一次请求)
 * 请求转发也可以叫做服务器端的跳转,虽然有页面的跳转但是地址栏是不会有变化的
 * req.getRequestDispatcher("页面路径").forward(req,resp);
 * request的作用域:一次响应的过程
 * */
@WebServlet(name = "Demo3Request", urlPatterns = "/request3")
public class Demo3Request extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       request.getRequestDispatcher("/succese.html").forward(request,response); 
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
    }
}

response对象所具的功能:

设置相应正文。
/**
 * response相关方法
 * getOutputStream()
 * getWriter()
 * 解决getWriter()传输中文乱码:
 * 设置response对象缓冲区的编码:setCharacterEncoding()
 * 通知浏览器,传输数据格式和采用的编码,通过响应头来设置Conttent-Type:setHeader()
 *  setContentType()等同上面两段代码
 *
 *  
 * */
@WebServlet(name = "Demo4Response", urlPatterns = "/response4")
public class Demo4Response extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置响应类型(MIME类型)为html,编码为utf-8,处理相应页面文本显示的乱码
        response.setContentType("text/html;charset=utf-8");
     
        //字节流方式设置相应正文
        response.getOutputStream().write("你好".getBytes());
        //字符流方式设置相应正文
        response.getWriter().print("返回正文");

    }
}

重定向。
@WebServlet(name = "Demo4Response", urlPatterns = "/response4")
public class Demo4Response extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       
        //重定向,相对路径写法
        response.sendRedirect("./succese.html");
    }
}

请求转发和重定向的区别:

1、请求转发只有1次请求(地址不变),重定向浏览器要发送2次请求(地址改变)
请求转发:A找B,B找C,B将C再传给A;重定向:A找B,B告诉A去找C,A重新找C。
2、请求转发可以使用request域对象传递数据,重定向不能使用request域对象
3、请求转发使用request对象调用,重定向使用response对象调用
4、请求转发路径不需要编写项目虚拟访问路径(因为请求已经在项目内部了),重定向需要编写项目虚拟访问路径(因为第2次请求是从浏览器端发送的)
5、请求转发只能在服务器内部转发,重定向可以定位任意资源(网络)

Cookie

Cookie在服务器端的体现,就是一个由键和值构成的对象,可以直接通过new来创建,但其实属于浏览器端技术,目的在于“跟踪浏览器状态”。

Cookie并不会自动产生,我们需要在服务器自己创建Cookie并存入值(第一次访问时),并响应给浏览器(加在响应头参数中),当同一浏览器下一次访问服务器时,才会自动传送Cookie(加在请求头参数中)

Cookie意味饼干,为了证明浏览器来过服务端,服务端给浏览器了一个小饼干,当浏览器下次再访问服务器,带着这个携带的小饼干,服务端就能成功识别。

效果如下:

@WebServlet(name = "Demo1Cookie", urlPatterns = "/cookie1")
public class Demo1Cookie extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        //创建Cookie对象
        Cookie cookie = new Cookie("username","123456");

        //向浏览器传输cookie对象
        response.addCookie(cookie);
    }
}
@WebServlet(name = "Demo2Cookie", urlPatterns = "/cookie2")
public class Demo2Cookie extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Demo2Cookie访问成功....");
        //获取cookie集合
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+":"+cookie.getValue());
        }
    }
}

第一次访问创建cookie并响应给浏览器

在这里插入图片描述

Cookie生命周期:

当浏览器被关闭,请求头里面的cookie就会被清除,服务器就接收不到我们上次操作的cookie了,这就是cookie默认生命周期。

setMaxAge(有效时间值)方法,可以设置cookie的有效时间,就算关闭电脑,cookie也会存在,时间一到,自动清除cookie。

设置有效时间的值:

  • 60*60是设置一个小时有效时间:
  • -1,是默认值,浏览器关闭自动清除
  • 0,通知浏览器立即清除cookie

Cookie有效路径:

cookie的路径如果一样,那么该路径下的所有资源、请求,都会将cookie传过去。

就像我们上面访问的两个路径:
/cookie1
/cookie2
都在/这一项目根路径下,使用不同的请求cookie3,4,5,6只要在同一路径下,都可以访问到。

我们可以通过setPath方法设置其有效路径,效果如下:

@WebServlet(name = "Demo1Cookie", urlPatterns = "/cookie1")
public class Demo1Cookie extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Demo1Cookie访问成功....");
        //创建Cookie对象
        Cookie cookie = new Cookie("username","123456");

        //设置cookie有效路径
        cookie.setPath("/web");

        //向浏览器传输cookie对象
        response.addCookie(cookie);
    }
}

在这里插入图片描述
在这里插入图片描述

Cookie处理中文乱码,空格异常

Cookie存入value时,对value进行编码处理:

URLEncoder.encoder(value,"utf-8");

Cookie取出value时,对value进行解码处理:

URLDecoder.decoder(value,"utf-8");

Session

Session与Cookie不同,是服务器端技术,全称HttpSession,也是一个域对象(一个会话范围内),每个session都有一个唯一的ID值,我们可以往这个session对象内存数据,取数据。

session既然是域对象,就一定会有getAttribute()和setAttribute()系列方法。

session的ID值会存入到响应头Cookie的JSESSIONID当中,响应给浏览器,当浏览器再次访问服务器时,请求头同样可以携带JSESSIONID传递给服务器,效果如下:

@WebServlet(name = "Demo4Session", urlPatterns = "/session4")
public class Demo4Session extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //当浏览器端第一次访问服务器时,服务器会为客户端创建一个session对象,然后把session对象放到session池中,通过cookie返回jsessionid给浏览器
        //当浏览器再次访问服务器,会在请求中带着自己的jsessionid,服务器根据该id找到对应的session对象,实现会话跟踪,session的数据就可被使用了
        HttpSession session = request.getSession();
        //获取session唯一ID值,同一会话内,接收到的sessionId一定想相同
        String sessionId = session.getId();
        session.setAttribute("username", "123456");
    }
}

@WebServlet(name = "Demo5Session", urlPatterns = "/session5")
public class Demo5Session extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取到session
        HttpSession session = request.getSession();

        String username = (String) session.getAttribute("username");
        System.out.println(username);
    }
}

在这里插入图片描述
在这里插入图片描述

HttpSession生命周期:

我们现在一共已经接触到了三个域对象,按照其生命周期做比较,是这样的:

HttpRequest(一次请求) < HttpSession(一次会话) < ServletContext(同服务器存在)

服务器会为每一个浏览器用户创建session对象,这些用户的浏览器不能共享同一个session对象。如果关闭浏览器,cookie会被清除,JSESSIONID也就不会存在了,就找不到session了,这时再访问时,会创建一个新的session。

关闭浏览器,会导致session对象重新创建,而且又有很多用户访问服务器,所以session对象会越来越多,因此服务器内session超过30分钟未操作,就会自动销毁(session超时)

我们也可以通过session:invalidate()手动销毁

或者在web.xml配置自动销毁时间:

<session-config>
	  <!--以分钟为单位,表示1分钟后失效-->
      <session-timeout>1</session-timeout>
</session-config>

Cookie和Session的区别:

Cookie是浏览端技术,将用户数据给用户浏览器端,浏览器保存数据;Session是服务器端技术,将用户数据给用户服务器端的HttpSession中,服务器端保存数据。

Cookie只能保存字符串,而Session可以保存对象。

Filter概述:

Filter就是用来拦截用户请求进行各种处理的,用户访问某个Servlet时,会先执行这个请求映射的Filter,执行业务逻辑合法后放行,不合法则不会执行请求对应的Servlet,项目中一般会创建多个过滤器,执行不同业务逻辑。

在这里插入图片描述

与Servlet一样,Filter程序需要实现对应Filter接口,web.xml也需要进行配置,代码如下:

public class Demo1Filter implements Filter{
    /**
     * 初始化方法
     * */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Demo1filter被初始化了");
        //FilterConfig:与ServletConfig的方法、作用基本相同,只不过对象是Filter
        String encode = filterConfig.getInitParameter("encode");
        System.out.println(encode);
    }
    /**
     *  放行方法
     * */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Demo1filter-doFilter前置代码执行");
        //该过滤器放行,如果不调用,则不会放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Demo1filter-doFilter后置代码执行");
    }
    /**
     * 销毁方法
     * */
    @Override
    public void destroy() {
        System.out.println("Demo1filter被销毁了");
    }
}
<?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_3_1.xsd"
         version="3.1">
    <servlet>
        <servlet-name>Demo1Jsp</servlet-name>
        <servlet-class>com.cx.jsp.Demo1Jsp</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Demo1Jsp</servlet-name>
        <url-pattern>/jsp1</url-pattern>
    </servlet-mapping>
    
    <filter>
        <filter-name>Demo1Filter</filter-name>
        <filter-class>com.cx.filter.Demo1Filter</filter-class>
        <init-param>
            <param-name>encode</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Demo1Filter</filter-name>
        <!--路径含义和servlet相同,此处所有请求都会被拦截-->
        <url-pattern>/ *</url-pattern>
    </filter-mapping>
</web-app>

Filter执行顺序:

按照web.xml的配置顺序执行,先执行前置代码,再执行后置代码。如果存在多个过滤器,根据web.xml的配置顺序执行前置代码,倒序执行后置代码,如下图:
在这里插入图片描述

Filter应用场景:

执行目标资源前做预处理工作,如设置编码,一般会直接放行。
通过条件判断是否放行:如校验用户是否已经登录,或者用户IP被禁用。
目标资源执行后,做一些后续工作,如把目标资源输出的数据进行处理。

Filter方法生命周期:

init():Tomcat服务器启动就会初始化过滤器,与Servelet的init方法不同
doFilter():每次被访问都会执行。
destory():Tomcat服务器关闭时销毁Fileter对象。

Listener概述:

Listener是用来监听各事件源(ServletContext/HttpSeesion/ServletRequest)被创建/销毁/进行操作时,来做一些业务逻辑。

由于Listener是对事件源做监听,与请求路径无关,所以在web.xml的配置中,可想而知,不需要配置Servlet和Filter都需要的mapping标签,这是需要注意的一点:

<listener>
        <!--只需要配置我们自定义的Listener类就可以-->
        <listener-class>com.cx.listener.Demo1Listener</listener-class>
</listener>

Listener事件源对应接口:

ServletContextListener、HttpSeesionListener、ServletRequestListener。

Listener事件源接口中方法(创建和销毁时执行):

ServletContextListener中的方法:contextCreated()、contextDestroyed()
HttpSeesionListener中的方法:seesionCreated()、seesionDestroyed()
ServletRequest中的方法:requestInitialized()、requestDestroyed();

JSP概述:

J(Java)S(Server)P(Page),字面翻译即Java服务页面。简单的讲,JSP文件在传统HTML文件中插入Java程序和JSP标记,是一种动态网页技术。

JSP真身:

JSP的真身是Servlet,在早期还没有JSP时,只能使用Servlet,我们需要手写大量的响应代码:response.getWriter().println(“<html>”);

JSP的出现,将逻辑与网页进行了分离,Servlet就不需要输出HTML代码了,可以直接转发给JSP文件。

JSP文件需要在Web服务器上,先进行编译,成为Servlet的字节码文件后,我们才能正常访问。

当浏览器请求JSP页面时(或者转发到JSP页面),服务器会查看JSP对应的Servlet是否存在,如果存在,直接调用其_jspService()方法处理请求;如果不存在,服务器会先把JSP编译成.java,再把.java文件编译成.class文件,然后调用_jspService()方法。当这个JSP页面第二次被访问时,就直接调用自身的_jspService()方法。

JSP的scriptlet:

所谓scriptlet,就是Java代码书写的代码块,在JSP中分为三种:

JSP脚本片段:<% java代码 %>
java代码可以直接在上面编写,可以定义变量,本页面也可以访问,部分类需要在JSP导包。

JSP表达式:<%= 变量名%>
可以访问java变量,并在JSP文件中直接展示。

JSP声明:<%! java代码 %>
与第一种不同的是,在这里定义的变量会成为真身Servlet类的成员变量存在,且在该代码块内无法使用内置对象。

三种不同的scriptlet,JSP声明是直接被编译到类中,而其他两种,会被编译到_jspService()中。

JSP的注释:

JSP只有一种注释:<%-- … --%>,注释中的内容会被JSP编译系统忽略,真身里没有注释内容。

一个简单的JSP文件:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>jsp_index</title>
  </head>
  <body>
    <!--html注释 浏览器后台有注释,真身会直接返回给浏览器,不建议在jsp代码中使用Html注释--!> 
    <%
      String name = "这是java代码,也可以通过jsp访问到页面";
      int age = 18;
      System.out.println("控制台输出:"+name+","+age);
      
      //java代码就是通过这样的形式,变为页面所展示的内容
      out.print("哈哈");
    %>

    <%--在此scriptlet写的代码为成员变量--%>
    <%!
      static String school = "nengyuan";
    %>

    <h3>name,age:<%=name+","+age%></h3>
  
  </body>
</html>

JSP编译文件:

在IDEA启动Tomcat后,我们可以去IDEA的.IntelliJIdea目录查找:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

JSP内置对象:

内置对象是在JSP页面中无需创建,就可以直接使用的变量。在JSP中一共有9个这样的对象:
out(JspWriter):最为常用的方法是print(),向页面输出,与response.getWriter()作用基本相同。

config(ServletConfig):如果JSP在web.xml中存在配置,而且存在初始化参数,那么可以使用config来获取,在JSP页面中基本没有什么用。

page(当前JSP的真身类型):可以理解为this,用处不大。

pageContext(PageContext):可以操作所有域对象,JSP域对象之一

exception(Throwable):只能在错误页中可以使用,用于跳转错误页面。

request(HttpServletRequest):与Servlet中的request一样,没有区别,JSP域对象之一

response(HttpServletResponse):与Servlet中的request一样,没有区别。

application(ServletContext):就是ServletContext对象,JSP域对象之一

session(HttpSession):就是HttpSession对象,JSP域对象之一

内置对象部分方法展示:

<%--
    jsp四个域对象:pageContext(当前页面有效)request(一次请求有效)session(一次会话有效,浏览器打开到关闭)application(服务器整个生命周期中)
    pageContext操作所有的域对象,可以通过findAttribute()方法来直接查找各个域对象内有没有相应的值(从最生命周期最短的开始查起,查到停止,未查到返回null)
    
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
<html>
<head>
    <title>域对象测试</title>
</head>
<body>
    <%
        //向pageContext内存内容
        pageContext.setAttribute("pkey","pvalues");
        request.setAttribute("rkey","requestValue");
        session.setAttribute("skey","sessionValue");
        application.setAttribute("akey","applicationValue");

    %>

    <h3>pageContext-pkey:<%=pageContext.getAttribute("pkey")%></h3>
    <h3>request-rkey:<%=request.getAttribute("rkey")%></h3>
    <h3>session-skey:<%=session.getAttribute("skey")%></h3>
    <h3>application-ckey:<%=application.getAttribute("akey")%></h3>
    <hr/>


    <%--通过多传参数,获取指定的作用域(如PAGE_SCOPE),就可以直接使用pageContext获取所有作用域的属性--%>
    <h3>pageContext-rkey:<%=pageContext.getAttribute("pkey",PageContext.PAGE_SCOPE)%></h3>
    <h3>request-rkey:<%=pageContext.getAttribute("rkey",PageContext.REQUEST_SCOPE)%></h3>
    <h3>session-skey:<%=pageContext.getAttribute("skey",PageContext.SESSION_SCOPE)%></h3>
    <h3>application-ckey:<%=pageContext.getAttribute("akey",PageContext.APPLICATION_SCOPE)%></h3>
    <hr/>


    <%--更简单的方法,从四个域对象去找对应参数,从最小作用域开始查,如果都没有,就返回空--%>
    <h3>pageContext-pkey:<%=pageContext.findAttribute("pkey")%></h3>
    <h3>request-rkey:<%=pageContext.findAttribute("rkey")%></h3>
    <h3>session-skey:<%=pageContext.findAttribute("skey")%></h3>
    <h3>application-ckey:<%=pageContext.findAttribute("akey")%></h3>

</body>
</html>

JSP指令(page、include、taglib):

JSP三大指令:page、include、taglib。指令在生成的Servlet类中不存在,但编译时需要使用这些指令,Tomcat编译系统,会根据JSP的指令信息来编译JSP,生成Java文件。

page:常用属性contentType,import,errorPage(错误跳转页面),isErrorPage(有此指令,可以使用excption对象)

<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
<%@ page import="java.util.Hashtable" %>

include:只有一个属性file,指定静态包含的页面(当页面A静态包含另一个页面B,在编译页面A时,会将页面B与页面A合并成一个文件,然后再编译)

<%@ include file="页面B.jsp"%>

taglib:在jsp页面中,使用第三方标签时,需要使用此指令导包。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

JSP常用动态标签(include和forward):

<%--动态包含--%>
<jsp:include page="页面B.jsp"></jsp:include>

<%--请求转发--%>
<jsp:forward page="页面C.jsp"></jsp:forward>

EL表达式:

EL(Expression Language)是一门表达式语言,它对应JSP的<%= … %>代码块,其格式为${}

EL表达式的使用:

<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Hashtable" %>

<%--
    EL表达式:使用时要求page指令的isELgnored属性为false(默认就为false),这样jsp在编译成.java时,才不会忽略EL表达式
    如果希望某个EL表达式被忽略,在${}前加上/
   
    使用EL表达式操作对象:常量、变量、集合(Map,List)、数组、对象

    EL表达式内置对象(隐藏对象):

    param:获取前台传递单个参数
    paramValues:获取前台传递多个参数
    header:获取请求头信息
    initParam:获取初始化参数
    cookie:获取cookie信息
    pageScope:获取pageContext的值
    requesScope:获取request的值
    seesionScope:获取seesion的值
    applicationScope:获取application的值

--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
    <title>EL表达式</title>
</head>
    <body>
    <%
        String name = "断舍离";

        List<String> list = new ArrayList<>();
        list.add("一");
        list.add("二");
        list.add("三");

        Map<String,String> map = new Hashtable<>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");

        int[] arr = {1,8,78,7};

        pageContext.setAttribute("name",name);
        pageContext.setAttribute("list",list);
        pageContext.setAttribute("arr",arr);
        pageContext.setAttribute("map",map);

        Cookie cookies = new Cookie("username","123456");
        response.addCookie(cookies);

        request.setAttribute("rkey","rvalues");
        session.setAttribute("skey","svalues");
        application.setAttribute("akey","avalues");

    %>
    <h1>EL表达式运算测试</h1>
    <p>\${1+1}</p>
    <p>${2-1}</p>
    <p>${2*3}</p>
    <p>${9/3}</p>
    <p>${9 div 3}</p>
    <p>${9%3}</p>
    <p>${9 mod 3}</p>
    <p>${9 == 3}  ${"a" eq "a"}</p>
    <p>${9 != 3}  ${"a" ne "a"}</p>
    <p>${9 > 3}  ${9 < 3}</p>
    <p>${9 lt 3}  ${9 gt 3}</p>
    <p>${9 >= 3}  ${9 <= 3}</p>
    <p>${9 >= 3 && 10 >= 3}  ${9 <= 3 and 8 <= 3}</p>
    <p>${ !(9 >= 33)}  ${not (9 <= 3) }</p>
    <p>${9 >= 3 || 10 >= 3}  ${9 <= 3 or 3 <= 8}</p>
    <p>${not empty ""} </p>
    <p>${empty null} </p>

    <h1>EL表达式操作对象</h1>
    <p>${name}</p>
    <p>${list[0]}</p>
    <p>${list[1]}</p>
    <p>${list[2]}</p>
    <p>${arr[0]}</p>
    <p>${arr[1]}</p>
    <p>${arr[2]}</p>
    <p>${arr[3]}</p>
    <p>${map.key1}</p>
    <p>${map['key1']}</p>

    <h1>EL表达式内置对象</h1>

    <%--参数隐藏对象--%>
    <p>${param.username}</p>

    <%--对应多个参数值时可以使用它--%>
    <p>${paramValues.hobby[0]}</p>
    <p>${paramValues.hobby[1]}</p>
    <p>${header.get("HOST")}</p>
    <p>${cookie.username.name}</p>
    <p>${cookie.username.value}</p>

    <%--requestScope.xxx与pageContext.getAttribute(xxx)效果一样,两者的区别在于,前者在数据不存在时返回空字符串,而后者返回null--%>
    <h1>EL表达式域内置对象</h1>
    <p>|${requestScope.rkey}|</p>
    <p>|${sessionScope.skey}|</p>
    <p>|${applicationScope.akey}|</p>

    <h1>EL表达式pageContext对象(也是内置对象)</h1>
    <p>${pageContext.request.queryString}</p>
    <p>${pageContext.request.requestURL}</p>
    <p>${pageContext.session.id}</p>
    <p>${pageContext.servletContext.serverInfo}</p>

    ${pageContext.session.maxInactiveInterval}
</html>

JSTL标签库:

JSTL(JSP Standard Tag Library),简称JSP标准标签库,提供了一系列的标签,供我们在JSP页面中使用,编写各种动态功能,弥补了HTML的不足。

开发中常用的标签库有3种:核心标签库、格式化标签库、函数标签库。

JSTL的使用:

<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%--
    JSTL核心标签库·:要想使用,用taglib指令导包c。
    <c:out value="">:表示在页面输出values内容
    <c:out value="" default="">:default属性表示该内容如果为空,那么则替换为其属性值
    <c:out value="" default="" escapeXml="">:escapeXml表示是否忽略xml的标签,默认为true忽略,如果设置为false可能会受到js攻击
    <c:set value="" var="" scope="">:存值到域对象中,scope为存储到哪个作用域内,默认为pageContext
    <c:if test="%{EL表达式}">:判断test属性(truefalse)是否成立,成立执行<c:if>执行语句</c:if>内标签
    <c:choose>:相当于多重if,里面为条件,和java判断语句同理
      <c:when test="">执行语句</c:when>
      <c:when test="">执行语句</c:when>
      <c:when test="">执行语句</c:when>
      <c:otherwise>
    </c:choose>
    <c:forEache begin="" end="" var=""></c:forEache>:begin:从哪开始,end:到哪结束,var:变量
    <c:forEache items="" var=""></c:forEache step="" varStatus="">: items:遍历的对象,var:变量 stemp"步数,一次循环后,跳过几步,varStatus循环状态

    JSTL格式化标签库:同样需要用taglib指令导包fmt,对于日期时间,数字等等我们需要格式化,
    <fmt:formatDate value="${}" pattern="" type=""> values:需要格式化的对象,pattern:自定义日期格式,type:库内格式,同java中SimpleDateFormat
    <fmt:formatNumber value="${}" pattern="" type="">

    JSTL函数库:taglib指令导包fn,包含了许多对字符串,集合对象的操作方法
    fn:方法名,函数库只能在EL表达式内使用
--%>

<html>
<head>
    <title>JSTL标签</title>
</head>
<body>
    <%--JSTL核心标签库--%>
    <%

        request.setAttribute("rkey","rvalues");
        request.setAttribute("socre",98);
        String[] arr = {"a","b","c"};
        request.setAttribute("arr",arr);
        List<String> list = new ArrayList<>();
        list.add("一");
        list.add("二");
        list.add("三");
        pageContext.setAttribute("list",list);
    %>

    <h1>c:out和c:set,输出与存储内容</h1>
    <c:out value="aaa"/><br/>
    <c:out value="${requestScope.rkey}"/><br/>
    <c:out value="${sessionScope.skey}" default="无内容"/><br/>
    <%--存值到域对象中,scope为存储到哪个作用域内,默认为pageContext--%>
    <c:set value="value2" var="skey2" scope="session"/>
    <c:out value="${sessionScope.skey2}" default="无内容"/><br/>

    <h1>c:if和c:choose,if与多重if</h1>
    <c:set value="58" var="socre"/>
    <%--判断request作用域的rkey值是否等于rvalues--%>
    <c:if test="${requestScope.rkey == 'rvalues'}">
        <c:out value="与aaa匹配成功"/>
    </c:if>

    <c:choose>
        <c:when test="${socre >=90}">
            A级
        </c:when>

        <c:when test="${socre >= 80}">
            B级
        </c:when>

        <c:when test="${socre >= 70}">
            C级
        </c:when>

        <c:otherwise>
            D级
        </c:otherwise>
    </c:choose>

    <h1>c:forEache</h1>
    <c:forEach begin="0" end="10" var="i">
        <c:out value="${i}"/>
    </c:forEach>

    <c:forEach items="${arr}" var="srt">
        <c:out value="${srt}"/>
    </c:forEach>

    <c:forEach items="${list}" var="l" varStatus="vs">
        <c:out value="${l}"/>
        <c:out value="${vs.begin}"/>
        <c:out value="${vs.end}"/>
        <%--1开始数--%>
        <c:out value="${vs.count}"/>
        <%--0开始数--%>
        <c:out value="${vs.index}"/>
        <%--是否为第一个元素--%>
        <c:out value="${vs.first}"/>
        <%--是否为最后一个元素--%>
        <c:out value="${vs.last}"/>
        <%--当前对象toString方法--%>
        <c:out value="${vs.current}"/>
    </c:forEach>

    <%--JSTL格式化标签库--%>
    <%
        Date date1 = new Date();
        Double num1 = 12345.4567;
        pageContext.setAttribute("date1",date1);
        pageContext.setAttribute("num1",num1);

    %>

    <h1>日期格式化</h1>
    <p>日期格式化 (1): <fmt:formatDate type="time" value="${date1}" /></p>
    <p>日期格式化 (2): <fmt:formatDate type="date" value="${date1}" /></p>
    <p>日期格式化 (3): <fmt:formatDate type="both" value="${date1}" /></p>
    <p>日期格式化 (4): <fmt:formatDate type="both" dateStyle="short" timeStyle="short" value="${date1}" /></p>
    <p>日期格式化 (5): <fmt:formatDate type="both" dateStyle="medium" timeStyle="medium" value="${date1}" /></p>
    <p>日期格式化 (6): <fmt:formatDate type="both" dateStyle="long" timeStyle="long" value="${date1}" /></p>
    <p>日期格式化 (7): <fmt:formatDate pattern="yyyy-MM-dd" value="${date1}" /></p>

    <h1>数字格式化</h1>
    <p>日期格式化 (1): <fmt:formatNumber type="currency" value="${num1}" /> </p>
    <p>格式化数字 (2): <fmt:formatNumber type="number" maxIntegerDigits="3" value="${num1}" /></p>
    <p>格式化数字 (3): <fmt:formatNumber type="number" maxFractionDigits="3" value="${num1}" /></p>
    <p>格式化数字 (4): <fmt:formatNumber type="number" groupingUsed="false" value="${num1}" /></p>
    <p>格式化数字 (5): <fmt:formatNumber type="percent" maxIntegerDigits="3" value="${num1}" /></p>
    <p>格式化数字 (6): <fmt:formatNumber type="percent" minFractionDigits="10" value="${num1}" /></p>
    <p>格式化数字 (7): <fmt:formatNumber type="percent" maxIntegerDigits="3" value="${num1}" /></p>
    <p>格式化数字 (8): <fmt:formatNumber type="number" value="${num1}" pattern="0.00‰"/></p>
    <p>格式化数字 (9): <fmt:formatNumber type="number" value="${num1}" pattern="0.00%"/></p>
    <p>美元 :
        <fmt:setLocale value="en_US"/>
        <fmt:formatNumber value="${num1}" type="currency"/></p>

    <%--JSTL函数库--%>
    <%
        String[] strs = {"a", "b", "c"};
        List list1 = new ArrayList();
        list.add("a");
        pageContext.setAttribute("arr", strs);
        pageContext.setAttribute("list", list);
    %>


    <h1>JSTL函数库用法,只能在EL里使用</h1>
    ${fn:length(arr)}
    ${fn:toLowerCase("Hello") }<br/><!-- hello -->
    ${fn:toUpperCase("Hello") }<br/><!-- HELLO -->
    ${fn:contains("abc", "a")}<br/><!-- true -->
    ${fn:containsIgnoreCase("abc", "Ab")}<br/><!-- true -->
    ${fn:contains(arr, "a")}<br/><!-- true -->
    ${fn:containsIgnoreCase(list, "A")}<br/><!-- true -->
    ${fn:endsWith("Hello.java", ".java")}<br/><!-- true -->
    ${fn:startsWith("Hello.java", "Hell")}<br/><!-- true -->
    ${fn:indexOf("Hello-World", "-")}<br/><!-- 5 -->
    ${fn:join(arr, ";")}<br/><!-- a;b;c -->
    ${fn:replace("Hello-World", "-", "+")}<br/><!-- Hello+World -->
    ${fn:join(fn:split("a;b;c;", ";"), "-")}<br/><!-- a-b-c -->

    ${fn:substring("0123456789", 6, 9)}<br/><!-- 678 -->
    ${fn:substring("0123456789", 5, -1)}<br/><!-- 56789 -->
    ${fn:substringAfter("Hello-World", "-")}<br/><!-- World -->
    ${fn:substringBefore("Hello-World", "-")}<br/><!-- Hello -->
    ${fn:trim("     a b c     ")}<br/><!-- a b c -->
    ${fn:escapeXml("<html></html>")}<br/><!-- <html></html> -->

</body>

</body>
</html>
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值