Servlet详解

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、基本语法

java 复制代码
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 ;
  • 导入三个包
java 复制代码
//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中进行配置

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

java 复制代码
 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

xml 复制代码
  <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

java 复制代码
@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

java 复制代码
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方法。

java 复制代码
 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

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里的初始化参数读出来,保存在全局变量里,然后给其他方法使用,如下

java 复制代码
 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

html 复制代码
<form action="formServlet1" method="post">
用户名:<input type="text" name="uname">
<input type="submit" value="提交">
</form>

servlet

java 复制代码
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

xml 复制代码
  <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的路径(也是请求地址)里找到要提交的地址。

xml 复制代码
<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 ,那么如果一百个用户呢?这样配置量太大了,我们可以采用通配符来统一管理

xml 复制代码
 <servlet-mapping>
	<servlet-name>form</servlet-name>
	<url-pattern>*.do</url-pattern>
  </servlet-mapping>

只要来自后缀为.do的请求都会映射到formServlet1

将a,b,c的表单提交地址改为

html 复制代码
<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,找不到对应路径,所以返回不了要找的结果,报异常

相关推荐
m0_571957582 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功4 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨4 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟5 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity6 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天6 小时前
java的threadlocal为何内存泄漏
java
caridle7 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^7 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋37 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx