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创建对象。数值越小(0,1,2,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-mapping中servlet-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对象:
request和response就是请求和响应对象,全称分别是HttpServletRequest和HttpServletResponse,是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默认生命周期。
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属性(true或false)是否成立,成立执行<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>