JavaEE 6 をテキストエディタだけで(5) - CDI編



前回の続きで CDI の導入

CDI(Contexts and Dependency Injection)とは

  • JavaEE 6 にて標準化されたDI仕様(JSR 299)
  • Java EE 5 仕様での DI は制限された形で仕様化されてきた(EJBJSF など個別の仕様で DI が定義されてきた)
    • 例えば EJB に @Resource で任意のクラスを DI できないため、EJB に DAOクラスなどを DI できなかったり、JSF以外の任意のプレゼン層フレームワークを採用した場合に @EJB の DI ができない等
  • CDIJava EE 環境で DI を統一的に行えるよう仕様化された
  • アノテーションベースで型安全な DI を提供

CDIの有効化

  • 現在の JavaEE 6 では、beans.xml が存在する場合に CDI が有効になる
  • beans.xml の内容は空でも良い

src/main/webapp/WEB-INF/beans.xml として空ファイル作成


CDIAPI 依存追加

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();
    }
}

サービスへ 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;
    }
}

ついでに 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 使うとー