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>
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 }
這個類其實是打醬油的
1.3 service包下,建一個ProductService接口

1 package service; 2 3 import model.Product; 4 5 public interface ProductService { 6 7 Product getNewProduct(); 8 9 }
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 }
這個是實現類的基類,注意這里私有成員上打了一個注解@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 }
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 }
可能有朋友注意到了,里面用到了二個自己寫的注釋@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
好了,一下子搞了這么多代碼,先停下來消化一下,這里我們模擬了分層架構:
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>
頁面里幾乎沒啥代碼,就是調用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 }

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 }
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注入的示例,最后來看下構造器注入,我們再新建一個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 }
運行時,系統會自動給構造器ClothProductServiceImpl傳遞一個實例化的Product對象作為參數,以實現Product實例的注入
附文中示例源碼下載:cdi-web-sample.zip
下一節,我們將學習Bean注入后的生命周期管理
文章列表