文章出處

CDI(Contexts and Dependency Injection 上下文依賴注入),是JAVA官方提供的依賴注入實現,可用于Dynamic Web Module中,先給3篇老外的文章,寫得很不錯

1、Java EE CDI Dependency Injection (@Inject) tutorial
2、Java EE CDI Producer methods tutorial
3、Java EE CDI bean scopes

此外,還有jboss官方的參考文檔:http://docs.jboss.org/weld/reference/latest/en-US/html/

如果不想啃洋文,也可以繼續往下看:

一、基本的Inject注入

1.1 在eclipse中先創建一個常規的maven Dynamic Web項目(不熟悉maven的,可以先看看這里),下面是完整的項目截圖

里面各package的代碼,后面會給出。 項目的屬性中,注意有幾個屬性要勾上(默認情況下,應該已經自動勾上了),如下圖:

上圖右側的圓圈,其實就是CDI 1.0使用的先決條件。

Pom.xml的內容如下:

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <!--
  3     JBoss, Home of Professional Open Source
  4     Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
  5     contributors by the @authors tag. See the copyright.txt in the
  6     distribution for a full listing of individual contributors.
  7 
  8     Licensed under the Apache License, Version 2.0 (the "License");
  9     you may not use this file except in compliance with the License.
 10     You may obtain a copy of the License at
 11     http://www.apache.org/licenses/LICENSE-2.0
 12     Unless required by applicable law or agreed to in writing, software
 13     distributed under the License is distributed on an "AS IS" BASIS,
 14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15     See the License for the specific language governing permissions and
 16     limitations under the License.
 17 -->
 18 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 19     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 20     <modelVersion>4.0.0</modelVersion>
 21 
 22     <groupId>cnblogs</groupId>
 23     <artifactId>cdi-web-sample</artifactId>
 24     <version>0.0.1-SNAPSHOT</version>
 25     <packaging>war</packaging>
 26     <name>cdi-web-sample</name>
 27     <description>A starter Java EE 6 webapp project for use on JBoss AS 7 / EAP 6, generated from the jboss-javaee6-webapp archetype</description>
 28 
 29     <url>http://jboss.org/jbossas</url>
 30     <licenses>
 31         <license>
 32             <name>Apache License, Version 2.0</name>
 33             <distribution>repo</distribution>
 34             <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
 35         </license>
 36     </licenses>
 37 
 38     <properties>
 39         <!-- Explicitly declaring the source encoding eliminates the following 
 40             message: -->
 41         <!-- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered 
 42             resources, i.e. build is platform dependent! -->
 43         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 44 
 45         <!-- JBoss dependency versions -->
 46         <version.jboss.maven.plugin>7.4.Final</version.jboss.maven.plugin>
 47 
 48         <!-- Define the version of the JBoss BOMs we want to import to specify 
 49             tested stacks. -->
 50         <version.jboss.bom>1.0.7.Final</version.jboss.bom>
 51         <!-- Alternatively, comment out the above line, and un-comment the line
 52             below to use version 1.0.4.Final-redhat-4 which is a release certified to
 53             work with JBoss EAP 6. It requires you have access to the JBoss EAP 6
 54             maven repository. -->
 55         <!-- <version.jboss.bom>1.0.4.Final-redhat-4</version.jboss.bom>> -->
 56 
 57         <!-- other plugin versions -->
 58         <version.surefire.plugin>2.10</version.surefire.plugin>
 59         <version.war.plugin>2.1.1</version.war.plugin>
 60 
 61         <!-- maven-compiler-plugin -->
 62         <maven.compiler.target>1.6</maven.compiler.target>
 63         <maven.compiler.source>1.6</maven.compiler.source>
 64     </properties>
 65 
 66 
 67     <dependencyManagement>
 68         <dependencies>
 69             <!-- JBoss distributes a complete set of Java EE 6 APIs including a Bill 
 70                 of Materials (BOM). A BOM specifies the versions of a "stack" (or a collection) 
 71                 of artifacts. We use this here so that we always get the correct versions 
 72                 of artifacts. Here we use the jboss-javaee-6.0-with-tools stack (you can 
 73                 read this as the JBoss stack of the Java EE 6 APIs, with some extras tools 
 74                 for your project, such as Arquillian for testing) and the jboss-javaee-6.0-with-hibernate 
 75                 stack you can read this as the JBoss stack of the Java EE 6 APIs, with extras 
 76                 from the Hibernate family of projects) -->
 77             <dependency>
 78                 <groupId>org.jboss.bom</groupId>
 79                 <artifactId>jboss-javaee-6.0-with-tools</artifactId>
 80                 <version>${version.jboss.bom}</version>
 81                 <type>pom</type>
 82                 <scope>import</scope>
 83             </dependency>
 84             <dependency>
 85                 <groupId>org.jboss.bom</groupId>
 86                 <artifactId>jboss-javaee-6.0-with-hibernate</artifactId>
 87                 <version>${version.jboss.bom}</version>
 88                 <type>pom</type>
 89                 <scope>import</scope>
 90             </dependency>
 91         </dependencies>
 92     </dependencyManagement>
 93 
 94     <dependencies>
 95 
 96         <!-- First declare the APIs we depend on and need for compilation. All 
 97             of them are provided by JBoss AS 7 -->
 98 
 99         <!-- Import the CDI API, we use provided scope as the API is included in 
100             JBoss AS 7 -->
101         <dependency>
102             <groupId>javax.enterprise</groupId>
103             <artifactId>cdi-api</artifactId>
104             <scope>provided</scope>
105         </dependency>
106 
107         <!-- Import the Common Annotations API (JSR-250), we use provided scope 
108             as the API is included in JBoss AS 7 -->
109         <dependency>
110             <groupId>org.jboss.spec.javax.annotation</groupId>
111             <artifactId>jboss-annotations-api_1.1_spec</artifactId>
112             <scope>provided</scope>
113         </dependency>
114 
115         <!-- Import the JAX-RS API, we use provided scope as the API is included 
116             in JBoss AS 7 -->
117         <dependency>
118             <groupId>org.jboss.spec.javax.ws.rs</groupId>
119             <artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
120             <scope>provided</scope>
121         </dependency>
122 
123         <!-- Import the JPA API, we use provided scope as the API is included in 
124             JBoss AS 7 -->
125         <dependency>
126             <groupId>org.hibernate.javax.persistence</groupId>
127             <artifactId>hibernate-jpa-2.0-api</artifactId>
128             <scope>provided</scope>
129         </dependency>
130 
131         <!-- Import the EJB API, we use provided scope as the API is included in 
132             JBoss AS 7 -->
133         <dependency>
134             <groupId>org.jboss.spec.javax.ejb</groupId>
135             <artifactId>jboss-ejb-api_3.1_spec</artifactId>
136             <scope>provided</scope>
137         </dependency>
138 
139         <!-- JSR-303 (Bean Validation) Implementation -->
140         <!-- Provides portable constraints such as @Email -->
141         <!-- Hibernate Validator is shipped in JBoss AS 7 -->
142         <dependency>
143             <groupId>org.hibernate</groupId>
144             <artifactId>hibernate-validator</artifactId>
145             <scope>provided</scope>
146             <exclusions>
147                 <exclusion>
148                     <groupId>org.slf4j</groupId>
149                     <artifactId>slf4j-api</artifactId>
150                 </exclusion>
151             </exclusions>
152         </dependency>
153 
154         <!-- Import the JSF API, we use provided scope as the API is included in 
155             JBoss AS 7 -->
156         <dependency>
157             <groupId>org.jboss.spec.javax.faces</groupId>
158             <artifactId>jboss-jsf-api_2.1_spec</artifactId>
159             <scope>provided</scope>
160         </dependency>
161 
162         <!-- Now we declare any tools needed -->
163 
164         <!-- Annotation processor to generate the JPA 2.0 metamodel classes for 
165             typesafe criteria queries -->
166         <dependency>
167             <groupId>org.hibernate</groupId>
168             <artifactId>hibernate-jpamodelgen</artifactId>
169             <scope>provided</scope>
170         </dependency>
171 
172         <!-- Annotation processor that raising compilation errors whenever constraint 
173             annotations are incorrectly used. -->
174         <dependency>
175             <groupId>org.hibernate</groupId>
176             <artifactId>hibernate-validator-annotation-processor</artifactId>
177             <scope>provided</scope>
178         </dependency>
179 
180         <!-- Needed for running tests (you may also use TestNG) -->
181         <dependency>
182             <groupId>junit</groupId>
183             <artifactId>junit</artifactId>
184             <scope>test</scope>
185         </dependency>
186 
187         <!-- Optional, but highly recommended -->
188         <!-- Arquillian allows you to test enterprise code such as EJBs and Transactional(JTA) 
189             JPA from JUnit/TestNG -->
190         <dependency>
191             <groupId>org.jboss.arquillian.junit</groupId>
192             <artifactId>arquillian-junit-container</artifactId>
193             <scope>test</scope>
194         </dependency>
195 
196         <dependency>
197             <groupId>org.jboss.arquillian.protocol</groupId>
198             <artifactId>arquillian-protocol-servlet</artifactId>
199             <scope>test</scope>
200         </dependency>
201 
202     </dependencies>
203 
204     <build>
205         <!-- Maven will append the version to the finalName (which is the name 
206             given to the generated war, and hence the context root) -->
207         <finalName>${project.artifactId}</finalName>
208         <plugins>
209             <plugin>
210                 <artifactId>maven-war-plugin</artifactId>
211                 <version>${version.war.plugin}</version>
212                 <configuration>
213                     <!-- Java EE 6 doesn't require web.xml, Maven needs to catch up! -->
214                     <failOnMissingWebXml>false</failOnMissingWebXml>
215                 </configuration>
216             </plugin>
217             <!-- The JBoss AS plugin deploys your war to a local JBoss AS container -->
218             <!-- To use, run: mvn package jboss-as:deploy -->
219             <plugin>
220                 <groupId>org.jboss.as.plugins</groupId>
221                 <artifactId>jboss-as-maven-plugin</artifactId>
222                 <version>${version.jboss.maven.plugin}</version>
223             </plugin>
224         </plugins>
225     </build>
226 
227     <profiles>
228         <profile>
229             <!-- The default profile skips all tests, though you can tune it to run 
230                 just unit tests based on a custom pattern -->
231             <!-- Seperate profiles are provided for running all tests, including Arquillian 
232                 tests that execute in the specified container -->
233             <id>default</id>
234             <activation>
235                 <activeByDefault>true</activeByDefault>
236             </activation>
237             <build>
238                 <plugins>
239                     <plugin>
240                         <artifactId>maven-surefire-plugin</artifactId>
241                         <version>${version.surefire.plugin}</version>
242                         <configuration>
243                             <skip>true</skip>
244                         </configuration>
245                     </plugin>
246                 </plugins>
247             </build>
248         </profile>
249 
250         <profile>
251             <!-- An optional Arquillian testing profile that executes tests in your 
252                 JBoss AS instance -->
253             <!-- This profile will start a new JBoss AS instance, and execute the 
254                 test, shutting it down when done -->
255             <!-- Run with: mvn clean test -Parq-jbossas-managed -->
256             <id>arq-jbossas-managed</id>
257             <dependencies>
258                 <dependency>
259                     <groupId>org.jboss.as</groupId>
260                     <artifactId>jboss-as-arquillian-container-managed</artifactId>
261                     <scope>test</scope>
262                 </dependency>
263             </dependencies>
264         </profile>
265 
266         <profile>
267             <!-- An optional Arquillian testing profile that executes tests in a remote 
268                 JBoss AS instance -->
269             <!-- Run with: mvn clean test -Parq-jbossas-remote -->
270             <id>arq-jbossas-remote</id>
271             <dependencies>
272                 <dependency>
273                     <groupId>org.jboss.as</groupId>
274                     <artifactId>jboss-as-arquillian-container-remote</artifactId>
275                     <scope>test</scope>
276                 </dependency>
277             </dependencies>
278         </profile>
279 
280         <profile>
281             <!-- When built in OpenShift the 'openshift' profile will be used when 
282                 invoking mvn. -->
283             <!-- Use this profile for any OpenShift specific customization your app 
284                 will need. -->
285             <!-- By default that is to put the resulting archive into the 'deployments' 
286                 folder. -->
287             <!-- http://maven.apache.org/guides/mini/guide-building-for-different-environments.html -->
288             <id>openshift</id>
289             <build>
290                 <plugins>
291                     <plugin>
292                         <artifactId>maven-war-plugin</artifactId>
293                         <version>${version.war.plugin}</version>
294                         <configuration>
295                             <outputDirectory>deployments</outputDirectory>
296                             <warName>ROOT</warName>
297                         </configuration>
298                     </plugin>
299                 </plugins>
300             </build>
301         </profile>
302 
303     </profiles>
304 </project>
pom.xml

 

1.2 model包下,會創建Product類

 1 package model;
 2 
 3 public class Product {
 4 
 5     private String productName;
 6 
 7     private String productNo;
 8 
 9     public String getProductNo() {
10         return productNo;
11     }
12 
13     public void setProductNo(String productNo) {
14         this.productNo = productNo;
15     }
16 
17     public String getProductName() {
18         return productName;
19     }
20 
21     public void setProductName(String productName) {
22         this.productName = productName;
23     }
24 
25     public String toString() {
26         return this.productName + " "    + this.productNo;
27 
28     }
29 }
Product

這個類其實是打醬油的

 

1.3 service包下,建一個ProductService接口

1 package service;
2 
3 import model.Product;
4 
5 public interface ProductService {
6 
7     Product getNewProduct();
8     
9 }
ProductService

 

1.4 service包下,再來幾個實現

 1 package service;
 2 
 3 import javax.inject.Inject;
 4 
 5 import model.Product;
 6 
 7 public abstract class BaseProductServiceImpl implements ProductService {
 8     
 9     @Inject
10     protected Product product;
11     
12     
13     public abstract Product getNewProduct();
14 }
BaseProductServiceImpl

這個是實現類的基類,注意這里私有成員上打了一個注解@Inject,表示運行時將動態注入(實例化)一個Product

再來二個具體的實現類,BookProductServiceImpl生成"書籍"

 1 package service;
 2 
 3 import annotation.Book;
 4 import model.Product;
 5 
 6 @Book
 7 public class BookProductServiceImpl extends BaseProductServiceImpl {
 8 
 9     public Product getNewProduct() {
10         product.setProductName("論程序員的自我修養");
11         product.setProductNo("ISBN 999");
12         return product;
13     }
14 
15 }
BookProductServiceImpl

TelephoneProductServiceImpl生成“電話”

 1 package service;
 2 
 3 import annotation.Telephone;
 4 import model.Product;
 5 
 6 @Telephone
 7 public class TeletephoneProductServiceImpl extends BaseProductServiceImpl {
 8 
 9     public Product getNewProduct() {
10         product.setProductName("NOKIA LUMIA");
11         product.setProductNo("920");
12         return product;
13     }
14 
15 }
TeletephoneProductServiceImpl

可能有朋友注意到了,里面用到了二個自己寫的注釋@Book和@Telephone,接下來會講到,這里先忽略

 

1.5 controller包下,添加IndexController類

為了能跟JSF的前臺頁面交互,這里需要添加一個Controller

 1 package controller;
 2 
 3 import javax.faces.bean.ManagedBean;
 4 import javax.inject.Inject;
 5 
 6 import annotation.*;
 7 import service.*;
 8 
 9 @ManagedBean(name = "Index")
10 public class IndexController {
11 
12     @Inject
13     @Book
14     private ProductService bookProductService;
15 
16     @Inject
17     @Telephone
18     private ProductService telephoneProductService;
19 
20     public ProductService getBookProductService() {
21         return bookProductService;
22     }
23 
24     public ProductService getTelephoneProductService() {
25         return telephoneProductService;
26     }
27 
28 }
29 IndexController
IndexController

好了,一下子搞了這么多代碼,先停下來消化一下,這里我們模擬了分層架構:

model - 代表了業務模型層(本例中,為了簡單起見,沒有細分 業務模型、實體模型、以及web中的ViewModel)

service - 代表了服務層(為了簡單起見,我們把接口+實現都放在一起了,實際中,可能會把這二個分開來)

controller - 這是web層MVC中的控制器層

當然,為了能展示最終的效果,我們會在后面加一個頁面做為View層來提供UI

 

1.6 webapp下,新建一個index.xhtml文件,內容如下:

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml"
 3     xmlns:h="http://java.sun.com/jsf/html"
 4     xmlns:f="http://java.sun.com/jsf/core"
 5     xmlns:ui="http://java.sun.com/jsf/facelets">
 6 
 7 <h:head></h:head>
 8 <body>
 9     Book:
10     <br /> #{Index.bookProductService.newProduct.toString()}
11     <br />
12     <br /> Telephone:
13     <br /> #{Index.telephoneProductService.newProduct.toString()}
14 </body>
index.xhtml

頁面里幾乎沒啥代碼,就是調用IndexController實例中的getBookProductService、getTelephoneProductService方法,進而得到相應的"服務實現類實例",最終輸出產品信息

 

1.7 Inject用在什么地方了?

a) 頁面顯示時,IndexController里,bookProductService和telephoneProductService這二個私有成員上,都加了@Inject注解,所以運行時,這二個成員都能被實例化,但是問題來了,它們都是ProductService的接口類型,而這個接口有二個具體的實現(BookProductServiceImpl和TeletephoneProductServiceImpl),最終運行時,應該實現化哪一個呢?

關鍵在于另一個注解@Book和@Telephone,觀察一下:BookProductServiceImpl類上我們也加了@Book,而TeletephoneProductServiceImpl上加了@Telephone,這樣正好可以跟IndexControll中這二個私成成員的注釋“匹配”上,所以最終系統知道私有成員bookProductService應該被實例化成BookProductServiceImpl,telephoneProductService被實例化成TeletephoneProductServiceImpl

@Book和@Telephone的代碼如下:

 1 package annotation;
 2 
 3 import java.lang.annotation.Retention;
 4 import java.lang.annotation.RetentionPolicy;
 5 
 6 import javax.inject.Qualifier;
 7 
 8 @Qualifier
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface Book {
11 
12 }
Book
 1 package annotation;
 2 
 3 import java.lang.annotation.Retention;
 4 import java.lang.annotation.RetentionPolicy;
 5 import javax.inject.Qualifier;
 6 
 7 @Qualifier
 8 @Retention(RetentionPolicy.RUNTIME)
 9 public @interface Telephone {
10 
11 }
Telephone

b) BaseProductServiceImpl中,在私成成員product上加了@Inject,這樣運行時,能自動實例化Product對象

 

1.8 運行結果

jboss中部署后,瀏覽http://localhost:8080/cdi-web-sample/faces/index.xhtml 或http://localhost:8080/cdi-web-sample/index.jsf

 

1.9 Method(方法)注入及Constructor(構造器)注入

剛才我們看到的都是在Field(成員)上注入,除了這種方式,也可以在Method或Constructor上注入

 1     private Product product ;
 2     
 3     /**
 4      * 演示在方法上使用@Inject注入
 5      * @param p
 6      */
 7     @Inject
 8     public void setProduct(Product p){
 9         product = p;        
10     }
11     
12     public Product getProduct(){
13         return product;        
14     }
Method Inject

上面的代碼即是Method注入的示例,最后來看下構造器注入,我們再新建一個ClothProductServiceImpl用于生產服裝

 1 package service;
 2 
 3 import javax.inject.Inject;
 4 
 5 import annotation.*;
 6 import model.Product;
 7 
 8 @Cloth
 9 public class ClothProductServiceImpl implements ProductService {
10 
11     private Product product;
12     
13     /**
14      * 構造器注入
15      * @param p
16      */
17     @Inject
18     public ClothProductServiceImpl(Product p ){
19         p.setProductName("A New Dress");
20         p.setProductNo("SPRIT-001");
21         product = p;
22         
23     }
24     
25     public Product getNewProduct() {        
26         return product;
27     }
28 
29 }
Constructor Inject

運行時,系統會自動給構造器ClothProductServiceImpl傳遞一個實例化的Product對象作為參數,以實現Product實例的注入

附文中示例源碼下載:cdi-web-sample.zip

下一節,我們將學習Bean注入后的生命周期管理


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()