1、Servlet
1、Java支持动态网页的技术:直接编写Java,利用CGI的方式与WebServer沟通
2、servlet在MVC中相当于控制层的作用。
Servlet的作用:
- CGI:通用网关接口:是从WEB容器中取得数据(内置对象)的能力
- 创建表单bean、收集表单数据
- 分析请求,分发请求
- 根据业务层返回的转向信息,转到视图组件
3、JavaServlet架构
tomcat启动,创建servlet对象,request和response方法注入到servlet(doGet方法、doPost方法),通过request创建表单bean、收集表单数据,通过response返回到浏览器
4、Servlet的生命周期
- create ( 加载、实例化):加载Servlet类,为对象分配空间;配置了loadOnStartup,tomcat一启动就创建;没有配置,在请求servlet的时候创建
- initilalize(初始化):servlet一创建,就会执行init方法(有参的或者无参的),如果都没有就执行父类的init方法,什么都不做
- 请求处理:服务阶段。根据请求调用doGet或者doPost方法处理请求
- 销毁
2、基本语法
1、基本语法
package cn.zte.pxb.servlet ;
//IOException属于java.io包下
import java.io.* ;
// ServletException属于javax.servlet包下
import javax.servlet.* ;
// HttpServletRequest、HttpServletResponse存放在javax.servlet.http包下
// HttpServlet属于javax.servlet.http包下
import javax.servlet.http.* ;
public class SimpleServlet extends HttpServlet
{
// 表示处理get请求 地址栏、表单。
public void doGet(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{ System.out.println("doGet方法被调用");
//依赖查找
PrintWriter out = resp.getWriter() ;
out.println("<HTML>") ;
out.println("<HEAD>") ;
out.println("<TITLE>THE FIRST SERVLET</TITLE>") ;
out.println("</HEAD>") ;
out.println("<BODY>") ;
out.println("<H1>Hello World!!!</H1>") ;
out.println("</BODY>") ;
out.println("</HTML>") ;
out.close() ;
}
public void doPost(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{System.out.println("doPost方法被调用");
this.doGet(req,resp) ;
}
}
- 也是组件,和JavaBean一样,需要包声明package cn.zte.pxb.servlet ;
- 导入三个包
//IOException属于java.io包下
import java.io.* ;
// ServletException属于javax.servlet包下
import javax.servlet.* ;
// HttpServletRequest、HttpServletResponse存放在javax.servlet.http包下
// HttpServlet属于javax.servlet.http包下
import javax.servlet.http.* ;
- 继承HttpServlet类
- 复写doGet或doPost方法
2、部署Servlet
- 因为要编译SimpleServlet需要JavaEE的包(javax.servlet.http),而我们使用javac编译是没有这个包的。
- JavaEE用到的jar包在tomcat下有。将tomcat/lib中的关于servlet的包servlet-api.jar复制一份到jdk/jre/lib/ext下,这里是jdk放添加的扩展jar包的目录 。这样之后,我们就可以用javac来编译Servlet了
- 将SimpleServlet放到web-inf下的class目录下,进行打包编译-- javac -d . SimpleServle.java
3、配置servlet ,在web.xml中进行配置
<servlet>
<servlet-name>simple</servlet-name>
<servlet-class>cn.zte.pxb.servlet.SimpleServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>simple</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
servlet-name : Servlet的名称
servlet-class : 字节码文件所在的包名(加上自己的文件名)
load-on-startup : 设置是否tomcat一启动就加载该servlet。设置数值是加载这个Servlet的优先级
servlet-mapping :设置请求这个Servlet的映射地址
请求地址:http://localhost:8080/test/demo
- tomcat一启动,加载web.xml文件,因为配置了loadOnStartup,所以一启动就会加载对应的字节码文件 ,即创建这个SimpleServlet对象(会进行初始化调用init())。
- 通过请求地址,先在test对应的虚拟目录下找demo文件,没有找到,接着在WEB-INF下查找,根据映射地址找到的是SimpleServlet这个对象
- 因为是get请求,调用doGet方法,request和response立马注入到doGet方法中。(Servlet容器内部实现,从源码可以看到,HttpServletRequest的实现类中,会调用getMethod()方法,从而判断出get方法或post方法,再分发到doGet方法或者doPost方法去处理 )
- 执行完后返回到浏览器显示结果
3、配置Servlet
1、完整的Servlet
package cn.zte.pxb.servlet ;
import java.io.* ;
import javax.servlet.* ;
import javax.servlet.http.* ;
public class LifeCycleServlet extends HttpServlet
{
// 初始化
public void init(ServletConfig config) throws ServletException
{
System.out.println("** Servlet 初始化 ...") ;
}
// 表示处理get请求
public void doGet(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
System.out.println("** Servlet doGet处理 ...") ;
}
// 处理post请求
public void doPost(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{ this.doGet(req,resp);
//System.out.println("** Servlet doPost处理 ...") ;
}
// 销毁
public void destroy()
{
System.out.println("** Servlet 销毁 ...") ;
}
}
- 复写了四个方法,分别是
public void init() :servlet对象一创建,就会执行init()方法,ServletConfig封装着配置文件里的初始参数信息会注入到init方法里头。可以通过config获取初始参数。至于是不是tomcat一启动就创建Servlet,要看有没有加上loadOnStartup配置。
public void doGet() : 处理get请求的方法
public void doPost() : 处理post请求的方法
public void destory() : 销毁Servlet。关闭服务器后,Servlet销毁
2、使用配置文件:在web.xml配置Servelt
<servlet>
<servlet-name>life</servlet-name>
<servlet-class>cn.zte.pxb.servlet.LifeCycleServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>life</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
3、servlet的注解开发 :使用注解来配置Servlet
@WebServlet(urlPatterns = "/hello",loadOnStartup = 1)
public class HelloServlet extends HttpServlet{
public void init(ServletConfig config)throws ServletException{
}
public void doGet(HttpServletRequest req,HttpServletResponse resp)throws IOException,ServletException{
}
public void doPost(HttpServletRequest req,HttpServletResponse resp)throws IOException,ServletException{
}
public void destory(){
}
}
@WebServlet(urlPatterns = “/hello”,loadOnStartup = 1)
配置映射地址,是否在启动就创建servlet
4、关于初始化Servlet
package cn.zte.pxb.servlet ;
import java.io.* ;
import javax.servlet.* ;
import javax.servlet.http.* ;
public class LifeCycleServlet1 extends HttpServlet
{
// 初始化
public void init() throws ServletException
{
System.out.println("** Servlet使用 init() 初始化 ...") ;
}
// 初始化
public void init(ServletConfig config) throws ServletException
{
System.out.println("** Servlet 使用 init(ServletConfig config)初始化 ...") ;
}
}
/*
<servlet>
<servlet-name>life</servlet-name>
<servlet-class>cn.zte.pxb.servlet.LifeCycleServlet1</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
*/
- 这个Servlet只进行初始化,不处理任何请求(没有doGet和doPost,只有init)
- 不需要配置servlet-mapping,因为不需要处理请求;但是一定要配置loadOnStartup,tomcat一启动就创建初始化Servlet,调用init方法。如果不配置loadOnStartup,而它又不处理请求,那永远都得不到初始化。
- 一个有参的初始化方法和一个无参的初始化方法。初始化tomcat会调用有参的初始化方法。只写一个就只调用写的那个。
5、配置初始参数
在web.xml中配置初始化参数,在tomcat启动时创建Servlet对象,调用init方法,ServlteConfig封装初始化参数注入init方法。
package cn.mldn.lxh.servlet ;
import java.io.* ;
import javax.servlet.* ;
import javax.servlet.http.* ;
public class InitParameterServlet extends HttpServlet
{ private String usename=null;
private String password=null;
//private int count=0;
// 初始化
// 要取得初始化参数,必须使用以下初始化方法
public void init(ServletConfig config) throws ServletException
{
// config对象中有取得初始化参数的方法:getInitParameter("参数名称")
this.usename= config.getInitParameter("usename") ;
this.password = config.getInitParameter("password") ;
String dd = config.getInitParameter("DBDRIVER") ;
//this.count=2;
System.out.println("usename => "+this.usename) ;
System.out.println("password => "+this.password) ;
System.out.println("DBDRIVER => "+dd) ;
}
// 表示处理get请求
public void doGet(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
System.out.println("** Servlet doGet处理 ...") ;
System.out.println("usename="+this.usename) ;
System.out.println("password="+this.password) ;
//System.out.println("password="+this.count-1) ;
}
// 处理post请求
public void doPost(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
// System.out.println("** Servlet doPost处理 ...") ;
}
// 销毁
public void destroy()
{
// System.out.println("** Servlet 销毁 ...") ;
}
}
配置xml
<servlet>
<servlet-name>param</servlet-name>
<servlet-class>cn.mldn.lxh.servlet.InitParameterServlet</servlet-class>
<load-on-startup>0</load-on-startup>
<init-param>
<param-name>usename</param-name>
<param-value>scott</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>tiger</param-value>
</init-param>
<init-param>
<param-name>DBDRIVER</param-name>
<param-value>oracle.jdbc.driver.OracleDriver</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>param</servlet-name>
<url-pattern>/init</url-pattern>
</servlet-mapping>
- tomcat一启动,扫描web.xml,将配置文件里的参数封装在ServletConfig中。< init-param>
- 创建Servlet对象,调用init方法初始化,封装了初始参数的ServletConfig会自动注入到init()方法中,可以通过config获取到初始化参数
- 从config中拿到封装的初始化参数。
this.usename= config.getInitParameter(“usename”) ;
- 用实例全局变量接收初始化参数,可以提供给这个类的所有方法使用
什么是线程安全?
线程安全问题一般都是由全局变量或者静态变量引起。当两个线程要对同一变量操作,涉及写操作时,就会产生线程问题。
-
init方法是tomcat一启动就会调用,只有这一个线程调用,这一定是线程安全的
-
而doGet和doPost是多个线程可以同时访问的,所以在doGet、doPost里只是对初始化参数进行读操作 ,而没有写操作,不存在竞争,所以也是线程安全的。
-
所以说Servlet是保证线程安全的
-
如果在doGet中要进行参数的写操作(修改),这样就存在多线程竞争一个全局变量的问题,会出现线程不安全的问题。可以将这个方法提取出来,然后加上synchronized,保证线程安全。
-
在web.xml中配置初始化参数风险很大,一旦标签或者写错就会导致整个tomcat启动报错 。可以将初始化参数存放在properties里,在init方法中,通过输入流将properties里的初始化参数读出来,保存在全局变量里,然后给其他方法使用,如下
package cn.mldn.lxh.servlet ;
import java.io.* ;
import javax.servlet.* ;
import javax.servlet.http.* ;
import java.util.Properties;
public class InitParameterServlet extends HttpServlet
{ private String username=null;
private String password=null;
// 初始化
// 要取得初始化参数,必须使用以下初始化方法
public void init()
{
try {
Properties properties = new Properties();
FileInputStream fileInputStream = new FileInputStream(new File("D:/testweb/WEB-INF/classes/dao.properties"));
properties.load(fileInputStream);
fileInputStream.close();
this.username = properties.getProperty("username");
this.password = properties.getProperty("password");
String dd = properties.getProperty("dbDriver") ;
System.out.println("username => "+this.username) ;
System.out.println("password => "+this.password) ;
System.out.println("dbDriver => "+dd) ;
} catch (IOException e) {
e.printStackTrace();
}
}
// 表示处理get请求
public void doGet(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
System.out.println("** Servlet doGet处理 ...") ;
System.out.println("username="+this.username) ;
System.out.println("password="+this.password) ;
}
// 处理post请求
public void doPost(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
// System.out.println("** Servlet doPost处理 ...") ;
}
// 销毁
public void destroy()
{
// System.out.println("** Servlet 销毁 ...") ;
}
}
4、通过表单访问Servlet
1、通过表单访问Servlet
htm
<form action="formServlet1" method="post">
用户名:<input type="text" name="uname">
<input type="submit" value="提交">
</form>
servlet
package cn.cx.servlet ;
import java.io.* ;
import javax.servlet.* ;
import javax.servlet.http.* ;
public class FormServlet extends HttpServlet
{
private ServletConfig config = null ;
public void init(ServletConfig config) throws ServletException
{
this.config= config ;
}
// 表示处理get请求
public void doGet(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
System.out.println("** Servlet doGet处理提交参数 ...") ;
req.getRequestDispatcher("demo.jsp").forward(req,resp);
//this.doPost(req,resp) ;
}
// 处理post请求
public void doPost(HttpServletRequest req,HttpServletResponse resp) throws IOException,ServletException
{
//从request中获取参数。request已经自动注入
String name = req.getParameter("uname") ;
// 取得一个session对象
HttpSession session = req.getSession() ;
session.setAttribute("sname",name) ;
// 取得application对象-没有config
// ServletContext app = this.getServletContext() ;
// 取得application对象-有config
//ServletContext app = this.config.getServletContext() ;
// 取得application对象,不通过config拿,直接拿
ServletContext app=req.getSession().getServletContext();
app.setAttribute("addr","www.zte.cn") ;
System.out.println("** Servlet doPost处理提交参数 ...") ;
System.out.println("name = "+name) ;
// 重
//resp.sendRedirect("demo.jsp") ;
req.getRequestDispatcher("demo.jsp").forward(req,resp);
}
};
web.xml配置Servlet
<servlet>
<servlet-name>form</servlet-name>
<servlet-class>cn.cx.servlet.FormServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>form</servlet-name>
<url-pattern>/02_servlet/formServlet1</url-pattern>
</servlet-mapping>
- tomcat启动,加载FormServlet对象,进行初始化。
< form action=“formServlet1” method=“post”>
- 进入表单页面,提交方式为post,虽然这里请求只写了formSerclet1,但真正的请求地址其实是/02_servlet/formServlet1,/02_servlet是访问表单的路径 ,servlet会自动将其当做转发到视图组件的默认地址。
- req.getRequestDispatcher(“demo.jsp”).forward(req,resp);所以这里跳转到的请求是 /02_servlet/demo.jsp
- 从哪访问,Servlet就会去哪找视图组件
2、模拟不同用通过相同的表单访问同一个Servlet
模拟三个不同用户访问同一个表单提交信息到同一个servlet。----只需要在web.xml中配置不同的url-pattern。servlet会在对应的url-pattern的路径(也是请求地址)里找到要提交的地址。
<servlet-mapping>
<servlet-name>form</servlet-name>
<url-pattern>/a/formServlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>form</servlet-name>
<url-pattern>/b/formServlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>form</servlet-name>
<url-pattern>/c/formServlet1</url-pattern>
</servlet-mapping>
-
提交的都是“formServlet”,但是发送的请求带着他们访问的路径的。转发到的demo.jsp是三个地址的demo.jsp,由他们配置的映射路径决定
-
req.getRequestDispatcher(“demo.jsp”).forward(req,resp); 这里只写了相对路径demo.jsp,servlet会自动到当前请求路径的目录下去寻找demo.jsp
如:对于a用户,访问a页面
a页面的from.htm,form action="formServlet1“ ,提交请求的地址实际是 /a/formServlet1 ,servlet根据请求地址,在servlt-mapping中找到对应的servlet-name,然后执行这个类的doPost方法(是post提交),转发到/a/demo.jsp 。
在url-pattern里一定要配置完整的路径名
3、优化
三个用户要配置三个servelt-mapping ,那么如果一百个用户呢?这样配置量太大了,我们可以采用通配符来统一管理
<servlet-mapping>
<servlet-name>form</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
只要来自后缀为.do的请求都会映射到formServlet1
将a,b,c的表单提交地址改为
<form action="formServlet1.do" method="post">
用户名:<input type="text" name="uname">
<input type="submit" value="提交">
</form>
这样就不用为访问同一个表单同一个servlet去配置多个mapping!
如:
**a表单,提交,请求地址实际为 /a/formServlet.1do ,servlet在web.xml中找到匹配的url-pattern: *.do ,然后映射到formServle1,调用doPost方法,转发到 /a/demo.jsp **
c表单提交没加.do后缀,提交,请求地址为 /b/formServlet1 ,servlet在web.xml没有找到匹配的url-pattern,找不到对应路径,所以返回不了要找的结果,报异常