JAVA CDI @Inject基本用法

dunne21發表於2021-09-09

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

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

一、基本的Inject注入

1.1 在eclipse中先建立一個常規的maven Dynamic Web專案(不熟悉maven的,可以先看看這裡),下面是完整的專案截圖

圖片描述

裡面各package的程式碼,後面會給出。 專案的屬性中,注意有幾個屬性要勾上(預設情況下,應該已經自動勾上了),如下圖:

圖片描述

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

Pom.xml的內容如下:


  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     
 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 --&gt
 18 
 20     4.0.0
 21 
 22     cnblogs
 23     cdi-web-sample
 24     0.0.1-SNAPSHOT
 25     war
 26     cdi-web-sample
 27     A starter Java EE 6 webapp project for use on JBoss AS 7 / EAP 6, generated from the jboss-javaee6-webapp archetype
 28 
 29     
 30     
 31         
 32             Apache License, Version 2.0
 33             repo
 34             .html
 35         
 36     
 37 
 38     
 39         <!-- Explicitly declaring the source encoding eliminates the following 
 40             message: --&gt
 41         <!-- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered 
 42             resources, i.e. build is platform dependent! --&gt
 43         UTF-8
 44 
 45         <!-- JBoss dependency versions --&gt
 46         7.4.Final
 47 
 48         <!-- Define the version of the JBoss BOMs we want to import to specify 
 49             tested stacks. --&gt
 50         1.0.7.Final
 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. --&gt
 55         <!-- 1.0.4.Final-redhat-4> --&gt
 56 
 57         <!-- other plugin versions --&gt
 58         2.10
 59         2.1.1
 60 
 61         <!-- maven-compiler-plugin --&gt
 62         1.6
 63         1.6
 64     
 65 
 66 
 67     
 68         
 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) --&gt
 77             
 78                 org.jboss.bom
 79                 jboss-javaee-6.0-with-tools
 80                 ${version.jboss.bom}
 81                 pom
 82                 import
 83             
 84             
 85                 org.jboss.bom
 86                 jboss-javaee-6.0-with-hibernate
 87                 ${version.jboss.bom}
 88                 pom
 89                 import
 90             
 91         
 92     
 93 
 94     
 95 
 96         <!-- First declare the APIs we depend on and need for compilation. All 
 97             of them are provided by JBoss AS 7 --&gt
 98 
 99         <!-- Import the CDI API, we use provided scope as the API is included in 
100             JBoss AS 7 --&gt
101         
102             javax.enterprise
103             cdi-api
104             provided
105         
106 
107         <!-- Import the Common Annotations API (JSR-250), we use provided scope 
108             as the API is included in JBoss AS 7 --&gt
109         
110             org.jboss.spec.javax.annotation
111             jboss-annotations-api_1.1_spec
112             provided
113         
114 
115         <!-- Import the JAX-RS API, we use provided scope as the API is included 
116             in JBoss AS 7 --&gt
117         
118             org.jboss.spec.javax.ws.rs
119             jboss-jaxrs-api_1.1_spec
120             provided
121         
122 
123         <!-- Import the JPA API, we use provided scope as the API is included in 
124             JBoss AS 7 --&gt
125         
126             org.hibernate.javax.persistence
127             hibernate-jpa-2.0-api
128             provided
129         
130 
131         <!-- Import the EJB API, we use provided scope as the API is included in 
132             JBoss AS 7 --&gt
133         
134             org.jboss.spec.javax.ejb
135             jboss-ejb-api_3.1_spec
136             provided
137         
138 
139         <!-- JSR-303 (Bean Validation) Implementation --&gt
140         <!-- Provides portable constraints such as @Email --&gt
141         <!-- Hibernate Validator is shipped in JBoss AS 7 --&gt
142         
143             org.hibernate
144             hibernate-validator
145             provided
146             
147                 
148                     org.slf4j
149                     slf4j-api
150                 
151             
152         
153 
154         <!-- Import the JSF API, we use provided scope as the API is included in 
155             JBoss AS 7 --&gt
156         
157             org.jboss.spec.javax.faces
158             jboss-jsf-api_2.1_spec
159             provided
160         
161 
162         <!-- Now we declare any tools needed --&gt
163 
164         <!-- Annotation processor to generate the JPA 2.0 metamodel classes for 
165             typesafe criteria queries --&gt
166         
167             org.hibernate
168             hibernate-jpamodelgen
169             provided
170         
171 
172         <!-- Annotation processor that raising compilation errors whenever constraint 
173             annotations are incorrectly used. --&gt
174         
175             org.hibernate
176             hibernate-validator-annotation-processor
177             provided
178         
179 
180         <!-- Needed for running tests (you may also use TestNG) --&gt
181         
182             junit
183             junit
184             test
185         
186 
187         <!-- Optional, but highly recommended --&gt
188         <!-- Arquillian allows you to test enterprise code such as EJBs and Transactional(JTA) 
189             JPA from JUnit/TestNG --&gt
190         
191             org.jboss.arquillian.junit
192             arquillian-junit-container
193             test
194         
195 
196         
197             org.jboss.arquillian.protocol
198             arquillian-protocol-servlet
199             test
200         
201 
202     
203 
204     
205         <!-- Maven will append the version to the finalName (which is the name 
206             given to the generated war, and hence the context root) --&gt
207         ${project.artifactId}
208         
209             
210                 maven-war-plugin
211                 ${version.war.plugin}
212                 
213                     <!-- Java EE 6 doesn't require web.xml, Maven needs to catch up! --&gt
214                     false
215                 
216             
217             <!-- The JBoss AS plugin deploys your war to a local JBoss AS container --&gt
218             <!-- To use, run: mvn package jboss-as:deploy --&gt
219             
220                 org.jboss.as.plugins
221                 jboss-as-maven-plugin
222                 ${version.jboss.maven.plugin}
223             
224         
225     
226 
227     
228         
229             <!-- The default profile skips all tests, though you can tune it to run 
230                 just unit tests based on a custom pattern --&gt
231             <!-- Seperate profiles are provided for running all tests, including Arquillian 
232                 tests that execute in the specified container --&gt
233             default
234             
235                 true
236             
237             
238                 
239                     
240                         maven-surefire-plugin
241                         ${version.surefire.plugin}
242                         
243                             true
244                         
245                     
246                 
247             
248         
249 
250         
251             <!-- An optional Arquillian testing profile that executes tests in your 
252                 JBoss AS instance --&gt
253             <!-- This profile will start a new JBoss AS instance, and execute the 
254                 test, shutting it down when done --&gt
255             <!-- Run with: mvn clean test -Parq-jbossas-managed --&gt
256             arq-jbossas-managed
257             
258                 
259                     org.jboss.as
260                     jboss-as-arquillian-container-managed
261                     test
262                 
263             
264         
265 
266         
267             <!-- An optional Arquillian testing profile that executes tests in a remote 
268                 JBoss AS instance --&gt
269             <!-- Run with: mvn clean test -Parq-jbossas-remote --&gt
270             arq-jbossas-remote
271             
272                 
273                     org.jboss.as
274                     jboss-as-arquillian-container-remote
275                     test
276                 
277             
278         
279 
280         
281             <!-- When built in OpenShift the 'openshift' profile will be used when 
282                 invoking mvn. --&gt
283             <!-- Use this profile for any OpenShift specific customization your app 
284                 will need. --&gt
285             <!-- By default that is to put the resulting archive into the 'deployments' 
286                 folder. --&gt
287             <!-- >
288             openshift
289             
290                 
291                     
292                         maven-war-plugin
293                         ${version.war.plugin}
294                         
295                             deployments
296                             ROOT
297                         
298                     
299                 
300             
301         
302 
303     
304 


 

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 }

可能有朋友注意到了,裡面用到了二個自己寫的註釋@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 html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">
 2 
 6 
 7 
 8 
 9     Book:
10     
 #{Index.bookProductService.newProduct.toString()} 11     
12     
 Telephone: 13     
 #{Index.telephoneProductService.newProduct.toString()} 14 

頁面裡幾乎沒啥程式碼,就是呼叫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中部署後,瀏覽 或

圖片描述

 

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例項的注入。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1600/viewspace-2808616/,如需轉載,請註明出處,否則將追究法律責任。

相關文章