什么是Servlet?
Servlet有什么用?
Idea写一个Servlet程序
一、新建一个类
我新建了一个HelloServlet类,要继承一个servlet接口 javax.servlet.Servlet,但是你是打不出来的,原因是没有包,看第二步导入包
然后就可以继承接口方法了,在service方法里面写一个输出语句吧
package main.com.vae.servletDemo; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; public class HelloServlet implements javax.servlet.Servlet{ public void init(ServletConfig servletConfig) throws ServletException { } public ServletConfig getServletConfig() { return null; } public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("许嵩小名许甜甜"); } public String getServletInfo() { return null; } public void destroy() { } }
二、导入servlet的jar包
你的Tomcat的lib文件夹里面有一个servlet的jar包,复制到WEB-INF下的lib文件夹内,如图
三、修改web.xml
打开WEB-INF下的web.xml,加上这几个
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>main.com.vae.servletDemo.HelloServlet</servlet-class> </servlet> <!--向外暴露该Servlet类的一个资源名称,供外键直接访问,资源名称必须以 / 打头--> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
四、你的webapp的路径,在Tomcat里面修改
如果你不修改Tomcat的server.xml里面的Context标签,那么你的Tomcat打开后出现的是官方的Tomcat猫界面,我们修改了context之后,如下
五、重启Tomcat
输入localhost/hello (备注,我的端口号改为80了,你们默认的是8080),查看idea的控制台
Servlet的生命周期
Servlet的请求流程
ServletConfig接口初始化参数
我写一个类,继承Servlet接口,想在service方法里面写一个判断,判断name是许嵩,就输出是许嵩,否则输出蜀云泉真帅
这个name变量,我如果在类里面写死的话,那么我想改只能改源码。所以写在配置文件web.xml里面
先看看配置文件web.xml
<servlet> <servlet-name>InitParamServlet</servlet-name> <servlet-class>main.com.vae.servletDemo.InitParamServlet</servlet-class> <init-param> <param-name>name</param-name> <param-value>许嵩</param-value> </init-param> </servlet> <!--向外暴露该Servlet类的一个资源名称,供外键直接访问,资源名称必须以 / 打头--> <servlet-mapping> <servlet-name>InitParamServlet</servlet-name> <url-pattern>/init</url-pattern> </servlet-mapping>
和上面大差不差,只不过多了一个参数的<init-param>,里面是参数名和参数值。再看看类里面怎么获取的
package main.com.vae.servletDemo; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; import java.util.Enumeration; public class InitParamServlet implements javax.servlet.Servlet{ private ServletConfig config; public void init(ServletConfig servletConfig) throws ServletException { config=servletConfig; } public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String name=config.getInitParameter("name"); System.out.println(name); if ("许嵩".equals(name)) { System.out.println("是许嵩"); } else{ System.out.println("蜀云泉最帅"); } //获取所有的参数的名称和值 Enumeration<String> en=config.getInitParameterNames(); while (en.hasMoreElements()) { String paramName=en.nextElement(); System.out.println(paramName + "," + config.getInitParameter(paramName)); } } public ServletConfig getServletConfig() { return null; } public String getServletInfo() { return null; } public void destroy() { } }
HttpServletRequest接口常用的方法
我的类是这样的
package main.com.vae.day1; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.Enumeration; import java.util.Map; public class HttpServletRequestDemo extends HttpServlet { @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(req.getMethod()); //返回请求的方式:如Get/Post System.out.println(req.getRequestURI());//返回类型String,返回请求中的资源名字部分:如/req System.out.println(req.getRequestURL());//返回类型StringBuffer,返回请求浏览器地址栏中所有的信息 System.out.println(req.getContextPath());//返回当前项目的上下文路径 System.out.println(req.getRemoteAddr()); //返回请求客户机的IP地址 String userAgent = req.getHeader("User-Agent"); //返回指定名称的请求头的值 System.out.println(userAgent.contains("MSIE")); System.out.println(userAgent); System.out.println(req.getParameter("username"));//返回指定参数名的值 String [] hobbys = req.getParameterValues("hobby"); System.out.println(Arrays.toString(hobbys)); //返回指定参数名的多个值 Enumeration<String> names=req.getParameterNames(); //返回所有参数名的Enumeration对象 while (names.hasMoreElements()) { System.out.println("--->" + names.nextElement()); } Map<String,String[]> paramMap=req.getParameterMap();//返回所有参数名的Map对象 System.out.println(paramMap); } }
浏览器这样输入
http://localhost:8080/req?username=vae&hobby=java&hobby=girl&age=23
结果
HttpServletResponse接口常用的方法
package main.com.vae.response; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @WebServlet("/resp") public class HttpServletResponseDemo extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置输出数据的MIME类型为html的,目的是输出的要以网页的格式展示 //resp.setContentType("text/html"); //设置编码格式为中文 //resp.setCharacterEncoding("UTF-8"); //上面的两行是必须写的,但是可以精简成一行,如下 resp.setContentType("text/html;charset=utf-8"); PrintWriter out=resp.getWriter(); //在浏览器中输出一些内容 out.println("Vae"); out.println("许嵩"); out.println("蜀云泉"); } }
servlet第一个页面,注册页面
在webapp下面新建一个HTML文件,叫register.html,代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>注册页面</h3> <form action="/regi" method="post"> 账号:<input type="text" name="userName" value="默认值" required><br/> <!--required是Html5的新特性,在以前必填字段我们需要通过js来判断,现在html5实现!--> 密码:<input type="password" name="passWord"><br/> 性别:<input type="radio" name="sex" value="boy" checked="checked"/>男 <input type="radio" name="sex" value="girl"/>女 <input type="radio" name="sex" value="none"/>保密<br/> 爱好: <input type="checkbox" name="hobby" value="Vae" checked="checked">许嵩 <input type="checkbox" name="hobby" value="JJ" checked="checked">林俊杰 <input type="checkbox" name="hobby" value="shuyunquan">蜀云泉<br/> 城市: <select name="city" size="1"> <!--size是一次显示几个,option加value就是值是什么,不加默认写的深圳--> <option value="sz">深圳</option> <option value="bj">北京</option> <option value="hn">河南</option> </select><br/><br/> 自我介绍: <textarea name="intro" rows="5" cols="30"></textarea> <!--这里textarea不能换行,必须写两个而且在同一行--> <br/><br/> <input type="submit" value="注册"/> </form> </body> </html>
然后我们再新建一个Registerservlet的Servlet,以前我们每个Servlet都要去xml文件里面添加,很麻烦,所以我们这里使用注解。
package main.com.vae.register; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; @WebServlet("/regi") public class RegisterServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name=req.getParameter("userName"); String passWord=req.getParameter("passWord"); String sex=req.getParameter("sex"); String city=req.getParameter("city"); String intro=req.getParameter("intro"); String[] hobbys=req.getParameterValues("hobby"); System.out.println(name); System.out.println(passWord); System.out.println(sex); System.out.println(city); System.out.println(intro); System.out.println(Arrays.toString(hobbys)); } }
使用上面的 HttpServletRequest 的方法,我们可以接收到参数信息
然后你会发现一个问题,中文乱码了
解决中文乱码问题
乱码的原因,因为Tomcat对get和post的请求,默认的都是采用 ISO-8859-1 的编码格式,而我们的中文是UTF-8的格式,所以编码不一样,肯定会出现乱码。
解决方案如下:
post方式很简单,直接输入req.setCharacterEncoding,设定一下编码格式就完事。如果是get方式,其实没必要,因为表单里面写get方式的话,参数都会在url里面显示的。所以采用get方式的时候一般都是查询,不会有参数的,有参数也是查询的参数,几乎没有中文。如果非要采用get传中文参数,可以修改Tomcat中的server.xml配置文件
Servlet的映射细节
每一个Servlet都可以在web.xml里面配置,这个配置的映射可以有很多可能
这个是xml文件里面的,注解里面也可以这样写
@WebServlet(value="/resp", initParams = { @WebInitParam(name="encoding",value = "utf-8"), @WebInitParam(name="name",value = "许嵩") } )
Servlet的配置xml和注解使用哪个?
xml
优点:一目了然,结构清晰
缺点:每个Servlet都要配置xml,那100个就要写100次,代码重复,且造成xml文件的臃肿
注解
优点:简单,不臃肿
缺点:和Java代码耦合在了一起,高耦合
Servlet随服务器一起启动
Servlet都是我们访问他们才会出结果。如果你想使得某一个Servlet随着服务器一起启动的话可以这样设置
先看我的Servlet类吧
package main.com.vae.day1; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MappingServlet extends HttpServlet { public MappingServlet() { System.out.println("构造器"); } @Override public void init(ServletConfig config) throws ServletException { super.init(config); System.out.println("初始化"); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(req.getMethod()); } }
xml配置方法:
加一个load-on-startup就可以了,完美
<servlet> <servlet-name>MappingServlet</servlet-name> <servlet-class>main.com.vae.day1.MappingServlet</servlet-class> <!--load-on-startup就是设置为启动服务器的时候启动这个Servlet,里面的数字越小,优先级越高--> <load-on-startup>1</load-on-startup> </servlet> <!--向外暴露该Servlet类的一个资源名称,供外键直接访问,资源名称必须以 / 打头--> <servlet-mapping> <servlet-name>MappingServlet</servlet-name> <url-pattern>/map</url-pattern> </servlet-mapping>
注解的配置方法:
先把我们上面的xml配置注释掉,看看注解的方式
@WebServlet(value = "/map",loadOnStartup = 1)
注解真的是好简单啊。。。
Servlet的线程不安全问题
在service方法里面这样写
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); PrintWriter out=resp.getWriter(); String name=req.getParameter("name"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } out.println("你输入的名字为:"+name); }
然后你打开浏览器输入一个名字
localhost/map?name=许嵩
让别人也输入一个名字,或者你在3秒之内改一个名字,你发现,获取的不是许嵩了,这就是Servlet的线程不安全问题
Servlet的缺点
合并Servlet
比如说我想写一个用户类的增删改查,难道我要写4个Servlet吗?那肯定是不行的,所以有了Servlet的合并,这样
Servlet的上下文路径
比如,我想访问某个Servlet,我要写 路径名称/Servlet名称 因为我们的path经常为空不写,所以在访问Servlet的时候也不写,万一我写path的名称了呢?我写一个aaa,那你代码里面访问Servlet必须加上aaa,我又突然修改了配置文件里面的path为bbb,难道你还要去改代码?所以我们使用上下文路径,就是上图,自动的去获取path的值。