Servlet API有以下4个Java包:
javax.servlet:其中包含定义Servlet和Servlet容器 之间的锲约类和接口
javax.servlet.http:其中包含定义HTTP Servlet和Servlet容器 之间的契约类和接口
javax.servlet.annotation:其中包含标注Servlet,Filter,Listener的标注 ,它还为被标注元件定义元数据
Servlet简介
Servlet的主要功能在于交互式的浏览和修改数据,生成动态的web内容,Servlet具有更高的效率,功能强大,具有更好的可移植性。
Servlet架构
浏览器发送http协议请求,Servlet程序中通过一系列ServletAPI读取http协议,对浏览器的请求进行处理,通过访问服务器和文件,拿到数据后发送对浏览的响应。Servlet程序会放在一个web服务器(容器)中。
servlet中主要执行以下操作
1.获取前台页面传递过来的参数数据
2 调用service层,调用DAO(Data Acess Object)层
3.将数据存储到域
4.跳转到前台页面
Servlet是一个单实例多线程的,在内存中只会创建一次servlet对象,存在多线程的安全问题
Servlet实现
注解出现在JDK5以后。
Servlet是一个接口,定义服务端程序的实现方法,它是所有Servlet类必须直接或间接实现的接口。Servlet实现类与接口的关系如下:
在Java中,利用web容器,例如tomcat来处理http协议。
httpServlet中的doget()方法的实现内容:首先会调用http协议,判断处理的协议版本,如果处理的http协议版本是1.1,则返回响应为405状态码错误(这样做是提醒编程人员需要自己实现get方法的内容,调用父类的doget()方法就会报错);如果处理的http协议版本是1.0,则返回响应为400状态码的错误.
httpServlet中的service()方法的实现内容:判断httpServlet中的请求是否是doGet()/doPost()/doPut()/doDelete()等方法,然后将请求转发到相应的方法实现中,由于子类继承了httpServlet,因此最终调用的是子类中对各种方法的实现逻辑。
Servlet方法
init,service,destroy 是Servlet的生命周期方法.
- init:当service容器第一次被请求时,Servlet容器会调用该方法,后续请求不会被继续调用。
- service:每当请求Servlet时,Servlet容器就会调用这个方法。(可多次调用)
- destroy:当要销毁Servlet时,Servlet容器就会调用该方法。(一次调用)
线程安全问题:一个Servlet类只有一个Servlet实例,Servlet实例会被一个应用程序中的所有用户共享,如果出现多个浏览器客户端访问Servlet,那么这些用户都会访问同一个实例,可能会造成结果混乱,因此在Servlet中尽量不使用类的成员变量。
ServletConfig
当当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init方法传入一个ServletConfig。ServletConfig封装可以通过部署描述符(需要在web.xml文件中配置init-param属性值)传给Servlet的配置信息。这样传入的每一条信息就叫做初始化参数,一个初始化参数有key和value组成。
<init-param>
<param-name>logFileName</param-name>
<param-value>filter.logging</param-value>
</init-param>
为了从Servlet内部获取到初始化参数的值,要在Servlet容器传给Servlet的init方法的ServletConfig中调用getInitParameter方法即可。
ServletContext
ServletContext表示Servlet应用程序。每个Web应用程序只有一个上下文,在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象,就可以共享从应用程序中的所有资料处访问到的信息,并且可以动态注册Web对象,共享信息可以将对象保存在ServletContext中的一个内部Map中,保存在ServletContext中的对象被称作属性
通过ServletConfig中调用getServletContext方法,可以获得ServletContext。
HttpServletRequestAPI
获取请求方法: String method=req.getMethod();
获取请求uri: req.getRequestURI();
请求的协议版本: req.getProtocol();
获取指定的请求头信息: req.getHeader("host");
获取user-agent请求头 :req.getHeader("user-agent");
会返回字符串,包含浏览器类型信息
Trisent:IE浏览器
Chrome:谷歌浏览器
firefox:火狐浏览器
以get方式提交请求时获取参数 数据:String queryString=req.getQueryString();
get方式会将请求信息放在地址栏中,get方法中的内容是有限制的。因此get方法一般用于数据查询。
以post方式提交请求时获取参数数据:
InputStream in= req.getInputStream();
byte[] bytes=new byte[1024];
int len=in.read(bytes);
while(len!=-1){
System.out.println(new String(bytes,1,len));
}
post方式是以二进制字节数据向服务器发送请求的
get方式与post方式通用的获取参数的方式一:
通过表单的name参数值获取表单内容,因此html中的表单一定要定义表单属性
获取所有参数值:Enumeration<String> enums= req.getParameterNames();
get方式与post方式通用的获取参数的方式二:
通过指定参数名称获取参数值:req.getParameter("name");
get方式与post方式通用的获取参数的方式三:
获取所有参数对象,返回值为map集合,key值为参数对象(String接收),value为参数值(参数值可以为多个,如表单为复选框的情况,用String[]):
Map<String,String[]> paraMap= req.getParameterMap();
get方式与post方式通用的获取参数的方式四:
通过参数名,获取多个参数值:String[] habits= req.getParameterValues("habit");
tomcat8.0以下的版本,利用get()方法提交时:遇见参数为中文,需要手动解码,因为在表单中的信息是以UTF-8编码的,tomcat服务器是以iso-8859-1的方式编码的,若不进行解码会出现乱码的情况
手动解码:String name= req.getParameter(“name”);
name=new String(name.getBytes(“iso-8859-1”),“utf-8”);
使用post()方式提交请求时,解码的方法为: req.setCharacterEncoding(“utf-8”);
一般为了防止冲突,使用这些通用方法获取参数时会在doPost()方法中复用doGet()方法(获取参数的方法一般写在doget()中)
浏览器的编码可能与程序的编码不同,所以一般要将从浏览器获取的信息进行编码转化。
String name=req.getParameter("name");
new String(name.getBytes("ISO-8859-1"),"utf-8");
HttpServletResponseAPI
设置编码: resp.setContentType("text/html;charset=utf-8");
向浏览器输出内容: resp.getWriter().write("hello,这是我的第一个Servlet");
使用字节流的方式向浏览器输出内容(图片文件,音频视频)response.getOutputStream().write("hello,response".getBytes());
设置响应头:setHeader("name","val)
设置响应的状态码: response.setStatus(404);
访问不到servlet:
200 响应成功
500 服务器有问题(出现的各种异常)
404 一般情况:路径有问题 (url-pattern:路径对不上) :404状态+404错误页面
406 :异步请求出现的问题
400 项目下找不到资源
302 :进步一步请求 +location响应头 :
重定向
//重定向原理
// 设置状态302
resp.setStatus(302);
//设置location响应头
resp.setHeader("location","adv.html");
当浏览器输入url向服务器发送请求时,服务器创建Servlet对象,调用了doGet()方法后,读到了302状态码,就会携带响应头地址resp.setHeader("location","adv.html");
响应回给浏览器,浏览器会进一步访问,向服务器请求adv.html地址;所以一共有两个request对象,且不相等,因为两个请求携带的数据不同。
在实际应用中其实没有这么麻烦,在tomcat中配置web applecation的上下文路径(工程名称),就可以直接跳转:resp.sendRedirect("/项目名称/资源文件名称");
重定向的地址栏会发生变化,跳转到指定的页面上。
请求转发: req.getRequestDispatcher("adv.html").forward(req,resp);
请求转发还是会处在请求的地址栏,是服务器的行为,request对象只有一个
设置定时刷新:
resp.setHeader("Refresh","2");
设置编码格式:resp.setContentType("TEXT/HTML;charset=utf-8");
以指定方式打开资源:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
File file=new File("");
//以下载方式打开资源
resp.setHeader("Content-Disposition","attachment;filename="+file.getName());
//下载文件
//创建字节流输入对象
InputStream in=new FileInputStream(file);
//获取字节输出流对象
OutputStream out=resp.getOutputStream();
//边读边写
byte[] buff=new byte[1024];
int len=0;
while((len=in.read(buff))!=-1){
out.write(buff,0,len);
}
in.close();
out.close();
}
(面)Servlet生命周期
Servlet的执行过程:
- http://localhost:8080/hello-->
在网站配置web.xml文件中中找是否存在一个映射url-pattern: /hello - 如果找到,去servlet-maping中的servlet-name:找到对应的类名称HelloServlet,再在**配置信息(servlet标签)**中找是否存在同名的一个HelloServlet,
- 访问Servlet-class---->通过反射获取到HelloServlet.class 类对象 (重点)
反射机制:
创建类对象
1.Class classz=Class.forName("com.github.sweeeeeet.tomcat.HelloServlet");
2.通过数据类型的class属性获取class对象String.class
3.Object类中getClass()方法获取Constructor 对象
Constructor con=classz.getDeclaredConstructor("构造函数的参数类型");
获取实例
Object obj=con.newInstance();
加载具体方法
Method m1=classz.getDeclaredMethod("init",ServletConfig.class);
m1.invoke(obj,config)
- 反射机制获取到成员方法---->tomcat解析里面内容
servlet初始化
servlet配置信息,初始化参数,或servlet加载机制
public void init(ServletConfig config)
简易初始化方法(用户自定义初始化信息时使用)
public void init()
servlet执行请求发送响应
tomcat服务器获取到浏览器的请求数据,会将请求数据封装到HttpServletRequset对象中,tomcat调用service方法,业务具体覆盖doGet()方法或doPost()方法(开发者做的事情)
业务服务
protected void service
(一般不会覆盖service 方法,会将逻辑写在doGet()或doPost()方法中)
-
使用HttpServletResponse响应给用户内容
-
销毁
public void destroy()
销毁的几种情况
System.exit(0);
jvm停止运行
ServletConfig对象API
ServletConfig是servlet配置对象,在处理逻辑doGet()/doPost()方法中,若需要频繁改动处理逻辑,可以将其放入到servlet配置信息中作为初始化参数,一个servlet 中可以使用多个ServletConfig对象,只需要在web.xml文件中配置init-param,在servlet标签中可以包含多个init-param
<init-param>
<param-name>path</param-name>
<param-value></param-value>
</init-param>
同时还可以设置servlet的加载时机:<load-on-startup>2</load-on-startup>
- 数值越大,优先级越低
获取ServletConfig对象:ServletConfig con=this.getServletConfig()
通过参数获取ServletConfig对象的值:String path=fig.getInitParameter("path");
ServletContext对象API
作用:
1.获取上下文路径
ServletContext context=this.getServletContext();
2.可以请求转发(跳转页面)
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher rd= context.getRequestDispatcher("/hello.html");
rd.forward(req,resp);
//简写方式
//req.getRequestDispatcher("hello.html").forward(req,resp);
}
请求转发和重定向的区别:
1.转发请求地址不会发生变化,重定向本质上是进一步请求,因此地址会发生变化
2.请求转发在业务中跳转页面,需要用到域对象(request域);重定向无法从request域中获取数据
3.内存角度:请求转发始终只有一个request对象;重定向会产生两个不同的request对象
3.作为一个域对象使用
域对象:共享数据,在不同的jsp/servlet之间保存数据,获取数据
常见的域有:
PageContext(jsp中的域对象,但是仅限于当前页面的jsp)—pageScope对象
HttpServletRequest—requestScope对象
HttpSession—sessionScope对象
ServletContext—applicationScope对象(最大的一个域)
在一个servlet中设置数据:context.setAttribute(String var1, Object var2);
在另一个servlet中获取数据:context.getAttribute(String s)
4.获取真实路径
ServletContext context=getServletContext();
context.getRealPath("a.txt");
//其他获取路径的方法
PathDemo.class.getClassLoader().getResource("a.txt");
5.获取全局参数
1.String con = context1.getInitParameter("name");
2.Enumeration<String> s= context1.getAttributeNames();
应用:
表单提交:
public class FormServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer=resp.getWriter();
writer.append("<html>")
.append("<head>")
.append("<title>")
.append("登录")
.append("</title>")
.append("</head>")
.append("<body>")
.append("<form action='handlerServlet' method='post'>")
.append("username:<input type='text' name='username'/></br>")
.append("password:<input type='password' name='password'/></br>")
.append("<input type='submit' value='submit'>" )
.append("<input type='reset' value='reset'>")
.append("</form>")
.append("</body>")
.append("</html>");
}
}
配置XML文件
<servlet>
<servlet-name>FormServlet</servlet-name>
<servlet-class>com.sweet.github.servlet.httpservlett.FormServlet</servlet-class>
</servlet>
<!-- Servlet的映射关系,告诉tomcat容器,当用户请求一个地址的时候,使用该Servlet处理 -->
<servlet-mapping>
<servlet-name>FormServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
文件上传:
public class UpoadFile extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Part part=req.getPart("filename");
InputStream is= part.getInputStream();
String appUploadPath = req.getServletContext().getRealPath("/upload");
File file=new File(appUploadPath,part.getSubmittedFileName());
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
OutputStream out=new FileOutputStream(file);
byte[] buff=new byte[1024];
int len=-1;
while ((len=is.read(buff))!=-1){
out.write(buff,0,len);
}
out.close();
is.close();
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer=resp.getWriter();
writer.append("<html>")
.append("<head>")
.append("<meta charset='UTF-8'>")
.append("<title>File</title>")
.append("</head>")
.append("<body>")
.append("<a href='>")
.append("/upload/").append(part.getSubmittedFileName())
.append("'>")
.append("上传的文件")
.append("</a>")
.append("</body>")
.append("</html>");
}
}
配置xml文件
<servlet>
<servlet-name>UpoadFile</servlet-name>
<servlet-class>com.sweet.github.servlet.httpservlett.UpoadFile</servlet-class>
<multipart-config>
<location>D:/upload</location>
<max-file-size>1048576</max-file-size>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>UpoadFile</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>