Code ví dụ CDI trong Java, @Named, @Inject, @Default, @Alternative

Code ví dụ CDI trong Java, @Named, @Inject, @Default, @Alternative

(Xem lại CDI là gì)

Ở bài này chúng ta sẽ tìm hiểu cách thực hiện Dependency Injection với CDI, các annotation được sử dụng troign CDI.

1. Dependency Injection mà không cần CDI, Spring DI…

Mình có 1 ví dụ sau: viết 1 chương trình kết nối tới các loại database khác nhau để thực hiện query.

Interface ConnectDatabase khai báo method connect tới database.

package stackjava.com.cdidemo.dao;

public interface ConnectDatabase {
  public void connect();
}

Các class ConnectDB2, ConnectMySQL, ConnectPostgreSQL sẽ thừa kế interface ConnectDatabase và cài đặt lại method connect tới database tương ứng (DB2, MySQL, PostgreSQL)

package stackjava.com.cdidemo.dao;

public class ConnectDB2 implements ConnectDatabase {

  public void connect() {
    System.out.println("Connect DB2 database");
  }

}
package stackjava.com.cdidemo.dao;

public class ConnectMySQL implements ConnectDatabase {

  public void connect() {
    System.out.println("Connect MySQL database");
  }

}
package stackjava.com.cdidemo.dao;

public class ConnectPostgreSQL implements ConnectDatabase {

  public void connect() {
    System.out.println("Connect PostgreSQL database");
  }

}

Class DAOUtils.java sẽ thực hiện sử dụng 1 cài đặt của ConnectDatabase để kết nối tới database và thực hiện query

Code ví dụ CDI trong Java, @Named, @Inject, @Default, @Alternative

1.1 Trường hợp không sử dụng Dependency Injection

Class DAOUtils.java sẽ như sau:

package stackjava.com.cdidemo.dao;

public class DAOUtils {

  private ConnectMySQL conn;

  public void query() {
    conn.connect();
    System.out.println("query database...");
  }

  // getter - setter
  // constructor with field
}

Một Cài đặt của ConnectDatabase sẽ được khai báo như thuộc tính trong DAOUtils và được truyền vào qua hàm setter hoặc hàm khởi tạo constructor. Cách này khá là hard code, vì mỗi lần thực hiện query tới database khác ta lại phải sửa lại code của class DAOUtils.java

1.2 Trường hợp sử dụng Dependency Injection

Class DAOUtils.java sẽ như sau:

package stackjava.com.cdidemo.dao;

public class DAOUtils {

  private ConnectDatabase conn;

  public void query() {
    conn.connect();
    System.out.println("query database...");
  }

  // getter - setter

}

Chúng ta chỉ khai báo interface ConnectDatabase giống như 1 thuộc tính chứ không chỉ rõ cài cài của ConnectDatabase

Khi sử dụng DAOUtils, tùy vào việc muốn query tới database nào ta sẽ truyền vào một cài đặt của ConnectDatabase tương ứng

package stackjava.com.cdidemo.main;

import stackjava.com.cdidemo.dao.ConnectDatabase;
import stackjava.com.cdidemo.dao.ConnectMySQL;
import stackjava.com.cdidemo.dao.DAOUtils;

public class MainApp {
  public static void main(final String[] args) {
    DAOUtils daoUtils = new DAOUtils();

    ConnectDatabase conn = new ConnectMySQL();
    daoUtils.setConn(conn);

    daoUtils.query();
  }
}

Phần khởi tạo ConnectDatabase sẽ được khởi tạo với cài đặt tương ứng với database mà chúng ta muốn thực hiện query.

Kết quả:

Code ví dụ CDI trong Java, @Named, @Inject, @Default, @Alternative

Việc cài đặt nào của ConnectDatabase được khởi tạo tùy vào class nào sử dụng nó, ví dụ có class luôn chỉ query tới MySQL thì ta để như trên. Hoặc ta có thể tạo 1 biến file config (.xml, .properties), mỗi khi khởi tạo ConnectDatabase ta sẽ tạo 1 cài đặt theo biến config đó, ví dụ:

package stackjava.com.cdidemo.main;

import stackjava.com.cdidemo.dao.ConnectDB2;
import stackjava.com.cdidemo.dao.ConnectDatabase;
import stackjava.com.cdidemo.dao.ConnectMySQL;
import stackjava.com.cdidemo.dao.ConnectPostgreSQL;
import stackjava.com.cdidemo.dao.DAOUtils;

public class MainApp {

  public static int CONNECT_MYSQL = 1;
  public static int CONNECT_POSTGRE = 2;
  public static int CONNECT_DB2 = 3;

  public static void main(final String[] args) {
    DAOUtils daoUtils = new DAOUtils();
    ConnectDatabase conn = null;

    int configDatabase = readDatabaseConfig();
    if (configDatabase == CONNECT_MYSQL) {
      conn = new ConnectMySQL();
    }
    if (configDatabase == CONNECT_POSTGRE) {
      conn = new ConnectPostgreSQL();
    }
    if (configDatabase == CONNECT_DB2) {
      conn = new ConnectDB2();
    }

    daoUtils.setConn(conn);
    daoUtils.query();
  }
}

Download code ví dụ trên tại đây 

Code ví dụ CDI trong Java, @Named, @Inject, @Default, @Alternative

2. Dependency Injection với CDI

Mình tiếp tục sử dụng ví dụ bên trên nhưng thực hiện DI với CDI

Có khá nhiều thư viện cài đặt thực hiện CDI. Ở đây mình sử dụng thư viện sau:

<dependency>
  <groupId>javax.enterprise</groupId>
  <artifactId>cdi-api</artifactId>
  <version>2.0-PFD2</version>
</dependency>
<dependency>
  <groupId>org.jboss.weld.se</groupId>
  <artifactId>weld-se</artifactId>
  <version>2.4.3.Final</version>
</dependency>

Tạo maven project

Tạo file beans.xml trongsrc/main/resources (nếu là ứng dụng web thì tạo ở trong folder webapp/WEB-INF

<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

</beans>

File beans.xml nó đóng vai trò như file spring config trong Spring Framework, hay faces-config.xml trong JSF

package stackjava.com.cdidemo.dao;

import javax.inject.Inject;
import javax.inject.Named;

@Named("daoUtils")
public class DAOUtils {

  @Inject
  private ConnectDatabase conn;

  public void query() {
    conn.connect();
    System.out.println("query database...");
  }

}
  • Annotation @Named(“daoUtils”) đánh dấu đây là 1 bean được quản lý bởi CDI container, có thể được lấy ra (look up) từ container với tên là “daoUtils”. (nếu bạn không chỉ rõ tên thì nó sẽ mặc định là tên class với chữ đầu tiên viết thường)
  • Annotation @Inject sẽ tìm 1 cài đặt tương ứng của interface ConnectDatabase tiêm vào /truyền vào (Injection) DAOUtils
  • Bạn cũng có thể thực hiện inject thông qua hàm setter hoặc qua constructor như sau:
// Inject via setter
@Inject
public void setConn(final ConnectDatabase conn) {
  System.out.println("setter");
  this.conn = conn;
}

// or

// Inject via constructor
@Inject
public DAOUtils(final ConnectDatabase conn) {
  System.out.println("Inject via constructor");
  this.conn = conn;
}

Các class cài đặt của ConnectDatabase

package stackjava.com.cdidemo.dao;

import javax.enterprise.inject.Default;

@Default
public class ConnectMySQL implements ConnectDatabase {

  public void connect() {
    System.out.println("Connect MySQL database");
  }

}
  • Annotation @Default đánh dấu đây là cài đặt mặc định khi thực hiện injection ConnectDatabase. (mặc định là @Default, nếu bạn không sử dụng annotation nào nó sẽ hiểu là đang dùng @Default)
package stackjava.com.cdidemo.dao;

import javax.enterprise.inject.Alternative;

@Alternative
public class ConnectDB2 implements ConnectDatabase {

  public void connect() {
    System.out.println("Connect DB2 database");
  }

}
package stackjava.com.cdidemo.dao;

import javax.enterprise.inject.Alternative;

@Alternative
public class ConnectPostgreSQL implements ConnectDatabase {

  public void connect() {
    System.out.println("Connect PostgreSQL database");
  }

}
  • Annotation @Alternative đánh dấu đây là cài đặt thay thế khi injection ConnectDatabase, nếu nó không tìm thấy cài đặt nào được đánh dấu @Default thì nó sẽ lấy cài đặt có đánh dấu @Alternative

Cơ chế khi injection ConnectDatabase vào DAOUtils như sau:

  • Tìm 1 cài đặt của ConnectDatabase với annotation @Default để chèn vào (nếu có nhiều hơn 1 cài đặt đánh dấu là @Default sẽ xảy ra lỗi vì nó không biết lấy cài đặt nào)
  • Nếu không tìm thấy cài đặt nào với @Default nó sẽ lấy cài đặt được đánh dấu @Alternative (Nếu có nhiều hơn 1 cài đặt được đánh dấu @Alternative sẽ xảy ra lỗi)

Demo:

package stackjava.com.cdidemo.main;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;

import stackjava.com.cdidemo.dao.DAOUtils;

public class MainApp {

  public static void main(final String[] args) {
    Weld weld = new Weld();
    WeldContainer container = weld.initialize();
    BeanManager bm = container.getBeanManager();

    Bean<DAOUtils> bean = (Bean<DAOUtils>) bm.getBeans("daoUtils").iterator().next();
    CreationalContext<DAOUtils> ctx = bm.createCreationalContext(bean);
    DAOUtils jdbc = (DAOUtils) bm.getReference(bean, DAOUtils.class, ctx);
    jdbc.query();
  }
}

Kết quả:

Connect MySQL database
query database...

Okay, Done!
Phần tiếp theo chúng ta sẽ tìm hiểu:

Download code ví dụ trên tại đây

Code ví dụ CDI trong Java, @Named, @Inject, @Default, @Alternative

References:

https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html

https://docs.oracle.com/javaee/7/tutorial/cdi-basic.htm

stackjava.com