SPRING-SOURCE.RU

Понимание контейнера инверсии контроля (IoC) Spring

Обсудим следующий сегмент кода:

                        
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/WroxJDBCDS”);
Connection conn = ds.getConnection("user”, "pass”);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery( "SELECT * FROM Cust” );
while( rs.next() )
System.out.println( rs.getString(1)) ;
rs.close() ;
stmt.close() ;
conn.close() ;
con.close();
		

Этот код получает JDBC (Java Database Connectivity) DataSource из контейнера используя JNDI (Java Naming and Directory Interface). В J2EE это нужно для получения JDBC соединения. Если разбирать по шагам, то компонент получает DataSource через следующие шаги:

  1. Получение нового JNDI InitialContext() из контейнера
  2. Делаем поиск ресурса
  3. Кладем возвращенный ресурс в DataSource

Обсудим следующий код из Spring компонента:

                        
private DataSource ds;
public void setDs(DataSource datasource) {
ds = datasource;
}
...
Connection conn = ds.getConnection("user”, "pass”);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery( "SELECT * FROM Cust” );
while( rs.next() )
System.out.println( rs.getString(1)) ;
rs.close() ;
stmt.close();
conn.close();
		

Этот код делает тоже самое, что и предыдущий сегмент кода. Тем не менее, фактически компонент обращался к контейнеру за DataSource. Сейчас, код просто использует private переменную ds, не зная какой именно DataSource будет в нее записан.

DataSource создается не внутри компонента. Здесь, наоборот компонент получает нужный DataSource через setDs метод. Все это и есть инверсия контроля. Контроль над используемыми ресурсами инвертирован, контроль перешел от компонента к контейнеру и его конфигурации. В этом случае, Spring контейнер принимает решение вместо компонента.

Конечно, в Spring, DataSource может быть связан во время старта посредством редактирования beans.xml файла.

                        
<bean name="wroxBean"
class="com.wrox.beginspring.DataBean">
<property name="ds" ref="jdbcds" />
</bean>
		

Обсудим рисунок

Spring IoC контейнер

Компонент на левой стороне – это стандартный не IoC компонент, и он обращается к контейнеру за двумя ресурсами (beans). На правой стороне контейнер создает необходимые ресурсы и затем делает инъекцию внутрь компонента, эффективно инвертируя контроль над выбранными ресурсами от компонента к контейнеру.

Инъекция зависимости

В предыдущем коде инъекция делается в компонент через setDs метод, эта техника получила название как инъекция зависимости. Фактически, использование setter метода, для инъекции DataSource, называется как setter инъекция. Spring также поддерживает constructor инъекцию, где зависимый ресурс вводится через конструктор.

Давайте попробуем. Создаем инъекцию зависимости

Чтобы увидеть инъекцию зависимости в действии вам не нужно перекомпилировать свой проект, так как бины связаны через файл описания контекста.

  1. Запускаем CalculateSpring:
    mvn exec:java -Dexec.mainClass=com.wrox.begspring.CalculateSpring –Dexec.args="3000 3"
    Текущий бин связывается автоматически при помощи Operation, который складывает два числа.

    Результат должен быть таким: The result of 3000 plus 3 is 3003!

  2. Теперь сменим директорию на target/classes
  3. Сейчас нужно изменить beans.xml файл. Здесь мы укажем, что будем использовать Multiply вместо операции суммирования.

                            
    <?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-2.0.xsd”>
    <bean id=”screen” class=”com.wrox.begspring.ScreenWriter” />
    <bean id=”multiply” class=”com.wrox.begspring.OpMultiply” />
    <bean id=”add” class=”com.wrox.begspring.OpAdd” />
    <bean id=”opsbean” class=”com.wrox.begspring.CalculateSpring”>
    <property name=”ops” ref=”multiply” />
    <property name=”writer” ref=”screen”/>
    </bean>
    </beans>
    		

  4. Снова меняем директорию (возвращаемся к файлу pom.xml) и пробуем снова запустить программу:
    mvn exec:java -Dexec.mainClass=com.wrox.begspring.CalculateSpring –Dexec.args="3000 3"

    Результат будет следующим: The result of 3000 times 3 is 9000!

Поведение приложения было изменено и различные компоненты вводятся через setter метод, без какой-либо перекомпиляции.

Как это работает

Код компонента CalculateSpring имеет setter метод для ops свойства, это дает возможность контейнеру Spring IoC делать инъекцию в компонент во время выполнения. Следующий код показывает setter метод:

                        
package com.wrox.begspring;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CalculateSpring {
private Operation ops;
private ResultWriter wtr;
public void setOps(Operation ops) {
this.ops = ops;
}
public void setWriter(ResultWriter writer) {
wtr = writer;
}
		

Когда CalculateSpring бин соединен в beans.xml, setter инъекция зависимости используется для связывания реализации OpMultiply, вместо бывшего OpAdd, с ops свойством. Эта setter инъекция выделена в следующем коде:

                        
<bean id=”opsbean” class=”com.wrox.begspring.CalculateSpring”>
<property name=”ops” ref="multiply" />
<property name="writer" ref="screen"/>
</bean>
		

Как вы могли увидеть, инъекция зависимостей дает возможность JavaBean компонентам изменять связи без компиляции в IoC контейнере.