前回の続きで CDI の導入
CDI(Contexts and Dependency Injection)とは
- JavaEE 6 にて標準化されたDI仕様(JSR 299)
- Java EE 5 仕様での DI は制限された形で仕様化されてきた(EJB や JSF など個別の仕様で DI が定義されてきた)
- 例えば EJB に @Resource で任意のクラスを DI できないため、EJB に DAOクラスなどを DI できなかったり、JSF以外の任意のプレゼン層フレームワークを採用した場合に @EJB の DI ができない等
- CDI は Java EE 環境で DI を統一的に行えるよう仕様化された
- アノテーションベースで型安全な DI を提供
CDIの有効化
- 現在の JavaEE 6 では、beans.xml が存在する場合に CDI が有効になる
- beans.xml の内容は空でも良い
src/main/webapp/WEB-INF/beans.xml として空ファイル作成
CDI の API 依存追加
POMに以下の依存を定義
<dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <scope>provided</scope> <version>1.0</version> </dependency>
全体としては以下
<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>etc9</groupId> <artifactId>webapp001</artifactId> <version>0.1</version> <packaging>war</packaging> <name>webapp001</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>6.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version>3.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>2.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
Repository の作成
CDI による DI 対象として Repository を作成(簡単のためかなり適当です・)
package etc9.repository; import java.io.Serializable; public interface Repository<T, PK extends Serializable> { T findById(PK id); T persist(T entity); void remove(T entity); void remove(PK id); }
GenericDao風に、
package etc9.repository; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; public abstract class JpaRepository<T, PK extends Serializable> implements Repository<T, PK> { @PersistenceContext(unitName = "webapp001PU") EntityManager em; private final Class<T> type = initType(); private Class<T> initType() { ParameterizedType pt = (ParameterizedType)getClass().getGenericSuperclass(); Type t = pt.getActualTypeArguments()[0]; @SuppressWarnings("unchecked") Class<T> classOfT = (Class<T>) t; return classOfT; } protected Class<T> getType() { return type; } protected TypedQuery<T> createQuery(String jpql) { return em.createQuery(jpql, getType()); } @Override public T findById(PK id) { return em.find(getType(), id); } @Override public T persist(T entity) { em.persist(entity); return entity; } @Override public void remove(T entity) { em.remove(entity); } @Override public void remove(PK id) { T entity = findById(id); remove(entity); } }
BookRepository
package etc9.repository; import java.util.List; import javax.inject.Named; import javax.persistence.TypedQuery; import etc9.domain.Book; @Named public class BookRepository extends JpaRepository<Book, Long> { public List<Book> findAll() { return createQuery("SELECT b FROM Book b").getResultList(); } public List<Book> findByLikeTitle(String title) { TypedQuery<Book> tq = createQuery("SELECT b FROM Book b WHERE b.title LIKE %:title%"); tq.setParameter("title", title); return tq.getResultList(); } }
- @Named のアノテーションを指定することで CDI の管理対象 Bean になる
サービスへ Repository を DI
BookService に BookRepository を DI
package etc9.service; import java.util.List; import javax.ejb.Local; import javax.ejb.Stateless; import javax.inject.Inject; import etc9.domain.Book; import etc9.repository.BookRepository; @Stateless @Local(BookService.class) public class BookServiceImpl implements BookService { @Inject private BookRepository bookRepository; @Override public List<Book> findAll() { return bookRepository.findAll(); } @Override public Book create(Book book) { bookRepository.persist(book); return book; } }
- 管理対象となった BookRepository を @Inject アノテーションにより DI
ついでに JSF 側も
前回までは @ManagedBean として JSF管理Bean として定義していたものも、ついでに CDI化
package etc9.web; import etc9.domain.Book; import etc9.service.BookService; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.enterprise.inject.Model; @Model public class BookController {
ここで @Model アノテーションは以下のように定義されており、Repository と同じように結局 @Named にてCDIの管理Beanとなる
@Named @RequestScoped @Documented @Stereotype @Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Model { }
次回は IDE 使うとー
blog1.mammb.com