ZetCode

Java Servlet Weld

最后修改于 2020 年 7 月 13 日

Java Servlet Weld 教程展示了如何在 Java Servlet 中使用 Weld 进行依赖注入。我们使用 Weld 和 Tomcat Web 服务器。

依赖注入

依赖注入 (DI) 是一种将一个对象的依赖项提供给另一个对象的技术。在 Java 应用程序中,它是一个特定的库,将依赖项注入到类中。DI 的主要好处是松耦合和易用性。DI 使类更内聚,因为它们承担的责任更少。

Java EE 通过引入上下文和依赖注入 (CDI) 规范,标准化了依赖注入。它是依赖注入和上下文生命周期管理的标准。

@Inject 注解

@Inject 注解用于将依赖注入到 Java 类中。要注入的依赖项可以选择性地用 @ManagedBean 注解进行装饰。

Weld

Weld 是 Java EE 平台 CDI 的参考实现。Weld 集成了许多 Java EE 应用服务器,如 WildFly、JBoss、GlassFish 等。Weld 也可以在纯 Servlet 容器(Tomcat、Jetty)或 Java SE 中使用。

Java Servlet Weld 示例

在下面的 Web 应用程序中,我们创建一个 Servlet,它在一个 HTML 文件中返回一个城市对象列表。在该应用程序中,我们在 Weld 库的帮助下使用依赖注入。

$ tree
.
├── nb-configuration.xml
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── bean
    │   │           │   └── City.java
    │   │           ├── dao
    │   │           │   ├── CityDao.java
    │   │           │   └── ICityDao.java
    │   │           ├── service
    │   │           │   ├── CityService.java
    │   │           │   └── ICityService.java
    │   │           └── web
    │   │               └── GetCities.java
    │   └── webapp
    │       ├── index.html
    │       ├── listCities.jsp
    │       ├── META-INF
    │       │   └── context.xml
    │       └── WEB-INF
    │           └── beans.xml
    └── test
        └── java

这是项目结构。

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>com.zetcode</groupId>
    <artifactId>JavaServletWeld</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>JavaServletWeld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>
               
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.jboss.weld.servlet</groupId>
            <artifactId>weld-servlet-shaded</artifactId>
            <version>3.0.2.Final</version>
        </dependency>        

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
 
        </plugins>
    </build>

</project>

这是 Maven POM 文件。javax.servlet-api 构件用于 Servlet。weld-servlet-shaded 依赖项使 Weld 能够在 Servlet 容器中运行。jstl 将 JSTL 库添加到项目中。JSTL 包含一组用于 JSP 应用程序的有用标签。maven-war-plugin 负责收集 Web 应用程序的所有构件依赖项、类和资源,并将它们打包成 Web 应用程序归档文件 (WAR)。

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/JavaServletWeld">
    
    <Resource name="BeanManager" 
               auth="Container"
               type="javax.enterprise.inject.spi.BeanManager"
               factory="org.jboss.weld.resources.ManagerObjectFactory" />
     
</Context>

在 Tomcat 的 context.xml 文件中,我们定义了上下文路径并注册了 Weld 的 BeanManager 工厂。

com/zetcode/City.java
package com.zetcode.bean;

import java.util.Objects;

public class City {

    private Long id;
    private String name;
    private int population;

    public City(Long id, String name, int population) {
        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 97 * hash + Objects.hashCode(this.id);
        hash = 97 * hash + Objects.hashCode(this.name);
        hash = 97 * hash + this.population;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final City other = (City) obj;
        if (this.population != other.population) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        return Objects.equals(this.id, other.id);
    }
}

City bean 包含城市对象的数据。它有三个属性:idnamepopulation

beans.xml
<?xml version="1.0"?>
<beans 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/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">

</beans>

WEB-INF 目录下,我们有一个空的 beans.xml 文件。它是 CDI 的部署描述符。它可以用于配置拦截器、装饰器和其他东西。即使我们不做任何配置,我们也需要添加一个空的 beans.xml 来注册 CDI。

com/zetcode/ICityService.java
package com.zetcode.service;

import com.zetcode.bean.City;
import java.util.List;

public interface ICityService {
    
    public List<City> getCities();
}

ICityService 包含 getCities 合约方法。

com/zetcode/CityService.java
package com.zetcode.service;

import com.zetcode.bean.City;
import com.zetcode.dao.ICityDao;
import java.util.List;
import javax.annotation.ManagedBean;
import javax.inject.Inject;

@ManagedBean
public class CityService implements ICityService {
    
    @Inject
    private ICityDao cityDao;
    
    @Override
    public List<City> getCities() {

        return cityDao.findAll();
    }
}

CityService 包含 ICityService 接口的实现。服务类调用 DAO 对象的该方法,DAO 对象是数据库的中间层。

@ManagedBean
public class CityService implements ICityService {

@ManagedBean 是一个可选注解,表示 CityService 将被 Weld 管理。

@Inject
private ICityDao cityDao;

使用 @Inject 注解,我们将 CityDao 注入到 cityDao 属性中。

com/zetcode/ICityDao.java
package com.zetcode.dao;

import com.zetcode.bean.City;
import java.util.List;

public interface ICityDao {
    
    public List<City> findAll();
}

这里是我们 DAO 的 findAll 合约方法。

com/zetcode/CityDao.java
package com.zetcode.dao;

import com.zetcode.bean.City;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.ManagedBean;

@ManagedBean
public class CityDao implements ICityDao {

    @Override
    public List<City> findAll() {
        
        List<City> cities = new ArrayList<>();
        
        cities.add(new City(1L, "Bratislava", 432000));
        cities.add(new City(2L, "Budapest", 1759000));
        cities.add(new City(3L, "Prague", 1280000));
        cities.add(new City(4L, "Warsaw", 1748000));
        cities.add(new City(5L, "Los Angeles", 3971000));
        cities.add(new City(6L, "New York", 8550000));
        cities.add(new City(7L, "Edinburgh", 464000));
        cities.add(new City(8L, "Berlin", 3671000));
        
        return cities;        
    }
}

CityDao 包含 findAll DAO 方法的实现。出于简单性,我们不连接数据库,而是简单地返回一个 City 对象列表。

@ManagedBean
public class CityDao implements ICityDao {

CityDao 也是一个托管 bean。

com/zetcode/GetCities.java
package com.zetcode.web;

import com.zetcode.bean.City;
import com.zetcode.service.ICityService;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
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;

@WebServlet(name = "GetCities", urlPatterns = {"/GetCities"})
public class GetCities extends HttpServlet {

    @Inject
    ICityService cityService;
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("application/html;charset=UTF-8");

        List<City> cities = cityService.getCities();
        request.setAttribute("cities", cities);
        
        RequestDispatcher dispatcher = request.getRequestDispatcher("listCities.jsp");
        dispatcher.forward(request, response);
    }
}

GetCities Servlet 调用城市服务的 getCities 方法,并响应一个 HTML 页面,其中包含一个 HTML 表格中的所有城市。

@WebServlet(name = "GetCities", urlPatterns = {"/GetCities"})

该 Java 类被 @WebServlet 注解装饰。它被映射到 GetCities URL 模式。

@Inject
ICityService cityService;

使用 @Inject 注解,我们将 CityService 注入到 CityService 属性中。

response.setContentType("application/html;charset=UTF-8");

Servlet 将以 HTML 输出数据,数据的编码设置为 UTF-8。

List<City> cities = cityService.getCities();
request.setAttribute("cities", cities);

使用 CityServicegetCities,我们检索所有城市。

request.setAttribute("cities", cities);

我们将列表设置为请求属性。

RequestDispatcher dispatcher = request.getRequestDispatcher("listCities.jsp");
dispatcher.forward(request, response);

使用 RequestDispatcher,我们将处理发送到 listCities.jsp 页面。这是一个模板文件,它将数据与 HTML 结合起来,并为客户端生成最终输出。

listCities.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Cities</title>
    </head>
    <body>
        <h2>Cities</h2>
        
        <table>
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                    <th>Population</th>
                </tr>
            </thead>
            
            <tbody>
                <c:forEach items="${cities}" var="city">
                <tr>
                    <td>${city.id}</td>
                    <td>${city.name}</td>
                    <td>${city.population}</td>
                </tr>
                </c:forEach>   
            </tbody>
        </table>
    </body>
</html>

listCities.jsp 使用 c:forEach 标签从提供的数据生成 HTML 表格。

index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <a href="GetCities">GetCities</a>
    </body>
</html>

这是主页。它包含一个调用 Servlet 的链接。

在本教程中,我们展示了如何将 Weld 库包含在 Java Servlet Web 应用程序中,并使用它来管理依赖项。