目录
1、基于 Servlet Controller 的应用程序设计
2、基于 Filter Dispatcher 的应用程序设计
3、使用表单验证器
4、添加数据库访问
5、依赖注入
在该模型中,用一个 Servlet 或者过滤器充当控制器 Controller。所有现代的 Web 框架都是基于此模型实现的。像 Struts 1 和 Spring MVC 这类框架是在它们的 MVC 架构中使用一个 Servlet Controller,而另一个流行的框架:Struts 2,则是使用过滤器。
应用程序架构,MVC(Model-View-Controller)设计模式:
1、基于 Servlet Controller 的应用程序设计
本次实现的应用程序可以用来输入产品信息,并可以提交输入信息。之后,应用程序会给用户发送一个确认页面,显示所保存产品的详细信息。//是的,这个程序就是如此简单,它完全基于 Servlet/JSP 实现
这个应用程序能够执行下面这两个 Action:
- 显示 Add Product 表单。这个 Action 将登录表单发送到浏览器。调用这个 Action 的 URI 必须包含字符串 product_input。
- 保存产品,并返回一个产品的确认页面。调用这个 Action 的 URI 必须包含字符串 product_save。
此应用程序的目录结构如下图所示://注意,此程序中没有 web.xml 文件
代码清单 Product 类:
import java.io.Serializable;
public class Product implements Serializable {
private static final long serialVersionUID = 748392348L;
private String name;
private String description;
private float price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
代码清单 ProductForm 类:
public class ProductForm {
private String name;
private String description;
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
代码清单 SaveProductAction 类:
import com.my.servlet.programmer.model.Product;
public class SaveProductAction {
public void save(Product product) {
// 将Product插入数据库
}
}
代码清单 ControllerServlet 类://非常简单的一个Action控制器,通过 URI 路径进行匹配
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.my.servlet.programmer.action.SaveProductAction;
import com.my.servlet.programmer.form.ProductForm;
import com.my.servlet.programmer.model.Product;
/**
* 基于Servlet的控制器实现
*/
@WebServlet(name = "ControllerServlet", urlPatterns = {"/product_input", "/product_save"})
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1579L;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
private void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//uri格式:/contextName/resourceName, 例如: /app/product_input
//默认情况下,contextName为空,所以contextName为空情况下,资源路径格式为:/resourceName
String uri = request.getRequestURI();
int lastIndex = uri.lastIndexOf("/");
String action = uri.substring(lastIndex + 1);
//1-通过URI路径进行匹配,执行一个Action
if (action.equals("product_input")) {
} else if (action.equals("product_save")) {
// 创建表单
ProductForm productForm = new ProductForm();
productForm.setName(request.getParameter("name"));
productForm.setDescription(request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
// 创建对象
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(productForm.getDescription());
try {
product.setPrice(Float.parseFloat(productForm.getPrice()));
} catch (NumberFormatException e) {
}
// 执行一个Action方法
SaveProductAction saveProductAction = new SaveProductAction();
saveProductAction.save(product);
// 将模型存储在视图的作用域变量中
request.setAttribute("product", product);
}
// 2-转发到视图
String dispatchUrl = null;
if (action.equals("product_input")) {
dispatchUrl = "/jsp/ProductForm.jsp";
} else if (action.equals("product_save")) {
dispatchUrl = "/jsp/ProductDetails.jsp";
}
if (dispatchUrl != null) {
RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl);
rd.forward(request, response);
}
}
}
层叠样式表 main.css 代码:
#global {
text-align: left;
border: 1px solid #dedede;
background: #efefef;
width: 300px;
padding: 20px;
margin: 100px auto;
}
JSP 页面 ProductDetails.jsp 代码:
<!DOCTYPE HTML>
<html>
<head>
<title>Save Product</title>
<style type="text/css">@import url(/css/main.css);</style>
</head>
<body>
<div id="global">
<h4>The product has been saved.</h4>
<p>
<h5>Details:</h5>
Product Name: ${product.name}<br/>
Description: ${product.description}<br/>
Price: $${product.price}
</p>
</div>
</body>
</html>
JSP 页面 ProductForm.jsp 代码:
<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">@import url(/css/main.css);</style>
</head>
<body>
<div id="global">
<h3>Add a product</h3>
<form method="post" action="product_save">
<table>
<tr>
<td>Product Name:</td>
<td><input type="text" name="name"/></td>
</tr>
<tr>
<td>Description:</td>
<td><input type="text" name="description"/></td>
</tr>
<tr>
<td>Price:</td>
<td><input type="text" name="price"/></td>
</tr>
<tr>
<td><input type="reset"/></td>
<td><input type="submit" value="Add Product"/></td>
</tr>
</table>
</form>
</div>
</body>
</html>
程序启动后的跳转主页 index.html:
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Apache Tomcat Examples</title>
</head>
<body>
<h3>
welcome to servlet !!!
</H3>
</body>
</html>
Maven 工程的 pom.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>servlet-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<!--javax.servlet-api-->
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
以上,便是该应用程序的所有代码。接下来,启动一个 tomcat 容器,访问产品输入页面(/product_input),成功访问后,页面如下图所示:
在页面的表单中输入相关数据后,点击 Add Product,会跳转到产品详情页,如下图所示:
2、基于 Filter Dispatcher 的应用程序设计
虽然 Servlet 是 MVC 应用程序中最常用的 Controller,但也可以用过滤器作为 Controller。不过注意,过滤器无权充当欢迎页面。因为只输入域名将不会调用过滤器分发器(Filter Dispatcher)。Struts 2 用过滤器作为 Controller,因为过滤器还用于提供静态的内容。//使用过滤器实现一个控制器
在基于 Servlet Controller 的应用程序设计上,只需对代码内容进行一些简单的更改:即注销 ControllerServlet,添加 DispatherFilter。
代码清单 DispatherFilter 类://对全局进行拦截,根据路径进行匹配
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import com.my.servlet.programmer.action.SaveProductAction;
import com.my.servlet.programmer.form.ProductForm;
import com.my.servlet.programmer.model.Product;
/**
* 使用过滤器实现Controller
*/
@WebFilter(filterName = "DispatcherFilter", urlPatterns = {"/*"})
public class DispatcherFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String uri = req.getRequestURI();
int lastIndex = uri.lastIndexOf("/");
String action = uri.substring(lastIndex + 1);
//1-通过URI路径进行匹配,执行一个Action
if (action.equals("product_input")) {
// do nothing
} else if (action.equals("product_save")) {
// 创建表单
ProductForm productForm = new ProductForm();
productForm.setName(request.getParameter("name"));
productForm.setDescription(request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
// 创建对象
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(productForm.getDescription());
try {
product.setPrice(Float.parseFloat(productForm.getPrice()));
} catch (NumberFormatException e) {
}
// 执行一个Action方法
SaveProductAction saveProductAction = new SaveProductAction();
saveProductAction.save(product);
request.setAttribute("product", product);
}
// 2-转发到视图
String dispatchUrl = null;
if (action.equals("product_input")) {
dispatchUrl = "/jsp/ProductForm.jsp";
} else if (action.equals("product_save")) {
dispatchUrl = "/jsp/ProductDetails.jsp";
}
if (dispatchUrl != null) {
RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl);
rd.forward(request, response);
} else {
// 让静态内容通过
filterChain.doFilter(request, response);
}
}
}
可以发现,除了基于过滤器的实现,其他代码逻辑基本上与 ControllerServlet 类一致,当然,基本的操作也与 ControllerServlet 类一样,浏览器操作对后端代码的变化并无感知,在此不做赘述。//简单来说,就是替换了控制器的实现,基于过滤器的拦截,需要考虑到过滤器的顺序
由于过滤器的目标是所有包含静态内容的 URL,因此如果没有调用任何 Action,那么就需要调用 filterChain.doFilter() 方法。
3、使用表单验证器
在执行 Action 时,输入验证是一个重要的步骤。验证范围从简单的任务到复杂的都有,例如,简单的有检验某个输入域中是否有值,复杂的有验证信用卡号码,等等。现代的 MVC 框架经常同时提供编程式和声明式的验证方法。在编程式验证中,是通过编写代码来验证用户的输入。在声明式验证中,则是在 XML 文档或者属性文件中提供验证规则。
接下来,为 Servlet 程序提供一个简单的验证器,用来校验页面输入的值。ProductValidator 类提供了一个 ProductForm 的 validate 方法。验证器可以确保产品的名称不为空,以及其价格是一个有效的数字。validate 方法返回个包含验证错误消息的 String 的 List。List 为空意味着验证成功。
代码清单 ProductValidator 类:
import java.util.ArrayList;
import java.util.List;
import com.my.servlet.programmer.form.ProductForm;
/**
* 校验器
*/
public class ProductValidator {
// 字段校验逻辑
public List<String> validate(ProductForm productForm) {
List<String> errors = new ArrayList<>();
String name = productForm.getName();
if (name == null || name.trim().isEmpty()) {
errors.add("产品名称不能为空");
}
String price = productForm.getPrice();
if (price == null || price.trim().isEmpty()) {
errors.add("产品价格不能为空");
} else {
try {
Float.parseFloat(price);
} catch (NumberFormatException e) {
errors.add("无效价格");
}
}
return errors;
}
}
在此次示例程序中,仍然使用 Servlet 作为控制器(即放弃过滤器的控制器实现方式),定义完校验器 ProductValidator 后,需要在 ControllerServlet 中引入校验器的逻辑,完整的代码清单如下://在原有基础上增加了校验逻辑
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.my.servlet.programmer.action.SaveProductAction;
import com.my.servlet.programmer.form.ProductForm;
import com.my.servlet.programmer.model.Product;
import com.my.servlet.programmer.validator.ProductValidator;
@WebServlet(name = "ControllerServlet", urlPatterns = {"/product_input", "/product_save"})
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 98279L;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
private void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String uri = request.getRequestURI();
int lastIndex = uri.lastIndexOf("/");
String action = uri.substring(lastIndex + 1);
String dispatchUrl = null;
//1-根据Url进行匹配
if (action.equals("product_input")) {
dispatchUrl = "/jsp/ProductForm.jsp";
} else if (action.equals("product_save")) {
//实例化表单
ProductForm productForm = new ProductForm();
productForm.setName(request.getParameter("name"));
productForm.setDescription(request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
//表单校验器
ProductValidator productValidator = new ProductValidator();
List<String> errors = productValidator.validate(productForm);
if (errors.isEmpty()) {
//表单校验通过创建产品
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(productForm.getDescription());
product.setPrice(Float.parseFloat(productForm.getPrice()));
//保存产品信息
SaveProductAction saveProductAction = new SaveProductAction();
saveProductAction.save(product);
request.setAttribute("product", product);
dispatchUrl = "/jsp/ProductDetails.jsp";
} else {
//如果校验失败,向页面返回错误信息
request.setAttribute("errors", errors);
request.setAttribute("form", productForm);
dispatchUrl = "/jsp/ProductForm.jsp";
}
}
// 2-重定向
if (dispatchUrl != null) {
RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl);
//解决输出到页面的中文乱码
response.setCharacterEncoding("UTF-8");
rd.forward(request, response);
}
}
}
为了在页面展示错误信息,相应的也需要添加一些页面的逻辑,本次示例只要修改 ProductForm.jsp 页面即可。
代码清单 ProductForm.jsp 页面:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page contentType="text/html; charset=utf-8"%> <!--解决页面显示中文乱码-->
<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">@import url(/css/main.css);</style>
</head>
<body>
<div id="global">
<h3>Add a product</h3>
<!--错误信息展示-->
<c:if test="${requestScope.errors != null}">
<p id="errors">
错误信息:
<ul>
<c:forEach var="error" items="${requestScope.errors}">
<li>${error}</li>
</c:forEach>
</ul>
</p>
</c:if>
<form method="post" action="product_save">
<table>
<tr>
<td>Product Name:</td>
<td><input type="text" name="productName"
value="${form.name}"/></td>
</tr>
<tr>
<td>Description:</td>
<td><input type="text" name="description"
value="${form.description}"/></td>
</tr>
<tr>
<td>Price:</td>
<td><input type="text" name="price"
value="${form.price}"/></td>
</tr>
<tr>
<td><input type="reset"/></td>
<td><input type="submit" value="Add Product"/></td>
</tr>
</table>
</form>
</div>
</body>
</html>
在上边 JSP 页面中,引入了 jstl 标签库。因此,还需在 pom.xml 文件中引入相应的 jar 包,完整的 pom.xml 文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>servlet-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<!--javax.servlet-api-->
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--jstl标签库-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
最后,此示例程序的完整目录结构如下:
接下来,启动 tomcat 容器,访问产品输入页面(/product_input),成功访问后,不输入任何信息便点击 Add Product 按钮,将显示如下页面://校验器生效
4、添加数据库访问
在数据库中访问数据时,最重要且最费时的操作经常是建立连接。按规则,设计良好的应用程序数据库连接应该始终是采用连接池的。目前常见的数据库连接池有:DBCP(Apache),C3P0, Druid(阿里),HikariCP 等。
如果使用的是 Tomcat 7 以上版本,那么它里面则已经内建了连接池(DBCP)。连接池目前并不需要亲自管理,而是让 Servlet/JSP 容器来完成。下面是利用 JNDI (Java 命名和目录接口) 查找,从容器管理的连接池中获取 JDBC 连接的代码示例。
private DataSource dataSource;
private DataSourceCache() {
Connection connection = null;
Context context = null;
try {
context = new InitialContext();
//java:comp/env/: 是JNDI树中的节点,可以在其中找到当前Java EE组件(web应用程序或EJB)的属性。
//jdbc/myServletDB: 在tomcat中配置的资源名称
dataSource = (DataSource) context.lookup("java:comp/env/jdbc/myServletDB");
connection = dataSource.getConnection();
} catch (NamingException e) {
}
}
调用 DataSource 中的 getConnection 方法比较快,因为连接永远不会被关闭;关闭连接时,只要将连接返回到池中即可。但是,JNDI 查找比较慢,因此,被返回的 DataSource
经常会被缓存起来。
为了让 Servlet 容器来管理连接池,需要对容器进行配置。在 Tomcat 中,是通过在应用程序的 Context 元素下声明 Resource 元素来实现的。例如,下面的 Tomcat 上下文中就包含了一个带有内部连接池的 DataSource 资源。//context.xml文件在tomcat的conf目录下
<Context>
<Resource name="jdbc/myServletDB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/servlet_db?useUnicode=true&serverTimezone=GMT-8&characterEncoding=UTF-8&useSSL=false"/>
</Context>
虽然目前的应用程序大多是利用一个依赖注入框架来管理数据库连接,但是也有许多的应用程序仍然还是依赖早期的 JNDI 查找方法,因此尝试 JNDI 查找仍然具有意义。
关于在 Tomcat 中配置数据库连接,可以查看官方文档的一些介绍,点击这里。
DAO(Data Access Objet,数据访问对象)设计模式:在数据库中访问数据的一种办法是单独利用一个模块来管理获得连接和构建 SQL 语句的代码复杂性。DAO 模式就是用来很好地完成这项工作。下边是本次程序设计中使用到的 DAO 模式://这个图很有用,尤其是理解后边的依赖注入
添加了数据库连接以及 DAO 类后,就可以演示一个简单的 Servlet 数据库存储程序了,因为前边已经贴了不少代码,本次 Servlet 程序的完整项目代码,可以直接点击这个链接进行下载。
代码下载后,本地需要有 Tomcat 以及安装 Mysql 数据库,修改成本地配置后即可运行。
5、依赖注入
在本次程序设计中,为了便于理解,使用 Dependencylnjector 类代替依赖注入框架。(在现实的应用程序中,可以选择使用某种适当的框架。) 这个类是专门为本次程序设计的,有没有容器都很容易被实例化。一旦被实例化,就必须调用它的 start 方法,准备一个可供其他组件使用的 C3PO 连接池。用完之后,应该调用它的 shutdown 方法。
代码清单 Dependencylnjector 类
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
import com.my.servlet.programmer.action.GetProductsAction;
import com.my.servlet.programmer.action.SaveProductAction;
import com.my.servlet.programmer.dao.DataSourceCache;
import com.my.servlet.programmer.dao.ProductDAO;
import com.my.servlet.programmer.dao.ProductDAOImpl;
import com.my.servlet.programmer.validator.ProductValidator;
/**
* 依赖注入
*/
public class DependencyInjector {
private DataSource dataSource;
/**
* 创建一个数据库连接池
*/
public void start() {
// dataSource = defaultDatasource();
dataSource = c3p0DataSource();
}
/**
* 默认数据源
*/
private DataSource defaultDatasource() {
return DataSourceCache.getInstance().getDataSource();
}
/**
* C3P0数据源
*/
private DataSource c3p0DataSource() {
// C3P0 dataSource
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
cpds.setJdbcUrl(
"jdbc:mysql://localhost:3306/servlet_db?useUnicode=true&serverTimezone=GMT-8&characterEncoding=UTF-8&useSSL=false");
cpds.setUser("root");
cpds.setPassword("root");
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
return cpds;
}
/**
* 销毁一个数据库连接池
*/
public void shutDown() {
try {
DataSources.destroy(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 根据类型创建类型实例
*/
public Object getObject(Class type) {
if (type == ProductValidator.class) {
return new ProductValidator();
} else if (type == ProductDAO.class) {
return createProductDAO();
} else if (type == GetProductsAction.class) {
return createGetProductsAction();
} else if (type == SaveProductAction.class) {
return createSaveProductAction();
}
return null;
}
private GetProductsAction createGetProductsAction() {
GetProductsAction getProductsAction = new GetProductsAction();
// 向 getProductsAction 中注入 ProductDAO
getProductsAction.setProductDAO(createProductDAO());
return getProductsAction;
}
private SaveProductAction createSaveProductAction() {
SaveProductAction saveProductAction = new SaveProductAction();
// 向 saveProductAction 中注入 ProductDAO
saveProductAction.setProductDAO(createProductDAO());
return saveProductAction;
}
private ProductDAO createProductDAO() {
ProductDAO productDAO = new ProductDAOImpl();
// 向 productDAO 注入DataSource
productDAO.setDataSource(dataSource);
return productDAO;
}
}
在 Servlet Controller 的 init 方法中实例化 Dependencylnjector,并在它的 destroy 方法中调用 Dependencylnjector 的 shutdown方法。Servlet 不再创建自己的依赖,而是从 Dependencylnjector 处获得。
代码清单 ControllerServlet 类:
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.my.servlet.programmer.action.GetProductsAction;
import com.my.servlet.programmer.action.SaveProductAction;
import com.my.servlet.programmer.form.ProductForm;
import com.my.servlet.programmer.model.Product;
import com.my.servlet.programmer.util.DependencyInjector;
import com.my.servlet.programmer.validator.ProductValidator;
/**
* 控制器实现
*/
@WebServlet(name = "ControllerServlet", urlPatterns = {"/product_input", "/product_save", "/product_list"})
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 6679L;
private DependencyInjector dependencyInjector;
@Override
public void init() {
//初始化依赖注入器
System.out.println("start init...");
dependencyInjector = new DependencyInjector();
dependencyInjector.start();
System.out.println("end init...");
}
@Override
public void destroy() {
dependencyInjector.shutDown();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
private void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String uri = request.getRequestURI();
int lastIndex = uri.lastIndexOf("/");
String action = uri.substring(lastIndex + 1);
String dispatchUrl = null;
//根据url路径进行匹配
if (action.equals("product_input")) {
dispatchUrl = "/jsp/ProductForm.jsp";
} else if (action.equals("product_save")) {
//1-实例化表单
request.setCharacterEncoding("UTF-8"); //防止中文乱码
ProductForm productForm = new ProductForm();
productForm.setName(request.getParameter("name"));
productForm.setDescription(request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
//2-依赖注入校验器
ProductValidator productValidator = (ProductValidator) dependencyInjector.getObject(ProductValidator.class);
List<String> errors = productValidator.validate(productForm);
if (errors.isEmpty()) {
//3-创建product实例
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(productForm.getDescription());
product.setPrice(Float.parseFloat(productForm.getPrice()));
//4-依赖注入存储Action
SaveProductAction saveProductAction = (SaveProductAction) dependencyInjector.getObject(SaveProductAction.class);
saveProductAction.save(product);
request.setAttribute("product", product);
dispatchUrl = "/jsp/ProductDetails.jsp";
} else {
request.setAttribute("form", productForm);
request.setAttribute("errors", errors);
dispatchUrl = "/jsp/ProductForm.jsp";
}
} else if (action.equals("product_list") || action.isEmpty()) {
//依赖注入查询Action
GetProductsAction getProductsAction = (GetProductsAction) dependencyInjector.getObject(GetProductsAction.class);
List<Product> products = getProductsAction.getProducts();
request.setAttribute("products", products);
dispatchUrl = "/jsp/ProductList.jsp";
}
// 重定向
if (dispatchUrl != null) {
RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl);
response.setCharacterEncoding("UTF-8");
rd.forward(request, response);
}
}
}
至此,一个简单的依赖注入就做好了,其功能演示并无变化,所以不做过多赘述。
之前通过 ProductDAOImpl 的 getConnection 方法获取的连接是来自一个由容器维护的 JNDI 对象,因此,如果要想测试 DAO 对象,就必须先将整个应用程序部署在一个容器中,并用一个浏览器输入 DAO 对象的值。这么做将损失很大的生产效率,所以在本在程序设计中通过依赖注入替换成了 C3P0 的 DataSource,因为有了依赖注射器,程序中的每一个组件都可以独立进行测试。
本次 Servlet 程序的完整项目代码,可以直接点击这个链接进行下载。
至此,全文介绍完毕。