Saturday, May 31, 2014

Giới thiệu về Spring Framework

Giới thiệu về Spring Framework

Spring là một application framework mã nguồn mở, được giới thiệu vào năm 2002. Rod Johnson đã đưa ra ý tưởng này từ kinh nghiệm làm việc với kiến trúc J2EE.


Ông ta đã viết cuốn sách với tiêu đề: “J2EE Develoment without using EJB” để giới thiệu khái niệm trình chứa hạng nhẹ (lightweight container). Với lý luận: EJB thì có giá trị của nó, nhưng không phải lúc nào cũng cần thiết và phù hợp cho tất cả các ứng dụng.

Như Hibernate chống lại CMP về nguyên lý cơ bản của kỹ thuật ORM, Spring chống lại EJB vì sự phức tạp và không phù hợp cho các unit test. Thay vì EJB, Spring sử dụng Java bean, với một vài sự thay đổi để thu được tất cả các thuận lợi mà môi trường EJB đưa ra. Do đó Spring là một sự lựa chọn khác so với EJB.

Spring không phải là một kỹ thuật persistence nhưng nó cho phép tích hợp với các kỹ thuật khác. EJB thì tập trung vào kỹ thuật persistence và bây giờ nó đã cộng tác với Hibernate, một ORM tốt nhất ngày nay. Nó đang dự định cộng tác với một kỹ thuật ORM tốt khác là JDO (cung cấp cho Object Database).

Trong Spring, chúng ta có thể sử dụng Java Bean để rút trích các thứ mà lúc trước chỉ có thể với EJB. Mục tiêu chính của Spring là đơn giản việc phát triển J2EE và testing.

EJB được xuất hiện vào 1988 và là một chuẩn, đặc tả trong thế giới Enterprise. Mặc dù có một số kỹ thuật Java gần đây, nhưng không có kỹ thuật nào đáng kể so với EJB về mặc tổng các đặc tính của nó.

Theo Rod Johnson thì EJB không phức tạp nhưng nó cố gắng cung cấp cách giải quyết cho các vấn đề phức tạp. EJB được phát triển chính cho các giao dịch, đối tượng được phân phát ở trên máy khác. Nhưng một số enterprise project không có mức độ phức tạp và vẫn sử dụng EJB và thậm chí các ứng dụng đơn giản trở nên phức tạp. Trong trường hợp này thì Spring là một sự chọn lựa.

Từ lúc Spring hỗ trợ các dịch vụ ở mức enterprise, thì nó tuyên bố là một sự lựa chọn khác đối với EJB.

Thuận lợi của EJB:

Transaction Management
Declarative Transaction support
Persistence ( CMP & BMP)
Declarative Security
Distributed Computing (Container managed RPC)

Spring không cố gắng làm mọi thứ nhưng nó hỗ trợ những kỹ thuật tốt nhất cho mỗi yêu cầu.Thay vì CMP & BMP, nó hỗ trợ một vài kỹ thuật persistence khác như JDO, Hiberbate và OJB. Các ORM tool này thì có nhiều khả năng cài đặt hơn CMP. Để đơn giản coding JDBC, có một tool là iBatis và Spring cũng hỗ trợ nó.

Spring sử dụng Acegi, một security framework mã nguồn mở và cung cấp để khai báo security thông qua cấu hình Spring hoặc class metadata, trong khi EJB khai báo security được cấu hình thông qua mô tả demployment. Spring cung cấp proxying cho RMI (kỹ thuật remoting đặc biệt như Burlap) JAX-RPC & web-service, trong khi EJB cung cấp lời gọi phương thức ở xa được quản lý bởi container.

Spring có thể cung cấp khai báo transaction như EJB. Hơn nữa, Spring còn cung cấp khai báo thủ tục rollback cho các phương thức và exception.

Do đó, trong khi EJB thì cứng và cố gắng làm mọi thứ, một vài công việc tốt nhưng một số thì không. Spring chỉ sử dụng Java Bean và thông qua một số kỹ thuật đặc biệt để cung cấp nhiều chức năng như EJB, bằng cách tích hợp với một số kỹ thuật open source khác.

Do đó, nó cung cấp một vài thuận lợi hơn EJB như:

Testing dễ dàng hơn - không cần khởi động EJB container để test.
Spring dựa vào quy ước của chuẩn Java Bean, nên programmer dễ dàng làm việc với nó.
Nó sử dụng AOP (Aspect-Oriented Programming), mô hình hữu ích để bổ sung vào OOP truyền thống và bảo toàn tính nguyên vẹn của OOP.
Nó thì uyển chuyển.

Mục đích của Spring là trở thành một application framework. Các framework phổ biến khác như Struts, Tapestry, JSF,... là các framework tốt cho tầng web nhưng khi chúng ta sử dụng các framework này, chúng ta phải cung cấp thêm framework khác để giải quyết tầng enterprise mà tích hợp tốt với các framework này. Spring làm giảm bớt vấn đề này bằng cách cung cấp một framework toàn diện bao gồm:

Core bean container,
MVC framework,
AOP integration framework,
JDBC integration framework,
EJB integration framework.

Nó cũng cung cấp module tích hợp với O/R tool như Hibernate và JDO. Do đó Spring framework có thể được xem như một kiến trúc chứa 7 module. Chức năng của mỗi thành phần như sau:

1. Core Container:
Core container cung cấp chức năng cơ bản của Spring. Thành phần chính của nó là Bean Factory, một cài đặt của Factory pattern. BeanFactory áp dụng IoC pattern để đặc tả sự phụ thuộc từ code của ứng dụng.

2. Spring Context/Application Context:
Spring context là một file cấu hình để cung cấp thông tin ngữ cảnh của Spring. Spring context cung cấp các service như JNDI access, EJB integration, e-mail, internalization, validation, và scheduling functionality.

3. Spring AOP (Aspect-Oriented):
Spring AOP module tích hợp chức năng lập trình hướng khía cạnh vào Spring framework thông qua cấu hình của nó. Spring AOP module cung cấp các dịch vụ quản lý giao dịch cho các đối tượng trong bất kỳ ứng dụng nào sử dụng Spring. Với Spring AOP chúng ta có thể tích hợp declarative transaction management vào trong ứng dụng mà không cần dựa vào EJB component.

Spring AOP module cũng đưa lập trình metadata vào trong Spring. Sử dụng cái này chúng ta có thể thêm annotation vào source code để hướng dẫn Spring nơi và làm thế nào để liên hệ với aspect.

4. Spring DAO:
Tầng JDBC và DAO đưa ra một cây phân cấp exception để quản lý kết nối đến database, điều khiển exception và thông báo lỗi được ném bởi vendor của database. Tầng exception đơn giản điều khiển lỗi và giảm khối lượng code mà chúng ta cần viết như mở và đóng kết nối. Module này cũng cung cấp các dịch vụ quản lý giao dịch cho các đối tượng trong ứng dụng Spring.

5. Spring ORM:
Spring có thể tích hợp với một vài ORM framework để cung cấp Object Relation tool bao gồm: JDO, Hibernate, OJB và iBatis SQL Maps.

6. Spring Web module:
Nằm trên application context module, cung cấp context cho các ứng dụng web. Spring cũng hỗ trợ tích hợp với Struts, JSF và Webwork. Web module cũng làm giảm bớt các công việc điều khiển nhiều request và gắn các tham số của request vào các đối tượng domain.

7. Spring MVC Framework:
MVC Framework thì cài đặt đầy đủ đặc tính của MVC pattern để xây dựng các ứng dụng Web. MVC framework thì cấu hình thông qua giao diện và chứa được một số kỹ thuật view bao gồm: JSP, Velocity, Tiles và generation of PDF và Excel file.

Ví dụ:
Có một số kỹ thuật tuyệt vời cho tầng web như: Spring MVC framework, Struts, JSF, WebWork, JSP, Tapestry, FreeMarker,...Developer sẽ bị lúng túng đối chiếu những điểm mạnh và xấu của tất cả chúng. Mỗi khi họ chọn một kỹ thuật và bắt đầu cài đặt, thì sau đó nếu họ muốn thay đổi một kỹ thuật khác thì rất khó. Nhưng Spring đưa ra các module cho tất cả các kỹ thuật trên, và rất đơn giản để thay đổi file cấu hình.

Với phương pháp này, nó có khả năng cho cả team thử và test các tất cả các hình thức trên và xem ảnh hưởng cùng tốc độ trước khi quyết định chọn lựa.

JSP là một view template mặc định. "InternalResouceViewResolver" có thể được sử dụng cho mục đích này
Như mình tìm hiểu thì Spring framework là một platform hổ trợ lập trình ứng dụng bằng java. Nó được phát minh bởi Rod Johnson. Nó được giới thiệu lần đầu tiên trong cuốn Expert One-on-One J2EE Design and Development, vào năm 2002. Spring đảm nhiệm(xử lý) phần cơ sở hạ tầng của phần mềm để bạn có thể tập trung vào xây dựng ứng dụng của mình.

Spring cho phép chúng ta xây dựng ứng dụng từ POJOs.
POJOs là viết tắt của "plain old Java objects" chỉ những object java bình thường, chúng không có gì đặc biệt, nó không theo một mô hình hay quy ước nào cả chỉ bao gồm các thuộc tính và phương thức.
1. Core package là phần cơ bản nhất của framework, cung cấp những đặc tính IoC và Dependency Injection. Khái niệm cơ bản là BeanFactory - cài đặt factory pattern cho phép bạn móc nối sự phụ thuộc giữa các đối tượng trong file cấu hình. (IoC và Dependency Injection mình sẽ giải thik sau )
2. Phía trên của Core package là Context package - cung cấp cách để truy cập đối tượng. Context package kết thừa các đặc tính từ bean package và thêm vào chức năng đa ngôn ngữ (I18N), truyền sự kiện, resource-loading,...
3. DAO package cung cấp cho tầng JDBC, bỏ bớt những coding dài dòng của JDBC và chuyển đổi mã lỗi được xác định bởi database vendor. JDBC package cung cấp cách lập trình tốt như declarative transaction management, không chỉ cho các lớp cài đặt các giao tiếp đặc biệt mà còn cho tất cả POJO (plain old Java objects).
4. ORM package cung cấp tầng tích hợp với object-relational mapping API bao gồm: JDO, Hibernate, iBatis. Sử dụng ORM package bạn có thể sử dụng tất cả các O/R mapper đó kết hợp với tất cả các đặc tính của Spring như declarative transaction management.
5. Spring AOP package cung cấp aspect-oriented programming cho phép bạn định nghĩa method-interceptor và pointcut để móc nối các chức năng được cài đặt trong các module. Sử dụng chức năng metadata bạn có thể kết hợp tất cả thông tin vào code.
6. Spring Web package cung cấp đặc tính của web như: chức năng file-upload, khởi tạo IoC container sử dụng trình lắng nghe serlvet và web-oriented application context. Package này để tích hợp với WebWork và Struts.
7. Spring MVC package cung cấp mô hình MVC cho ứng dụng web. Spring MVC framework cung cấp sự phân biệt rõ ràng giữa domain model và web form - cho phép bạn sử dụng tất cả các đặc tính khác của Spring framework.

src : http://khoinguonit.com/f40/hoc-spring-framework-1110/
Bây giờ mình sẽ nói về Inversion of Control(IoC) và Dependency Injection (DI). Đây là hai pattern quan trọng cần phải hiểu trong lập trình với Spring .
Đặt vấn đề :
Trong quá trình lập trình hướng đối tượng khi chúng ta xây dựng các ứng dụng với rất nhiều class. Khi đó thường xảy ra sự phụ thuộc giữa các class với nhau. Để giễ hiểu chúng ta có thể xét ví dụ sau :
Giả sử chúng ta có class XeMay chứa một đối tượng của class DongCo. 

class XeMay{
private DongCo dongco;
public XeMay(){
dongco = new DongCo();
}
}

Vấn đề cần quan tâm ở đây là ta có thể thấy có sự móc nối của hai class này. Ta có thể thấy class XeMay phụ thuộc vào đối tượng “dongco”. Một số vấn đề cần nói ở đây là :
- class XeMay đảm nhiệm (control ) việc tạo ra đối tượng động cơ.
- Đối tượng của class DongCo được tham chiếu trực tiếp từ class XeMay. Tạo tight coupling(kết nối cứng) giửa DongCo và XeMay.
- class XeMay biết rỏ kiểu của DongCo. Nhưng giả sử khi chúng ta muốn thay đổi DongCo theo kiểu DongCoPhunXang, DongCoHaiKy, … thì chúng ta phải sửa lại class XeMay.
Để giải quyết vấn đề này thì người ta đưa ra một giải pháp gọi là Inversion of Control . Giải pháp là thay vì class XeMay tạo ra đối tượng của class DongCo thì chúng ta sẻ truyền nó vào cho class XeMay. Nghĩa là chúng ta thay đổi trách nhiệm tạo đối tượng "dongco" của class XeMay cho một thành phần nào đó bên ngoài(nên phương pháp này mới được gọi là IoC).

class XeMay{
private DongCo dongco;
public XeMay(DongCo dc){
dongco = dc;
}
public inserDongCo(DongCo dc){
dongco = dc;
}
}
Ở đây ta có một nguyên lý cần quan tâm đó là class chính chứa các đối tượng của class khác thì không nên phụ thuộc trực tiếp vào các class đó. Mà nên để class chính phụ thuộc vào interface hoặc abstract class của các class thành phần. Như trường hợp này thì DongCo nên là một interface của các loại động cơ khác. Khi đó chúng ta có thể truyền cho class XeMay bất kì loại động cơ nào mà không cần phải sửa code trong class XeMay.
Ví dụ : xemay = new XeMay(new DongCoBonKy());
hoặc : xemay.insertDongCo(new DongCoHaiKy());
Từ đầu đến giờ mình không nhắc đến Dependency injection là vì hai pattern này gần như tương tự nhau( không biết có phải là một ko ). Có thể dịch sơ sơ DI là tiêm vào sự phụ thuộc . Ta có thể thấy trong ví dụ thì đối tượng của class DongCo được truyền vào (tiêm vào) cho class XeMay.
package example.com;

public class XeMay {
private DongCo dongco;

public XeMay(DongCo dongco){
this.dongco = dongco;
}

public String XeNoMay(){
return dongco.NoMay();
}

public DongCo getDongco() {
return dongco;
}

public void setDongco(DongCo dongco) {
this.dongco = dongco;
}

}

package example.com;

public interface DongCo {
public String NoMay();
}

package example.com;

public class DongCoHaiKy implements DongCo {

@Override
public String NoMay() {
return "brum brum brum, hello world....";

}

}

package example.com;

public class DongCoBonKy implements DongCo {

@Override
public String NoMay() {

return "ye yae ye yee, hello wooorld......";

}

}

package example.com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args){

ApplicationContext context = 
new ClassPathXmlApplicationContext("beans.xml");

XeMay xehaiky = (XeMay) context.getBean("xebonky");
XeMay xebonky = (XeMay) context.getBean("xehaiky");

System.out.println(xehaiky.XeNoMay());
System.out.println(xebonky.XeNoMay());
}

}

Và phần cuối cùng củng là quan trọng nhất đó là các bạn viết file để cấu hình cho các thành phần
Nhấp chuột phải vào Src chọn New -> Other sau đó chọn như hình :
Chúng ta đặt tên cho file này là “beans.xml” và pass nội dung này vào :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="dongcohaiky" class="example.com.DongCoHaiKy"></bean>
<bean id="dongcobonky" class="example.com.DongCoBonKy"></bean>

<bean id="xehaiky" class="example.com.XeMay">
<constructor-arg name="dongco" ref="dongcohaiky"></constructor-arg>
</bean>

<bean id="xebonky" class="example.com.XeMay">
<constructor-arg ref="dongcobonky"></constructor-arg>
</bean>
</beans>
Bây giờ mình sẻ phân tích ví dụ này.

Trước hết chúng ta hảy xem hàm main trong class App


public static void main(String[] args){

ApplicationContext context = 
new ClassPathXmlApplicationContext("beans.xml");

XeMay xehaiky = (XeMay) context.getBean("xebonky");
XeMay xebonky = (XeMay) context.getBean("xehaiky");

System.out.println(xehaiky.XeNoMay());
System.out.println(xebonky.XeNoMay());
}

Đáng ra nếu bình thường thì chúng ta thường viết như sau :


public static void main2(String[] args){

DongCo dongcohaiky = new DongCoHaiKy();
DongCo dongcobonky = new DongCoBonKy();

XeMay xehaiky = new XeMay(dongcohaiky);
XeMay xebonky = new XeMay(dongcobonky);

System.out.println(xehaiky.XeNoMay());
System.out.println(xebonky.XeNoMay());

}

So sánh ta có thể thấy ở đây trong hàm main chúng ta không cần dùng toán tử new để khởi tạo các đối tượng xehaiky và xebonky. Chúng ta củng không phải tạo các đối tượng động cơ để truyền vào cho các đối tượng của class XeMay. Vậy thì
- ai đả tạo ra các đối tượng xehaiky, xebonky?
- Và chúng lấy các đối tượng DongCo từ đâu ?
- Và file beans.xml có ý nghĩa và vai trò như thế nào?
Trước khi trả lời nhửng câu hỏi này mình muốn nhắc lại ở phần giới thiệu về IoC và DI mình đả nói là dùng IoC để thay đổi trách nhiệm tạo ra đối tượng “dongco” của class XeMay cho một thành phần nào đó bên ngoài. Trong bài helloworld này thành phần bên ngoài đả tạo ra các đối tượng xemayhaiky, xemaybonky và các đối tượng DongCo là các thành phần của spring framework mà chúng ta đả sử dụng(đó là IoC container).

Trong Spring các đối tượng chính để xây dựng ứng dụng thì được quản lý bởi Spring IoC container. Và IoC container gọi các đối tượng đó là các bean. Một bean chỉ đơn giản là một đối tượng được khởi tạo và quản lý bởi Spring IoC container.

Ta đả biết là giữa các bean thì có sự phụ thuộc lẩn nhau vậy làm sao Spring Framework biết được chúng phụ thuộc lẩn nhau như thế nào ? Làm sao biết được cần tạo ra đối tượng nào, truyền các loại DongCo vào các đối tượng của class XeMay ra sao , … Spring sử dụng hai cách( hai loại metadata) để người lập trình có thể cấu hình các bean (tạo ra như thế nào, phụ thuộc vào ai , …) đó là dùng Annotation( Java 5 mới hổ trợ Annotation) và file XML .
Trong ví dụ này mình đả sử dụng file beans.xml để cấu hình các bean. Trước khi phân tích file beans.xml nếu bạn nào chưa biết về xml thì có thể tham khảo tại đây :
[Only registered and activated users can see links. ]
File beans.xml :


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="dongcohaiky" class="example.com.DongCoHaiKy"></bean>
<bean id="dongcobonky" class="example.com.DongCoBonKy"></bean>

<bean id="xehaiky" class="example.com.XeMay">
<constructor-arg name="dongco" ref="dongcohaiky"></constructor-arg>
</bean>

<bean id="xebonky" class="example.com.XeMay">
<constructor-arg ref="dongcobonky"></constructor-arg>
</bean>
</beans>

Trước hết là phần tử beans đây là phần tử root của file xml. Nó sẽ chứa tất cả các phần tử bean của chúng ta và ngoài ra nó còn có một số thuộc tính nhưng trong khuôn khổ bài này mình nghĩ rằng chúng ta không phải quan tâm đến chúng.
Bây giờ chúng ta sẽ xét tới phần tử bean.



<bean id="dongcohaiky" class="example.com.DongCoHaiKy"></bean>

Với phần tử bean này chúng ta sẽ cấu hình cho IoC container tạo ra một bean của class "example.com.DongCoHaiKy". Ta thấy phần tử này có hai thuộc tính “id” và “class”. Thuộc tính “id” được dùng để định danh. Một bean có thể có nhiều id, nhưng các id không được trùng nhau.Còn thuộc tính “class” là dùng để chỉ bean đó được tạo thành từ class nào.
Nhưng với phần tử bean này nó lại có phần tử con :


<bean id="xehaiky" class="example.com.XeMay"> <constructor-arg name="dongco" ref="dongcohaiky"></constructor-arg> </bean>

Trong ví dụ mình đả đưa ra thì constructor của class XeMay có một đối số truyền vào. Và phần tử con <constructor-arg> dùng để cấu hình cho IoC container biết là phải truyền đối số nào vào cho constructer của class XeMay. Ta thấy phần tử <constructor-arg > có thuộc tính “ref = “dongcohaiky” ” cấu hình cho IoC container biết được là truyền bean nào vào cho contructer của class XeMay. Giá trị của thuộc tính ref là id của bean mà chúng ta muốn truyền vào.
Bây giờ chúng ta hảy xem lại hàm main, dòng đầu tiên là :


ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Dòng này dùng để khởi tạo IoC container với phần cấu hình là file beans.xml. Và đối tượng context là đại diện cho container đó, nó có trách nhiệm khởi tạo, cấu hình các đối tượng và quản lý sự phuc thuộc giữa các đối tượng trong container.

Bạn có thể hình dung IoC container theo hình trên.


XeMay xehaiky = (XeMay) context.getBean("xebonky");

Ở đây ta dùng phương thức “getBean()” của ApplicationContext interface để truy cập bean có id =”xebonky” mà chúng ta đả cấu hình trong file beans. xml.
Để cấu hình các bean thì bạn sẻ phải sử dụng phần tử <bean></bean> trong file xml. Với phần tử này thì các bạn sẻ làm việc với các vấn đề sau :
• Namimg beans.
• Instantiating beans.
• Bean scopes.
• Dependency injection
• Autowiring collaboratiors.
• Lazy-initialized beans.
• Initialization callbacks.
• Destruction callbacks.

Chúng ta sẽ từng bước làm việc với các vấn đề này.

Naming beans:

Để beans container có thể quản lý được các bean thì chúng phải có cá định danh (giống như con người phải có tên vậy). Spring cho phép chúng ta có thể đặt cho bean một định danh hoặc có thể là nhiều định danh. Chúng ta cấu hình trên file xml thì chúng ta có thể dùng hai thuộc tính là “id “ và “name” để đặt định danh cho bean. Nếu chúng ta sử dụng id thì nó phải là duy nhất trong trong bean container. Khi bạn muốn nhiều định danh cho một bean bạn có thể dùng thuộc tính name và các định danh với name có thể cách nhau bằng khoảng trắng, dấu phẩy hoặc dấu chấm phẩy. Nếu bạn không tự định danh cho bean thì bean container sẻ tự sinh ra cho bean đó một định danh.
Với hai cách định danh trên thì các thuộc tính thuộc phần tử <bean>. Bạn có thể thêm định danh cho bean bằng cách dùng alias. Bạn có thể dùng phần tử <alias> để thêm định danh cho bean. Để dễ hiểu hơn bây giờ mình sẻ lấy một ví dụ về định danh cho bean:


File: context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="name1" name="name2,name3,name4" class="java.lang.String"/>
<alias name="name1" alias="namex1"/>
<alias name="name1" alias="namex2"/>

</beans>


using

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {
public static void main(String[] a) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("context.xml"));

String s1 = (String) factory.getBean("name1");
String s2 = (String) factory.getBean("name2");
String s3 = (String) factory.getBean("name3");
String s4 = (String) factory.getBean("name4");
String s5 = (String) factory.getBean("namex1");
String s6 = (String) factory.getBean("namex2");

System.out.println((s1 == s2));
System.out.println((s2 == s3));
System.out.println((s3 == s4));
System.out.println((s4 == s5));
System.out.println((s5 == s6));
}
}

Khi container tạo một bean thì làm sao nó biết được là cần tạo bean của class nào. Với XML thì chúng ta sử dụng thuộc tính “class” trong phần tử <bean>.
Như chúng ta đả biết trong lập trình java có hai cách để chúng ta tạo ra đối tượng. Cách thứ nhất là dùng toán tử new ( thông qua constructor). Cách thứ hai là dùng hàm static của một class nào đó(thường là một Factory). Ví dụ như :

XeMay xemay = new XeMay(); // qua constructor
XeMay xemay = HonDa.createXeMay(); //qua static method là createXeMay().

Và spring củng hổ trợ chúng ta hai cách để tạo ra bean bằng constructor và static method.

Khởi tạo bằng constructor:

bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
Như ví dụ thì thuộc tính class được gán bằng tên của class mà nó muốn tạo bean.
Khởi tạo bằng static method :

Giả sử ta có class sau :
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}

để khởi tạo đối tượng cho class đó thì ta phải cấu hình như sau :

<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
Khi đó thuộc tính class sẻ được gán bằng tên của Class có phương thức static như ví dụ trên.
Nhưng khi mà khởi tạo một đối tượng (bean) từ một đối tượng (bean) khác thì sao. Spring làm như ví dụ sau :
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
}

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator" factory-method = "createClientServiceInstance"/>
bean scope.

Khi bạn cấu hình một bean, và bean container sẻ tạo đối tượng(bean) cho bạn.Bạn sẻ muốn bean container tạo cho bạn mấy đối tượng như vậy, hay là chỉ cần tạo một đối tượng thôi thôi ,… Và để cấu hình việc này bạn phải sử dụng thuộc tính scope. Scope sẻ có nhửng giá trị sau :

Singleton : Với mổi bean container chỉ tao duy nhất một đối tượng
Prototype: tạo một đối tượng mới khi có yêu cầu
Request : tạo một đối tượng cho mổi lần có HTTP request.
Session : tạo một đối tượng cho mổi HTTP session.
globalSession : tạo một đối tượng cho một global HTTP session.

Trong phạm vi bài này mình chỉ muốn giới thiệu đến Singleton và Prototype. Vì đây là hai giá trị thường được dùng nhât. Còn ba thuộc tính còn lại chỉ sử dụng trong ứng dụng web. Và giá trị mặc định của scope là singleton.
Scope = “singleton” nghỉa là trong một bean container chỉ tạo ra một đối tượng duy nhất của bean đó. Để dễ hiểu chúng ta lấy ví dụ như sau :
File CustomerService.java
package com.example;

public class CustomerService 
{
String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

File cấu hình beans.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="customerService" 
class="com.example.CustomerService" scope="singleton" />

</beans>

File App.java

package com.example;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.example.CustomerService;

public class App 
{
public static void main( String[] args )
{
ApplicationContext context = 
new ClassPathXmlApplicationContext(new String[] {"beans.xml"});

CustomerService custA = (CustomerService)context.getBean("customerService");
custA.setMessage("Message by custA");
System.out.println("Message : " + custA.getMessage());

CustomerService custB = (CustomerService)context.getBean("customerService");
System.out.println("Message : " + custB.getMessage());
}
}

Khi chúng ta chạy ứng dụng này thì kết quả thu được sẻ là :

Message : Message by custA
Message : Message by custA

Như bạn có thể thấy ở đây khi chúng ta yều cầu bean container hai lần nhưng chỉ có một đối tượng được tạo ra.

Còn với scope=”prototype” thì mổi lần chúng ta yêu cầu bean container sẻ tạo cho chúng ta một đối tượng mới. Và với ví dụ trên nếu chúng ta thay đổi scope = “prototype” như sau :

Dependency Injection (DI) và Inversion of Control (IOC)

Dependency Injection (DI) và Inversion of Control (IOC)

DI là gì?

Bài viết này giới thiệu một sự tổng quan ở mức cao về Dependency Injection (DI). Mục đích của bài viết nhằm giới thiệu khái niệm tổng quát về DI cho những nhà phát triển ít kinh nghiệm cũng như cách sử chúng trong những DI container khác nhau như thế nào.
Dependency Injection (DI) hay Inversion of Control (IOC) ?

Nhiều tài liệu hiện nay thường đề cập đến cùng một mô hình thiết kế như Dependency Injection hoặc Inversion of Control. Tôi ưa thích DI hơn xuất phát từ sự tin tưởng rằng nó là một cái tên rõ ràng hơn. Tôi có thể hình dung nhiều kịch bản mà một người có thể tìm thấy bằng chứng của Inversion of Control, mặc dù kết quả không giống nhau để có thể tập trung vào giải quyết những gì liên quan (chẳng hạn như chuyển từ một ứng dụng console sang một vòng lặp sự kiện Windows). Tuy nhiên bạn thích iOC , bạn có thể thoải mái đọc phần còn lại của bài viết bằng việc thay thế DI với iOC. Kèm theo iOC có nhiều kiểu khác nhau (được gọi ban đầu là kiểu 1, kiểu 2, kiểu 3). Sự khác nhau giữa những kiểu này không hề ảnh hưởng đến phạm vi bài viết và không ăn nhập đến nó.

Cũng nên để ý rằng tôi đã sử dụng thuật ngữ Service mở rộng thông qua bài viết này. Điều này không hề lẫn lộn với Kiến trúc Hướng Dịch vụ (Service Oriented Architectures). Đơn giản chỉ là để dễ dàng hơn trong việc nhận biết được vai trò của “Clients” và “Services” cũng như sự đối ngược của “Client Component” và “Server Component hay Supplier Component”.
Giới thiệu đơn giản về Dependency injection

Kịch bản 1

Bạn làm việc trong một tổ chức mà ở đó, bạn và những người đồng nghiệp thường xuyên đi xa. Thông thường, bạn di chuyển bằng máy bay và mỗi lần bạn cần phải đón một chuyến bay, bạn sắp xếp đón tiếp bằng taxi. Bạn biết về đại lý vé máy bay nơi mà bán vé máy bay,và đại lý taxi nơi taxi đón bạn tai sân bay. Bạn biết số điện thoại của những nơi đó, bạn nắm chắc về các hoạt động thương thảo để đặt vé cần thiết.

Như vậy, kế hoạch chuyến đi xa của bạn thông thường phải như sau:

* Quyết định đích đến và yêu cầu ngày giờ .

* Gọi cho đại lý vé máy bay và thông báo các thông tin vần thiết để đăng ký vé máy bay.

* Gọi cho đại lý taxi, yêu cầu cho xe tới đón đúng giờ chuyến bay và thông báo thời gian cư trú của bạn (đại lý taxi lần lượt phải liên lạc với đại lý vá máy bay để lấy thông tin về chuyến bay, tính toán khoản thời gian chênh lệch giữa thời gian cư trú, đồng thời tính toán thời gian phù hợp để mà sắp xếp taxi đưa đón bạn trong thời gian cư trú).

* Lấy vé, bắt taxi và tiếp tục theo cách đó

Bây giờ, nếu như công ty bạn đột nhiên thay đổi những đại lý đó và họ đưa ra phương thức giao tiếp mới, bạn sẽ phải theo một kịch bản khác.

* Đại lý mới và phương thức liên lạc mới ( toàn bộ mọi giao dịch và liên lạc đều thông qua internet thay thế việc liên lạc qua điện thoại)

* Việc đặt vé được làm thông qua trao đổi được thực hiện thường xuyên ( Dữ liệu thay cho giọng nói ).

Điều đó không khó cho bạn, nhưng có lẽ nhiều đồng nghiệp của bạn sẽ cần sắp đặt lại chúng để có một lịch bản mới. Điều này có thể dẫn tới phải tốn một khoản thời gian lớn trong việc điều chỉnh lại xử lý.

Kịch bản 2

Bây giờ ta hãy nói tới giao thức chỉ có một chút thay đổi. Bạn có một phòng quản lý. Bất cứ khi nào bạn cần đi xa hệ thống tương tác điện thoại phòng quản lý sẽ tự động gọi cho bạn ( mà đã được kết nối với các văn phòng đại diện ). Mục đích đơn giản là xác nhận bạn đến nơi nào, ngày giờ đến để thiết lập chương trình thiết lập câu hỏi. Chuyến bay được đặt cho bạn, sắp xếp các xe taxi cho phù hợp với lịch công việc, và vé được chuyển tới cho bạn.

Bây giờ nếu văn phòng đại diện thay đổi, phòng quản lý sẽ thay đổi cho phù hợp để có thể làm việc với các văn phòng đại diện. Hệ thống tương tác điện thoại có thể thay đổi lại chương trình giao tiếp với các đại diện trên internet. Tuy nhiên bạn và những đồng nghiệp của bạn lại không biết những thay đổi đó. Bạn vẫn tếp tục theo cùng giao thức (vì phòng quản lý đã làm tất cả những gì cần thiết bạn không cần làm bất cứ điều gì khác).
Dependency injection ?

Trong cả hai kịch bản, bạn là khách hàng và bạn phụ thuộc vào những dịch vụ được cung cấp bởi những văn phòng đại diện. Tuy nhiên kịch bản 2 có một vài điểm khác biệt: 

* Bạn không cần biết số điện thoại và văn phòng đại diện nào – phòng quản lý sẽ làm chuyện đó và liên lạc với bạn khi cần thiết.

* Bạn không cần biết chính xác các cuộc đàm thoại diễn ra theo cách thức nào (nói/dữ liệu …) mặc dù bạn phải nhận biết được chuẩn đàm thoại với phòng quản lý

* Những dịch vụ mà bạn dựa vào được cung cấp theo một cách mà bạn không cần phải thay đổi nó khi mà những nhà cung cấp dịch vụ đó thay đổi.

Đó là dependency injection trong “đời sống thực”. Điều này có lẽ trông không giống như bạn hình dung một khoản chi phí cho một mình bản thân bạn – nhưng nếu như bạn hình dung một tổ chức lớn thì việc tiết kiệm là rất đáng kể.
Dependency injection trong phần mềm

Các component phần mềm (Client), thường là một phần của một tập các component với nhau phụ thuộc vào những component khác (Service) hoàn thành trọn vẹn mục đích dự định của chúng. Trong nhiều ngữ cảnh, chúng cần phải biết component nào để giao tiếp, nơi nào để xác định chúng và làm thế nào để giao tiếp với chúng. Khi cách thức mà những dịch vụ có thể được truy cập bị thay đổi, những thay đổi như thế có thể tiềm ẩn yêu cầu rất nhiều client phải thay đổi.
Một cách để cấu trúc mã là đặt các client nhúng tập hợp với việc xác định và/hoặc tạo thực thể các dịch vụ như là một phần của chúng. Một cách khác để cấu trúc mã là có các client công bố sự phụ thuộc của chúng trên các service, và có những phần “mở rộng” của mã nắm bắt nhiệm vụ của việc xác định và/hoặc tạo thực thể các dịch vụ và việc hỗ trợ đơn giản những dịch vụ liên quan đến các client khi cần thiết. Trong phương thức sau cùng, mã client thông thường không yêu cầu được thay đổi khi cách thức xác định một phần mở rộng liên quan thay đổi. Kiểu bổ sung này được xem như một sự bổ sung của Dependency injection và phần mã “mở rộng” liên quan đến trước đó được xử lý riêng hoặc bổ sung sử dụng một trong những DI framework khác nhau.

Những nỗ lực để ánh xạ kịch bản trên cho đặc ngữ thiết kế sẽ chú ý rằng sự tương tự có thể được áp dụng cho ba đặc ngữ thiết kế khác nhau – Dependency injection, Factory và Program cho một Giao tiếp và không phải là một Bổ sung. Ba đặc ngữ thiết kế này thường tương tự nhưng không cần thiết được sử dụng với nhau.

Quy ước trước đây một vài năm là phân chia interface từ implementation. Mẫu thiết kế này thậm chí cho phép che dấu sự phức tạp của các thực thể. Tuy nhiên kỹ thuật “xác định” các dịch vụ thường tách rời với các client. Hơn nữa, một vài phần của phần mềm cũng cần biết những phần phụ thuộc giữa bản thân những dịch vụ khác nhau và vì thế hoàn toàn tuyệt đối phải làm việc liên tục với việc khởi tạo component, và phải chia nhỏ và quản lý các vòng đời của chúng.

Một ví dụ, trong khi đặc tả J2EE sử dụng JNDI như một kỹ thuật để chuẩn hoá kỹ thuật xác định các dẫn xuất đối tượng, chẳng hạn như các bổ sung thường yêu cầu một lượng lớn sự thay đổi mã khi nói các client cần làm việc trong những môi trường đơn giản hơn nhiều mà ở đó JNDI không có sẵn.

Việc sử dụng DI yêu cầu phần mềm chúng ta viết phải công bố các dependency và đặt framework hoặc các container làm việc liên tục với những rắc rối của các thực thể service, khởi tạo, sắp xếp và hỗ trợ các service liên quan đến client được yêu cầu.
Một vài DI Framework

Có một số các framework sẵn có để giúp đỡ các developer. Những framework tôi thấy hữu dụng là: 

Spring Framework: một framework lớn chắc chắn đưa ra một lượng các khả năng khác tách ra từ Dependency injection

PicoContainer: một DI container framework nhỏ nhưng mạnh mẽ

HiveMind: một DI container framework khác.

Xwork: Một framework độc lập thường được sử dụng trong liên kết chung với Webwork

Src sưu tầm: internet