Servlet
//是一个接口、规范,其相应的类由Tomcat服务器实现
//GenericServlet HttpServlet 是其实现类
Servlet的执行过程
1. 在浏览器上输入http://localhost/firstservlet/first
2. 首先被监听80端口号的connector HTTP 1.1 接收,并生成request对象和一个空的response对象,之后将请求转发给Engine处理。
3. engine根据host选择虚拟主机host
4. 在host节点下去搜寻Context节点,应用名
5. 去web.xml中搜索/first,通过url-pattern找到servlet-name,找到相应的class文件,加载
6. 检查是否有servlet实例化对象,若没有就通过反射来创建实例化对象,并执行init方法初始化
7. 直接调用servlet的service方法并将创建的request和response对象传进去
8. 执行service方法
9. WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
实现Servlet的方式
//继承GenelicServlet
实现其抽象方法server
修改web.xml文件,添加
<servlet>
<servlet-name/class>
</servlet>
<servlet-mapping>
<servlet-name>
<url-pattern>
</servlet-mapping>
//继承HttpServlet
重写其doGet/doPost方法
修改web.xml文件
//使用注解
在idea的新建中的最后面找到creat new servlet
出现:@WebServlet(name="ThirdServlet",url-pattern="/third")
Name对应web.xml中的servlet-name节点,urlPatterns对应url-pattern节点。
可以进一步精简为@WebServlet("/third")
idea与Tomcat的关联
idea里面的目录是开发目录,他将tomcat里的conf复制了一份,不会对tomcat产生影响
最终部署目录,在最终部署应用的时候会将开发目录的web目录下的所有文件都复制到部署目录中
若开发目录的web下未设置classes文件夹,他也会自动在部署目录生成一个文件夹保存.class文件
servlet的生命周期
正常情况下,servlet在第一次访问的时候被tomcat创建
可以通过设置Load-on-startup的参数为一个非负数,让servlet在项目加载的时候自动创建。
Load-on-startup默认-1,表示初次访问的时候才会加载。数字大小表示加载的先后顺序,越小则越先加载。
在web.xml中创建
<servlet>
<load-on-startup>1</load-on-startup>
</servlet>
###url映射
一个servlet可以配置多个url,
多个servlet不可以配置同一个url
url冲突匹配
url-pattern只能以/或*开头
/开头的优先级高于*开头的
匹配程度越高优先级越高
存在/ * jsp html 都无法访问,被/ *拦截了
存在/,不存在/ *时,可以访问jsp无法访问html//因为系统会自动执行自己定义的/,将系统默认的/覆盖
访问静态资源的时候,有servlet参与
//在开发过程中,不要设置/*和/的url-pattern。
/的优先级最低,当匹配不到资源的时候访问/,/一般定义的是一些404页面之类的处理
ServletConfig
可以在init方法中获取该servlet定义的一些初始化参数
ServletContext对象
获取EE项目的文件路径
getServletContext().getRealPath("1.html")//真实路径
//获取ee项目的文件路径
在哪个目录下调用jvm,就相对的是哪个路径
用Main方法调用jvm
共享对象
setAttribute() getAttribute()
removeAttribute()
//可以被同应用下的不同servlet访问
ServletContext servletContext = getServletContext();//活得context对象
servletContext.setAttribute("name","zhangsan");//设置context的值
String name = (String)servletContext.getAttribute("name");//获得值
servletContext.removeAttribute("name");//删除name属性
//使用共享对象来统计网页访问量
@WebServlet("/history")
public class HistoryCountServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
//首先应该想一下用什么来存
//首先是set还是get
Integer count = null;
synchronized (this){
count = (Integer) servletContext.getAttribute("count");
//条件判断 空指针异常
//count.intValue() 这步才会空指针异常
if(count == null){
//没有代表第一次访问
servletContext.setAttribute("count",new Integer(1));
}else{
servletContext.setAttribute("count",count + 1);
}
}
count = (Integer) servletContext.getAttribute("count");
response.getWriter().println("history count :" + count);
}
}
获取全局性的初始化参数
//所有的servlet均可以获取到该初始化参数
在web.xml中定义
<context-param>
<param-name>enconding</param-name>
<param-value>utf-8</param-value>
</context-param>
ServletContext servletContext = getServletContext();
String name = servletContext.getInitParameter("name");
//ServletConfig初始化参数的标签叫init-param,
这个标签是存在于servlet内部的,
所以仅当前servlet可以获取到该初始化参数。
request
request获取客户机信息api
getQueryString 方法返回请求行中的参数部分,不能返回正文的参数
getRequestURL//方法返回客户端发出请求时的完整URL。
getRequestURI//方法返回请求行中的资源名部分。
getRemoteAddr//方法返回发出请求的客户机的IP地址
getRemoteHost//方法返回发出请求的客户机的完整主机名
getRemotePort//方法返回客户机所使用的网络端口号
getLocalAddr//方法返回WEB服务器的IP地址。
getLocalName//方法返回WEB服务器的主机名
getMethod//得到客户机请求方式
获取客户机请求头
getHeader(name)//name是请求头前面的key,可以是任意的
getHeaderNames//返回一个包含请求头的key的一个数组
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
System.out.println(headerNames.nextElement());
}
获取客户机提交的数据
getParameter(name)//获取名为name的属性的值
getParameterValues(String name)//获取某个属性的多个值
getParameterNames()//获取所有提交数据的属性名,返回一个数组
将客户端发送的数据存到bean中
//利用反射
工具包//beanUtils
使用:
Map<String, String[]> parameterMap = request.getParameterMap();
BeanUtils.populate(user, parameterMap);
中文乱码问题
post: request.setCharacterEncoding("utf-8");//设置服务器的解码方式
get: 若浏览器提交的是utf-8 编码的就不会乱码
地址路径写法
分为:以/开头和不以/开头两种
对以/开头来说,要看执行主体是服务器还是浏览器
若是浏览器,因为浏览器不知道应用名所以,/应用名/资源名
若是服务器,/资源名
请求转发和包含
//一种在服务器内部的资源跳转方式
1.步骤:
//通过request对象获取请求转发器对象:
RequestDispatcher dispatcher = request.getRequestDispatcher(String path);
//使用RequestDispatcher对象来进行转发:
dispatcher.forward(request,response);
//包含
dispatcher.include(request,response);
2.特点:
浏览器地址栏路径不发生变化
只能转发到当前服务器内部资源中。
转发是一次请求
源组件和目标组件共享同一个request对象和response对象。
3.包含特点
源组件与被包含的目标组件的输出数据都会被添加到响应结果中。
在目标组件中对响应状态代码或者响应头所做的修改都会被忽略。
4.源组件与目标组件
转发:(源组件)留头(响应头)不留体(响应体)。
//源组件做了初步处理之后,将请求转发给目标组件,
//之后源组件对于响应正文的数据不再参与进来,全权由目标组件来完成。但是它可以写入响应头信息。
包含:(源组件)留头(响应头)也留体(响应体)。
//主要用在一个页面引用另外一个或者多个页面。
5.用途
转发:一般用在servlet组件完成相应的逻辑之后,跳转至相关的页面,
转发的源组件一般仅会处理一些响应头,响应正文由目标组件来提供,执行的侧重点在目标组件。
包含:一般用在页面和页面的相互引用方面,比如一个页面可以在底部引入一个声明页。
这个时候就可以用包含。源组件均可响应体以及响应头。执行的侧重点仍然在源组件。
6.转发包含两个组件的执行过程
执行到forward/include行代码,执行完毕,
跳转至目标组件执行,目标组件代码执行完之后,
再次回到源组件继续执行forward/include行后面的代码。
request域
Context域范围:整个web应用下的资源都可以访问到
Request域范围:针对同一个request对象内有效,所有共享同一个request对象的组件,都可以共享request域。
转发的源组件和目标组件共享request域
方法:
void setAttribute(String name,Object obj):存储数据
Object getAttitude(String name):通过键获取值
void removeAttribute(String name):通过键移除键值对
Response对象
功能:设置响应消息
结构
响应行:版本协议 状态码 描述原因
响应头
空行
响应体
设置响应行
1.格式:
HTTP/1.1 200 ok
2.设置状态码:
response.setStatus(int sc);
设置响应头:
setHeader(String name, String value)
设置响应体:
使用步骤:
1.获取输出流
字符输出流:PrintWriter getWriter()
字节输出流:ServletOutputStream getOutputStream()
2.使用输出流,将数据输出到客户端浏览器
输出中文乱码问题
1.
浏览器端,在window中文简体下,默认是GBK编码格式
服务器端,可以设置服务器的编码为response.setCharacterEncoding("GBK");来解决
//但这种方法不具有普遍性
2.
//告诉浏览器服务器采用什么编码方式
response.setContentType("text/html;charset=utf-8");
respons.setHeader("content-type","text/html;charset=utf-8");
两层意思:
规范服务端的数据采用utf-8 进行编码
告诉浏览器我的编码方式是什么
3.jsp方式
字节流传输文件和中文
//字符输出流:默认编码是GBK
response.setContentType("text/html;charset=utf-8");
String realPath = getServletContext().getRealPath("2.png");
FileInputStream fin = new FileInputStream(new File(realPath));
ServletOutputStream out = respons.getOutputStream();
byte[] bytes = new byte[1024];
int len=0;
while((len=fin.read(bytes))!=-1){
out.write(bytes,0,len);
out.write("中文".getBytes(utf-8));
}
fin.close();
设置响应头定时刷新
response.setHeader("refresh","2;url=path");//可不写url表示当前页面自动刷新
//特点
地址栏发生变化
可以访问其他站点(服务器)的资源
是两次请求。不能使用request对象来共享数据
重定向
1.
response.sendRedirect(request.getContextPath()+"/应用名/资源名");
2.设置响应码,加location头
response.setStatus(302);
response.setHeader("Location",request.getContextPath()+"/应用名/资源名");
3.重定向的特点:redirect
地址栏发生变化
重定向可以访问其他站点(服务器)的资源
重定向是两次请求。不能使用request对象来共享数据
获取路径
request.getServletContext().getRealPath(path);//获取Web项目的全路径
request.getServletPath() //得到当前页面所在目录下全名称
request.getRequestURI()//得到资源的相对地址