Java打卡:第98天

javaWeb — Servlet

Java EE


Servlet


这几天servlet的进度有一点停滞,因为Mysql的理论,还有数字逻辑基本电路,包括FSM和FPGEA的学习;之前说过狭义的Servlet接口中有5个基本的方法,包括service服务,init初始化,destroy销毁Servlet对象,getServletInfo获得开发者等信息,还有就是getServletConfig可以获得配置对象,一个Servlet对象对应一个config对象,但是一个web.xml也就是一个应用程序只对应一个compex对象,就是环境【上下文】

ServletContext

一个应用程序application就只有一个Context对象;其中的Servelt对象的上下文环境都是一样的。Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.

其中有很多的常用的方法比如attribute,parameter,path路径等

首先就是这里也有parameter操作,这里的参数是对应的application,所以配置的时候不是在Servlet标签中,而是在外面的标签中。

<!-- 定义所有的servlet对象可以共享的参数 -->
	<context-param>
		<param-name>name</param-name>
		<param-value>cfeng.cn</param-value>
	</context-param>
	<context-param>
		<param-name>address</param-name>
		<param-value>西安古城墙</param-value>
	</context-param>

这里就是设置的一个全局的一个参数,这里的访问和ServletConfig对象是相同的

	ServletContext context = config.getServletContext();
		//获取所有的初始化参数名
		Enumeration<String> names = context.getInitParameterNames();
		while(names.hasMoreElements()) {
			String nextPara = names.nextElement();
			System.out.println(nextPara + ":" + context.getInitParameter(nextPara)); //这里就是键值对,输入名称获得值
		}

信息: [622]毫秒后服务器启动
address:西安古城墙
name:cfeng.cn

输出的效果和上面是相同的。这里获取的是一个Enums的一个存储了所有的键key

  • 域属性attribute

域属性是全局的,任何Servlet所做的修改都是全局性的

除了参数直接在xml文件中直接定义,还可以在java程序中写入attribute域属性,同样,域属性也是可以共享的,其他的Servlet类可以访问修改

public class otherServlet implements Servlet
    ……

配置文件很简单

<servlet>
		<servlet-name>otherServlet</servlet-name>
		<servlet-class>com.cfeng.otherServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>otherServlet</servlet-name>
		<url-pattern>/other</url-pattern>
	</servlet-mapping>
  • 路径ContextPath

这个方法可以获取到的就是程序的路径,其中遵循的是HTTP的url的方式,而RealPath则是获取到遵循本地的file协议的文件的路径

	//context中的参数是应用程序的,所有的servlet对象共享
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		ServletContext context = config.getServletContext();
		//获取所有的初始化参数名
		Enumeration<String> names = context.getInitParameterNames();
		while(names.hasMoreElements()) {
			String nextPara = names.nextElement();
			System.out.println(nextPara + ":" + context.getInitParameter(nextPara)); //这里就是键值对,输入名称获得值
		}
		//设置域属性  【所有的Servlet对象是共享的】
		context.setAttribute("qq", "18979697");
		context.setAttribute("email", "163@com");
		
		
		String attriName = (String) context.getAttribute("qq");
		System.out.println(attriName);
		
		System.out.println(context.getContextPath());
		System.out.println(context.getRealPath("/webapp"));
	}
	
	
	
address:西安古城墙
name:cfeng.cn
18979697
/proj1
D:\Java项目\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\proj1\webapp

可以看到上面的路径就是项目的路径,HTTP中URL的路径;而下面的是本地的文件的存放的位置

欢迎页面

在web.xml中可以设置欢迎页面,也即是welcome-list,包括html,jsp等

所用的标签就是welcome-file-list ,其中的子标签都是welcome-file

<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>

所以可以在项目下面直接建立一个html文件

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>cfeng.com IT助力学习</title>
</head>
<body>
	<font color="red">你好,欢迎访问本界面</font>
</body>
</html>

这样,当我们访问的时候九会直接访问该程序,其实就是web.xml中的欢迎界面

就是直接只输入项目的名称的时候,访问到的就是这个默认的界面;还可以通过将文件名改为ROOT,就可以通过域名直接访问到该程序,同时访问到的就是欢迎界面

如果有多个文件,就按顺序查找欢迎界面里面的文件,前面的HTML是先访问的,后面的HTML就不访问了;所以先访问的是html,后面的css和jsp都是会查找;查找不到的时候会从Tomcat服务器中找;找不到就404

url-pattern 精确路径 通配路径 全路径

路径的匹配有精确的路径匹配模式;一个servlet可以书写多个url-pattern和其匹配

当要访问的时候就是使用Servlet-Mapping中的标签url-pattern就可以

  • 同时还可以使用通配符匹配模式
使用通配符模式
<url-pattern>/other/*</url-pattern>

这里就代表访问的时候,只要输入了路径/other,后面的就可以随意书写

  • 全路径

上面的通配路径使用的是通配路径,要写上前面的,后面的任意都可以访问;全路径就是可以书写任意的都可以访问

使用全路径模式
<url-pattern>/*</url-pattern>
<url-pattern>/</url-pattern>

这里就拦截了所有的请求,包括动态或者静态的资源的访问都连接,只是执行该servlet

/* 会拦截所有的请求;但是/只是拦截静态资源请求,动态资源请求包括jsp等不会拦截

  • 后缀模式
<url-pattern>*.do</url-pattern>

这里的意思是拦截所有的以.do结尾的请求;后缀名模式和路径模式不能同时使用

匹配优先级

  • 路径优先后缀

也就是当都满足路径和后缀的时候,是优先访问的路径

  • 精确优先通配

当输入的路径是精确的时候,会优先查找是否有精确匹配的,之后才会去找通配的;

  • 最长路径优先匹配

当都是通配的时候,路径更长的自然会更精确,所以会先匹配长路径

Servlet core

GenericServlet

Generic 通用的,一般的

GenericServlet;之前定义Servlet就发现了问题,实现一个类必须实现Servlet接口,但是真正使用到的只有一个service方法,其他的方法都没有使用到;那么就构想使用一个适配器,该适配器只有一个抽象方法就是service

使用适配器设计模式中的缺省适配器模式;就是空实现一些方法,这样在开发的时候,先定义一个abstract类实现Servlet接口,之后定义的Servlet操作类都只需要继承给Generic

这里的思路和之前讲解Mysql后面为了方便操作定义一个工具类DBUtil相同,都是为了简化之后的操作

package cfeng;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.*;

public abstract class GenericServlet implements Servlet, ServletConfig {
	private ServletConfig config;

	@Override
	public void init(ServletConfig config) throws ServletException {
		this.config = config;
	}

	@Override
	public ServletConfig getServletConfig() {
		return config;
	}

	@Override
	public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void destroy() {

	}

	@Override
	public String getServletName() {
		return config.getServletName();
	}

	@Override
	public ServletContext getServletContext() {
		return config.getServletContext();
	}

	@Override
	public String getInitParameter(String name) {
		return config.getInitParameter(name);
	}

	@Override
	public Enumeration<String> getInitParameterNames() {
		return config.getInitParameterNames();
	}

}

这里的GenericServlet实现这个ServletConfig的目的就是方便子类的使用,直接在其中定义一个私有的对象,然后子类直接就通过this就可以调用方法了,因为本身就是一个config对象

package cfeng;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class SomeServlet extends GenericServlet {

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		//但是如果这里想使用配置对象,那么就必须要创建才能使用,可以直接让父类实现Config接口
		//但是这样子还是要先创建对象,可以直接实现接口,之后就可以直接使用方法,使用this
		System.out.println(this.getInitParameter("name"));
	}
}

子类的书写就没有之前的那么繁杂了,非常简洁,但是还是需要在配置文件中进行配置

修改init方法: 模板方法设计模式

上面介绍的设计模式是适配器设计模式,对于方法很多,但是经常使用的方法很少的时候,就使用适配器模式,java的源码中,特别是界面位置广泛使用了这种设计模式。这里的Generic的init方法可以修正一下

  • 需求: 子类someServlet执行的时候想要在init方法中输出一个”hello world“?

这里作为一个基本的想法就是子类重写一下父类的init方法;但是容易造成的是空指针异常,因为GenericServlet中的init的一个作用就是获取到config对象;所以要想正常的使用子类就必须加上一个super

@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config); //不加这一个,就会出现空指针异常
		System.out.println("hello world");
	}

信息: 已完成重新加载名为/proj1的上下文
hello world
cfeng

但是这里的缺点就是一定要记得加上super.init;这里因为是使用的快捷键alt + shift + s最后按下v;所以自动生成了,自己写容易忘记,如何优化呢?
这里就使用模板方法模式,对于这种父类的方法有特定功能的,就要将其作为模板方法,剥离出一个空方法,专门用来作为继承的方法

HTTP状态 500 - 内部服务器错误
类型 异常报告

消息 Cannot invoke "javax.servlet.ServletConfig.getInitParameter(String)" because "this.config" is null

描述 服务器遇到一个意外的情况,阻止它完成请求。

例外情况

java.lang.NullPointerException: Cannot invoke "javax.servlet.ServletConfig.getInitParameter(String)" because "this.config" is null
	cfeng.GenericServlet.getInitParameter(GenericServlet.java:46)
	cfeng.SomeServlet.service(SomeServlet.java:16)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
):注意 主要问题的全部 stack 信息可以在 server logs 里查看

Apache Tomcat/9.0.55

这里就是删除上面那行super.init(config)之后的结果

package cfeng;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class SomeServlet extends GenericServlet {

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println(this.getInitParameter("name"));
	}

	@Override
	public void init() {
		System.out.println("hello world");
	}
}

这里就是看到init就没有写上面那行代码,但是不报错,就使用模板方法模式

package cfeng;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.*;

public abstract class GenericServlet implements Servlet, ServletConfig {
	private ServletConfig config;

	@Override
	public void init(ServletConfig config) throws ServletException {
		this.config = config;
		init();
	}
	
	public void init() {
		//专门用来继承
	}

	@Override
	public ServletConfig getServletConfig() {
		return config;
	}

	@Override
	public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void destroy() {

	}

	@Override
	public String getServletName() {
		return config.getServletName();
	}

	@Override
	public ServletContext getServletContext() {
		return config.getServletContext();
	}

	@Override
	public String getInitParameter(String name) {
		return config.getInitParameter(name);
	}

	@Override
	public Enumeration<String> getInitParameterNames() {
		return config.getInitParameterNames();
	}

}

模板方法模式就是基于的重载,接下来就是HTTPServlet了,虽然JSP等过时,但是为了给框架学习打基础,还是要学习的🎄

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐