Java Bean

Java Bean 是什么?

1.JavaBean 本身就是一个类,属于 Java 的面向对象编程。

2.在 JSP 中如果要应用 JSP 提供的 Javabean 的标签来操作简单类的话,则此类必须满足如下的开发要求:

(1) 所有的类必须放在一个包中,在 WEB 中没有包的是不存在的;
(2) 所有的类必须声明为 public class,这样才能够被外部所访问;
(3) 类中所有的属性都必须封装,即:使用 private 声明;
(4) 封装的属性如果需要被外部所操作,则必须编写对应的 setter、getter 方法;
(5) 一个 JavaBean 中至少存在一个无参构造方法,此为 JSP 中的标签所使用。

Java Bean 有什么作用和优点?

JavaBean 是使用 Java 语言开发的一个可重用的组件,在 JSP 的开发中可以使用 JavaBean 减少重复代码,使整个 JSP 代码的开发更简洁。

JSP 搭配 JavaBean 来使用,有以下的优点:

  1. 可将 HTML 和 Java 代码分离,这主要是为了日后维护的方便。如果把所有的程序代码( HTML 和 Java )写到 JSP 页面中,会使整个程序代码又多又复杂,造成日后维护上的困难。
  2. 可利用 JavaBean 的优点。将日常用到的程序写成 JavaBean 组件,当在 JSP 要使用时,只要调用 JavaBean 组件来执行用户所要的功能,不用再重复写相同的程序,这样以来也可以节省开发所需的时间。

简单的 demo

package com.seasidecrab.test;

public class SimpleBean {
    
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
}

与 JSP 搭配使用

JSP 开发目录结构

java-bean
-- WEB-INF
---- classes
---- libs
---- web.xml
-- index.jsp

java-bean 为 web 应用根目录,WEB-INF 下 classes 存放编译好的字节码文件,libs 存放项目依赖的 jar 包,web.xml 配置当前项目的 web 配置,如: 入口文件,使用到的 servlet 配置等等。index.jsp 为默认入口文件。

import 方式引入 Bean

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!-- 导入com.seasidecrab.test包 -->
    <%@ page import="com.seasidecrab.test.*"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>java-bean</title>
</head>
<body>
<%
    out.println("Hello World!");
    SimpleBean simpleBean = new SimpleBean();
    simpleBean.setName("Jason Li");
    simpleBean.setAge(27);
%>
<h3>姓名:<%=simpleBean.getName() %></h3>
<h3>年龄:<%=simpleBean.getAge() %></h3>
</body>
</html>

使用 jsp:useBean 标签引入

除了使用 import 的语句之外,也可以使用 JSP 中提供的:<jsp:useBean> 指令完成操作,指令的操作语法如下所示:

1.<jsp:useBean id="实例化对象名称" scope=“保存范围” class=“包.类名称“/>

2.主要属性:

(1) id:表示实例化对象的名称
(2) scope:表示此对象保存的范围,一共有四种属性范围:page、request、session、application
(3) class:对象所对应的包.类名称

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!-- 使用 jsp:useBean -->
    <jsp:useBean id="simpleBean" scope="page" class="com.seasidecrab.test.SimpleBean"/>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>java-bean</title>
</head>
<body>
<%
    out.println("Hello World!");
    simpleBean.setName("Jason Li");
    simpleBean.setAge(27);
%>
<h3>姓名:<%=simpleBean.getName() %></h3>
<h3>年龄:<%=simpleBean.getAge() %></h3>
</body>
</html>

Java Servlet

Servlet Brief Introduction

Servlet 是 sun 公司提供的一门用于开发动态 web 资源的技术(这定义有点老,sun 都被 Oracle 给收购了)。

Sun 公司在其 API 中提供了一个 servlet 接口,用户若想用发一个动态 web 资源(即开发一个 Java 程序向浏览器输出数据),需要完成以下 2 个步骤:

1、编写一个 Java 类,实现 servlet 接口。
2、把开发好的 Java 类部署到 web 服务器中。

按照一种约定俗成的称呼习惯,通常我们也把实现了 servlet 接口的 java 程序,称之为 Servlet

Servlet 运行过程

Servlet 程序是由 WEB 服务器调用,web 服务器收到客户端的 Servlet 访问请求后:
① Web 服务器首先检查是否已经装载并创建了该 Servlet 的实例对象。如果是,则直接执行第 ④ 步,否则,执行第②步。
② 装载并创建该 Servlet 的一个实例对象。
③ 调用 Servlet 实例对象的 init() 方法。
④ 创建一个用于封装 HTTP 请求消息的 HttpServletRequest 对象和一个代表 HTTP 响应消息的 HttpServletResponse 对象,然后调用 Servlet 的 service() 方法并将请求和响应对象作为参数传递进去。
⑤ WEB应用程序被停止或重新启动之前,Servlet 引擎将卸载 Servlet ,并在卸载之前调用 Servlet 的 destroy() 方法。

eclipse 创建过程

在 eclipse 下,右击项目,弹出菜单,选择 New -> Servlet,填写源文件夹、包名、类名之后,就可以直接生成了,默认继承 javax.servlet.http.HttpServlet

对比网上的教程,发现了一个细节差别。

之前版本里在添加了 Servlet 之后,需要修改 web.xml,添加该 Servlet 的定义配置。demo 如下:

<servlet>  
    <description>This is the description of my J2EE component</description>  
    <display-name>This is the display name of my J2EE component</display-name>  
    <servlet-name>HelloServlet</servlet-name>  
    <servlet-class>com.hhl.servlet.HelloServlet</servlet-class>  
</servlet>  
  
<servlet-mapping>  
    <servlet-name>HelloServlet</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  

servlet 中定义了名称和实现类,servlet-mapping 中的 url-pattern 则定义了 web 访问地址,有点 ThinkPHP 框架里 route.php 文件的意思。可以自定义成 Hello、hello.do、123.html 等形式,也可以使用通配符 * 。

而在我本地下载了新版本 eclipse java ee 后,创建过程中可以直接定义 url-pattern 等项目,默认为 Servlet 类名。通过这种方式创建出来的 Servlet 类多了一个注解:@WebServlet(description = "Test servlet functionality", urlPatterns = { "/HelloServlet" }),不必再去修改 web.xml 文件,直接在代码中就可以定义 url-pattern 了。 查看了一下 WebServlet 注解的定义,retention 属性值为 runtime,这意味着,注解到运行环境一直生效。

There are two options for defining web application metadata. Prior to version 3.0 of the servlet specification, metadata resided in a deployment descriptor called web.xml in the WEB-INF folder of the project. Since 3.0, the metadata can be defined using annotations.

查找资料发现,注解的使用跟引入的 servlet-api 依赖有关。servlet-api 3.0 以上,可以使用注解定义元数据,3.0 以下的,需要在部署描述符 web.xml 文件中定义。

Servlet 与普通 Java 类的区别

Servlet 是一个供其他 Java 程序( Servlet 引擎)调用的 Java 类,它不能独立运行,它的运行完全由 Servlet 引擎来控制和调度。

针对客户端的多次 Servlet 请求,通常情况下,服务器只会创建一个 Servlet 实例对象,也就是说 Servlet 实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至 web 容器退出,servlet 实例对象才会销毁。(注)
  
在 Servlet 的整个生命周期内,Servlet 的 init 方法只被调用一次。而对一个 Servlet 的每次访问请求都导致 Servlet引擎调用一次 servlet 的 service 方法。对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个新的 HttpServletResponse 响应对象,然后将这两个对象作为参数传递给它调用的 Servlet 的 service() 方法,service 方法再根据请求方式分别调用 doXXX 方法。

Servlet 的线程安全问题

由于 Servlet 的特性,多次 Servlet 请求,只会创建一个 Servelet 实例对象,直到 web 容器退出(比如:tomcat 关闭,重启),Servlet 实例对象才会销毁。所以 Servlet 可能会存在线程安全问题,即多个线程请求同一个 Servlet 对象中的资源。

存在线程安全的 servlet demo

package com.seasidecrab.test;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ValidateServlet
 */
@WebServlet(description = "Validate Servlet functionality", urlPatterns = { "/ValidateServlet" })
public class ValidateServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public int i = 1;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ValidateServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        i++;
        try {
            Thread.sleep(1000 * 4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("中国万岁!<br/>");
        response.getWriter().write("i=" + i);
        response.getWriter().append("<h3>Hello, Jason Li!</h3>").append("Served at: ").append(request.getContextPath());
    
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

线程安全解决方案

  1. 使用 synchronized (this) {} 包裹可能存在线程安全的代码块。

现在这种做法是给 Servlet 对象加了一把锁,保证任何时候都只有一个线程在访问该 Servlet 对象里面的资源,这样就不存在线程安全问题了。

这种做法虽然解决了线程安全问题,但是编写 Servlet 却万万不能用这种方式处理线程安全问题,假如有 9999 个人同时访问这个Servlet,那么这 9999 个人必须按先后顺序排队轮流访问。

2.让 Servlet 去实现一个 SingleThreadModel 接口,如果某个 Servlet 实现了 SingleThreadModel 接口,那么 Servlet 引擎将以单线程模式来调用其 service 方法。

对于实现了 SingleThreadModel 的接口的 Servlet 中,Servlet 的引擎仍然支持对该 Servlet 的的多线程并发访问,其采用的方式是产生多个 Servlet 的实例对象,并发的每个线程分别调用一个独立的 Servlet 的实例对象。

实现了 SingleThreadModel 接口并不能真正解决的 Servlet 的线程安全问题,因为 Servlet 的引擎会创建多个 Servlet 的实例对象,而真正意义上解决多线程安全问题是指一个 Servlet 实例对象被多线程同时调用的问题。事实上,在 Servlet API 2.4中,已经将 SingleThreadModel 标记为 Deprecated(过时的)。

3.不用成员变量。

不使用成员变量就不会存在多个资源被同时请求的问题了。

  1. atomic 包装类枷锁

不了解,有待补充。

5.使用框架

Struct MVC 或者 Spring 等框架都内部解决这个问题。解决方式?不了解。

参考:
JavaBean简单及使用 - jack_Meng - 博客园
servlet到底是什么_森林体系-CSDN博客