Java for Web学习笔记(四):Servlet(2)HelloServlet

时间:2020-12-24 21:02:37

继承关系: javax.servlet.GenericServlet –》javax.servlet.http.HttpServlet。

405返回

如果我们不重写Servlet的doGet而采用HTTP GET的方式,将返回405 将返回405 Method Not Allowed。

Java for Web学习笔记(四):Servlet(2)HelloServlet

如果我们重写doGet()方法,直接return,则返回空页面,需要区分这两种方式。很多情况下,我们不太区分doGet()和doPost()这两类请求,但是如果接口明确说明,则应当不重写,拒绝服务。

第一个Servlet:HelloServlet

例子如下:

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

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/* 我们这里并不需要将reponse.getWriter()这个java.io.PrintWriter对象进行close。 一般而言,
* 我们只需要关闭我们创建的对象, 这个PrintWriter不是我们创建的, web container创建这个
* 资源并负责将之关闭。即使我们通过PrintWriter out = response.getWriter() 进行引用,也
* 不属于创建,即无需进行close()。*/
response.getWriter().println("Hello, World!");
}
}

init()和destroy()

我们先利用Maven加上Log4j2,目前最新的版本是2.4。我们在pom.xml中加入相关的关联,如图所示。Maven会下载log4j-web的2.4版本的jar包,并将关联的jar包,log4j-api.2.4.jar和log4j-core-2.4.jar都下载到maven的仓库目录中,并在项目中加入。

如果我们不是web项目,而是普通的java项目,则在pom.xml中关联log4j-api(scrope=compile)和log4j-core(scrope=runtime)即可,groupId仍是org.apache.logging.log4j。

我们将log4j2的配置文件放入main/resources目录中,这些配置文件和src文件分布存放在不同的目录。如果直接创建Dynamic web project,则这个xml文件也位于src下面。这是Maven对项目的管理功能之一。

Java for Web学习笔记(四):Servlet(2)HelloServlet

代码如下:

private static final Logger log = LogManager.getLogger();
@Override
public void destroy() {
log.info("Servlet " + this.getServletName() + " has stopped.");
}

@Override
public void init() throws ServletException {
log.info("Servlet " + this.getServletName() + " has started.");
}

要注意的是init()和destroy()与java类的构造和finalizer不同。

init()是在web container启动servlet时调用,是在servlet创建之后,在第一次请求时调用。由于已经创建,可以通过getServetCofnig()访问ServletConfig和ServletContext对象,可以读取一个properties文件或者建立JDBC的连接。

destroy()在servlet不能在接收请求时马上调用,通常是在web app停止或者卸装,或者web container关闭时。finalizer()则是在垃圾回收时触发的,而垃圾回收的VM的事,可能会在几分钟,甚至一个小时之后才进行。我们应该在此清除资源,包括临时文件,关闭与数据库的连接。

Web.xml

display-name

在maven中,我们重写了web.xml。web.xml用来描述发布servlet,filter,listener。在Servlet 3.0版本开始,我们无需为servelt在web.xml中加上定义,可以在代码中加上描述(Eclipse会自动加上),如本例的:@WebServlet("/HelloServlet")其相当于在web.xml中有以下的定义。注意是相当于,即不会写到web.xml中

<servlet>
<servlet-name> cn.wei.flowingflying.chapter03.HelloServlet </servlet-name>
<servlet-class>cn.wei.flowingflying.chapter03.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cn.wei.flowingflying.chapter03.HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>

web.xml的例子如下,我们加上display-name,这样如果通过tomcat管理页面进行部署,界面上会显示相关的信息。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Hello World Application</display-name>
</web-app>

通过项目右键->Export->war获得war文件,然后进行部署。

Java for Web学习笔记(四):Servlet(2)HelloServlet

load-on-startup

现在,我们可以不在web.xml中定义servlet,但是web.xml提供了更多的控制。init()是在第一个请求时运行,如果init()的过程持续比较久,会导致第一个请求的响应时间慢,用户体验差。我们在web.xml的web-app下面加入以下内容,要求web container在启动web app时启动servlet(即触发init()),如下:

<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>cn.wei.flowingflying.chapter03.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

如果有多个servlet都要求马上启动,数值越小表示越优先,本例是1,是最小的数值,即最优先执行。如果两个servlet的load-on-startup的数值相同,则按它们在web.xml文件中的顺序。

但是我们需要注意,如果这里定义的servlet-name与代码中定义的HelloServlet不一样的。代码中的@WebServlet("/HelloServlet")定义的servelt的名字包含完整的包名。Tomcat会认为这有两个独立的servlet实例,名字分别是cn.wei.flowingflying.chapter03.HelloServlet和HelloServlet,只不过是同一个class的两个实例。我们将会看到web app启动时,马上启动了名为HelloServlet实例,即触发了HelloServlet的init()。

此外由于HelloServlet尚未在web.xml中进行url的mapping,这个HelloServlet实例在是无法进行请求的。如果我们在浏览器中发起http://localhost:8080/chapter03/HelloServlet,则触发了名字为cn.wei.flowingflying.chapter03.HelloServlet的另一个servlet实例的init()。

所以我们应该在缺省的代码定义和web.xml的手工定义方式继续二选一,以免引起混淆。如果选择手工定义的方式,要注销掉代码的@WebServlet的定义,以免代码由他人处理时会在使用servlet实例上出现混乱。如果两者同时使用,发生冲突时,以web.xml中的定义为准,但这和具体的web container有关。

这也给我们另外的启示。我们可以为同一个servlet类给出两个servlet名字,也就是web container基础同样的codebase创建两个servlet实例。我们可以通过getServlet()来获取servlet的名字,基于不同的servlet,我们可以有不同的初始化,例如引导到不同的数据库来源。

mapping

url的映射不是必要的。缺省按代码中@WebServlet("/HelloServlet")的描述。也可以将之映射到多个url中,如下所示。我们仍保留@WebServlet的描述,将url增加映射到/greeting和/hello,整个web.xml如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Hello World Application</display-name>

<servlet-mapping>
<servlet-name>cn.wei.flowingflying.chapter03.HelloServlet</servlet-name>
<url-pattern>/greeting</url-pattern>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

</web-app>

名字为cn.wei.flowingflying.chapter03.HelloServlet将可以被http://localhost:8080/chapter03/greeting和http://localhost:8080/chapter03/hello访问到。这是不同的入口,实际上都是同一个servlet。注意,如果访问http://localhost:8080/chapter03/HelloServlet,则得到404的响应,对代码中描述@WebServlet在mapping中进行覆盖,因此再次建议@WebServlet和xml的描述应二选一。

使用@webServlet

如果我们要使用@WebServlet进行诸如load-on-startup的定义,如下所示。urlPatterns是一个数组,可以定义多个url mapping。

@WebServlet(
name = "ticketServlet",
urlPatterns = {"/tickets"},
loadOnStartup = 1
)


相关链接: 我的Professional Java for Web Applications相关文章