WEB开发(2) Servlet

 

HTTP协议

点击链接,服务器怎么知道要打开哪一个网页呢?答案是HTTP协议。在浏览器中单击链接的时候,浏览器会向服务器发送一段文本,告诉服务器请求的是那个网页。

GET方式提交数据:

b61781bae462e9e0c7d9390839ad0cb23be.jpg

HTTP头数据:

以Get方式访问/s?wd=Java 这个网站,用的是HTTP/1.0的协议。Get方式查询时,提交的查询内容Java是显示在浏览器地址栏中的,并且提交的网址不能超过256个字符。

其中User-Agent是浏览器信息,Accept是浏览器支持的格式,Cookie记录的是用户当前的状态,Referer是指从哪个页面点击链接进入/s?wd=Java页面的。

 

POST方式提交数据:

567a585bd81a5926de729c5bb68ecb0c199.jpg

7c6dc84fa0cd35808e36efe50e0bc56267a.jpg

由于Get不能提交超过256个字符,于是大量的文本需要用Post来提交,如提交用户资料、上传文件。提交的数据不会在地址栏显示。

文本的,普通文本数据:application/x-www-form-urlencoded

非文本的多媒体数据,文件数据:multipart/form-data

总的来说,Get负责显示页面(以及传输少量数据),Post负责上传数据

 

Servlet

23dac9a0d2b8ca5aa2a1ef325fa4c61fcc0.jpg

在配置文件中,servlet的配置:

6b37aa60fa4674e47643224516c2de2d7b7.jpg

537737d535b81786466007b24e46b073914.jpg

<servlet> 告诉servlet容器(如tomcat),我需要提供XX功能的servlet;

<servlet-mapping> 我可以通过哪个url访问这个servlet;

** servlet-name可以取任意字符串值,但在web.xml里必须唯一。

** servlet-name须与<servlet>元素中声明的名字一致。

 

Servlet工作流程

06b23ca074d3caa575b8df9bcb01839f527.jpg

ab58f83fad87ba6cfa514ec3d9f83409240.jpg

** 所有的HTTP头数据都可以通过request相应的方法查询到!所以说,servlet把HTTP的内容对象化了。

Servlet规范和Web访问方式

ba6cb5c99ce7b520a475fd8b9e0a82405ad.jpg

 

Java Web目录结构

0154183916b880dd76e87b9aedfe62915ce.jpg

Tomcat的/webapps是存放所有web部署的文件夹,里面所有的应用(文件夹)之间分别成为上下文路径(相对路径)

Web程序有固定的结构,必须按照这样的方式去部署:

b5d85310268028dca396dc858a034e19be7.jpg

 

编写Servlet

@WebServlet("/GetTimeServlet")
public class GetTimeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

    public GetTimeServlet() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.log("running doGet method...");
		this.execute(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.log("running doPost method...");
		//doGet(request, response); //显示用post方式提交,结果执行的时候还是用get方式提交,实际上两种方式都执行
		this.execute(request, response);
	}
	
	public long getLastModified(HttpServletRequest request) {
		this.log("running getLastModified method..");
		return -1;
	}
	
	private void execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		response.setCharacterEncoding("UTF-8");
		request.setCharacterEncoding("UTF-8");
		String requestURI=request.getRequestURI(); // http header envoloped in form of request
		String method=request.getMethod();
		String param=request.getParameter("param");
		response.setContentType("text/html");
		PrintWriter out=response.getWriter();
		out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
		out.println("<HTML>");
		out.println("<HEAD><TITLE>A Servlet</TITLE></HEAD>");
		out.println("<BODY>");
		out.println("via method:"+method+", we have the param of:"+param+"<br/>");
		out.println("  <form action='"+requestURI+"' method='get'><input type='text' name='param' value='param string'><input type='submit' value='request with GET"+requestURI+"'></form>");
		out.println("  <form action='"+requestURI+"' method='post'><input type='text' name='param' value='param string'><input type='submit' value='request with POST"+requestURI+"'></form>");
		out.println("  <script>document.write('last modified at: '+document.lastModified);</script>");
		out.println("</BODY>");
		out.println("</HTML>");
		out.flush();
		out.close();
	}
}

配置<servlet>

  <servlet>
  	<servlet-name>GetTimeServlet</servlet-name>
  	<servlet-class>com.integrate.GetTimeServlet</servlet-class>
  </servlet>

64cfe7cb17c9ccac5d854b0d6f9dd031b79.jpg

此外,<servlet>还有一些可选的配置,如init-param(包含所有你想要初始化的变量),load-on-startup

a1b4d96e11ef6b365e0e0c13c779a00c4fa.jpg

245c20b770c829cd15e46f63ed6c66c52f4.jpg

9173ea9181ca673ad0b8f767ebf2e7dbeab.jpg 

配置<servlet-mapping>

** 配置好servlet名称与类名之后还需要配置servlet访问方式

 <servlet-mapping>
  	<servlet-name>GetTimeServlet</servlet-name>
  	<url-pattern>/servlet/GetTimeServlet</url-pattern>
  </servlet-mapping>

94aff85e907777c3b8a9dc3aa6383109096.jpg

438336b410223610b7d226a2247f69e2b7d.jpg

一个完整的Servlet包括Servlet类<servlet>配置<servlet-mapping>配置,缺一不可。

出来的网页:

008b1156d2522b227e78a3607e504b50df8.jpg

能够根据表格<form>的调用方法method特性,去将数据传递到servlet从而实现处理输出。

其中采用get方法会在地址栏中显示参数,而post不会。

 

请求与响应

客户端浏览器发送了一个请求,服务器做出一系列操作后做出一个响应,发送给客户端,完成一次【Web过程】操作。

【Web编程】的过程就是通过请求分析客户需要什么信息或者进行了什么操作,然后进行一系列的处理,最后通过响应结果显示给客户。

 9f344c8baab218e87caae17c0141a05e3cd.jpg

18a633ee6a8d446d74d43ba419dc5c34997.jpg

Request的实例在Integrate/RequestServlet,Response的实例在Integrate/IdentityServlet,通过打开IdentityTest.html可以测试。

a3a729659d8394c40f4067add3abda6e583.jpg

 

初始化参数Init-param

Servlet从配置文件中获取参数

bc71a5f9abbfdfdb2724dc90615380d7570.jpg

因此我们只要用getInitParameter()就可以访问相应param-name的param-value了

fb281c053bc45a428ea0ad5cd9c3ba7e6d3.jpgc665a29fd3fe87a30c2991cc00b77a3d153.jpg

// 只显示方法部分,答对了账号和密码才能登入

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.log("run doGet()...");
		response.setCharacterEncoding("UTF-8");
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
		out.println("<HTML>");
		out.println("<HEAD><TITLE>Please log in to check File 'Notice' (Servlet)</TITLE></HEAD>");
		out.println("<style>body, font, td, div{font-size:12px; line-height:18px;}</style>");
		out.println("<BODY>");
		out.println("<form action='"+request.getRequestURI()+"' method='post'>"); //上传表格还是要调用doPost方法的
		out.println("please enter you account: <input type='text' name='username' style='width:200px'><br/>");
		out.println("please enter you password: <input type='password' name='password' style='width:200px'><br/><br/>");
		out.println("<input type='submit' value='   log in  '/>");
		out.println("</form>");
		out.println();
		out.println("</BODY>");
		out.println("</HTML>");
		out.flush();
		out.close();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		Enumeration params =  this.getInitParameterNames();
		while(params.hasMoreElements()) {
			String usernameParam=(String) params.nextElement();
			String passnameParam=getInitParameter(usernameParam);
			if(usernameParam.equalsIgnoreCase(username) && passnameParam.equals(password)) {
				request.getRequestDispatcher("/WEB-INF/notice.html").forward(request, response);
				return;
			}
		}
		this.doGet(request, response);
	}

a6d4989141bc09ad93368c098b4983ccd5d.jpg

 

上下文参数Context-Param

Init-Param配置在<servlet>里,只能够由该servlet来读取,不能被全局访问。Context-Param类似全局所有servlet都可访问的Init-Param。

56e30710222602066fe5032a46caed34593.jpg

ServletContext对象

产生:getServletConfig().getServletContext()

作用:使用getInitParameter()方法来获取指定名称的参数,使用getInitParameterNames()获取所有context-param参数名称

 87e1cbea9ecc49ed85bd43af8a85c3fa9c9.jpg

资源注射(@Resource)

通过注解,自动读取在web.xml里面的初始参数,运行时赋值

@Resource(name="messageNameInWebXml")
private String message;

也可写在同一行上

private @Resource(name="messageNameInWebXml") String message;

实例中,获取注射信息的代码

bda6f9592c368352ad4857056c25ec9986c.jpg

对应的配置文件

  <servlet>
  	<servlet-name>InjectionServlet</servlet-name>
  	<servlet-class>com.integrate.InjectionServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>InjectionServlet</servlet-name>
  	<url-pattern>/servlet/InjectionServlet</url-pattern>
  </servlet-mapping>
  
  <!-- 资源配置 -->
  <env-entry>
  	<env-entry-name>hello</env-entry-name>
  	<env-entry-type>java.lang.String</env-entry-type>
  	<env-entry-value>Hello, welcome to the JavaEE Resource Injection...</env-entry-value>
  </env-entry> 
  <env-entry>
  	<env-entry-name>i</env-entry-name>
  	<env-entry-type>java.lang.Integer</env-entry-type>
  	<env-entry-value>31</env-entry-value>
  </env-entry>
    <env-entry>
  	<env-entry-name>persons</env-entry-name>
  	<env-entry-type>java.lang.String</env-entry-type>
  	<env-entry-value>Helloween, Cobain, Roses, Axl, </env-entry-value>
  </env-entry>

配置通过环境条目env-entry来配置,配置内容包括:名称、类型和值

资源注射的原理是JNDI,也可以不用注解@Resource,直接查找JNDI获取这三个资源

e3fa6b6c15439125f4c202d3de0fce94df1.jpg

提交表单信息

decbbdd5ca9a808e92658b89d3494edaf6f.jpg

实现了一个简单的网页搜索html+servlet+xml,使用了https://duckduckgo.com/html/?q=x网站的搜索引擎,将输出网页截取部分div显示出来。

详情见search.html,SearchServlet.java

search.html的搜索界面,图片是乱找的。。。

490e2dbedc1f1c73f6218ebd573deae4d25.jpg

搜索结果,可以看见使用了get方法会在地址栏显示出来

1b58a54b61ba752b78cc7733b938e44dfee.jpg

由于GET的所有信息都显示在浏览器地址栏里,并可能被记录在缓存里,因此敏感信息(银行卡、密码)和过长内容(>255字符)不能用GET方式

上传文件客户端

433d5fa21c2589a132ff035497713cf72a3.jpg

需要先修改form表格的enctype属性

1ae1b6a8b1ae6b8010b6eddb73bf3a09118.jpg

 

Servlet的生命周期

59241249d49e106756d835e5ecb38493eea.jpg

启动:load-on-startup==1(或,第一次请求Servlet:load-on-startup==0)时初始化servlet

init()和destroy()只执行一次,但是service()在每次客户端请求servlet的时候都会被执行

c41300bca85024a841e12a8423a490b24c4.jpg

	@Override
	public void init() throws ServletException {
		this.log("running init() method...");
		ServletConfig conf = this.getServletConfig(); //  也可以直接this.getInitParameter("startPoint");这里展示ServletConfig()的用法
		startPoint = Double.parseDouble(conf.getInitParameter("startPoint"));
	}
	@Override
	public void destroy() {
		this.log("running destroy...");
		startPoint=0;
	}

此外,@PostConstruct和@PreDestroy两个注解都可以使用修饰一个非静态的void()方法,并且它不能有抛出异常声明。

// 两种写法
// 一种写在上面作为注解
@PostConstruct
public void someMethod(){}
//另外一种是写在方法名称内,返回类型的前面
public @PreDestroy void someMethod(){}

a33b865e49851aeaf8913a3ec102fccdd9b.jpg

注意!!PostConstruct和PreDestroy都是指Servlet这个东西的产生和销毁的之后和之前,

因此PreDestroy是指Servlet的销毁之前,而不是destroy()方法之前执行

* 注解会影响服务器启动速度:服务器启动时会便利Web应用的WEB-INF/classes下所有的class文件与WEB-INF/lib下所有的jar文件,以便检查那些类使用了注解。

** 在<web-app>标签设置metadata-complete="true"可以关闭掉服务器启动时的例行注解检查

 Servlet之间的相互跳转

从一个Servlet程序跳转到另外一个,可以很容易地把一项任务分隔开。

fd023a5db9ef5a09d8cee287e9d03342c5b.jpg

Forward

转向是通过RequestDispatcher对象的forward(request, response)来实现的。可以通过request.getRequestDispatcher()方法得到。

// getRequestDispatcher参数必须以“/”开始,表示本Web应用程序的根目录
RequestDispatcher dispatcher = request.getRequestDispatcher("/servlet/LifeCycleServlet");
dispatcher.forward(request,response);

以上代码就能够实现跳转,从当前Servlet跳到LifeCycleServlet去。

02792a90b167b0b0755d6572878663d34f8.jpg

Forward能够实现页面跳转,整合业务逻辑

2c3ac47b2bfe3aa32f6e30fee9b25280672.jpg

执行forward是在服务器端的改变,具体可以看ForwardServlet.java

重定向Redirect

 

d444b35342283101f5a9287303375355853.jpg

1** 信息,2** 正确,3** 重定向,4**请求错误,5**服务器错误。301是永久性重定向,302是临时性重定向

ae50d877ac6940d900e9f7c8d7b05c2107a.jpga078664aa178d6c87672ae286f926412edc.jpg

Redirect实际上是客户端再次请求,而forward是直接在服务端的页面改变

自动刷新Refresh

自动刷新不仅可以实现一段时间之后自动跳转到另外一个页面,也能实现一段时间之后自动刷新本页面。

029521cc1ddb5f87f8d9d6597df452a4d99.jpg

 反正就是,一段时间之后,刷新访问一个URL路径(可以是别的,也可以是当前的)

url是universal resource locator

uri是universal resource identifier

uri>url,一般uri用于资源,url用于网站

Servlet和线程安全

a2e83ff2bed7390305dbfb08a98f708391f.jpg

调用同一个servlet,并发时大家都会占用资源,如果让线程sleep就会发现,新线程的使用的servlet是沉睡中的旧线程

49263435ab6ba64ff1acc271ca89d56c6ba.jpg 

f5dce9f03219f96f2582080686e94b524bd.jpg

Servlet不是线程安全的,多线程并发的读写会导致数据不同步的问题。解决办法是,尽量不使用name属性,把它分别定义在doGet()或doPost()里面。

虽然使用sychronize(name){}语句块也可以解决问题,但是会造成线程的等待,不是很科学。

ec11ae5c3562dc92474a9341c36075223ee.jpg

小结一下:

270937b889590b69260494bf92bc127781f.jpg

转载于:https://my.oschina.net/swanf/blog/3063194

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值