Spring 控制反转和依赖注入


控制反转的类型

控制反转(IOC)旨在提供一种更简单的机制,来设置组件的依赖项,并在整个生命周期管理这些依赖项。通常,控制反转可以分成两种子类型:依赖注入(DI)和依赖查找(DL),这些子类型各自又可以被进一步分解为 IOC 服务的具体实现

1. 依赖查找

1.1 依赖拉取

依赖拉取(Dependency Pull),即根据需要,从注册表中提取依赖项,以下代码显示了基于 Spring 的依赖拉取

public class DependencyPull {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/app-context.xml");
        ctx.getBean("renderer", MessageRenderer.class);
    }
}
1.2 上下文依赖查找

上下文依赖查找(contextualized dependency lookup,CDL),同样属于依赖查找的子类型,和依赖拉取有点类似,但在 CDL 中,查找是针对管理资源的容器执行的,这个容器通常由应用程序服务器或框架(Tomcat、JBoss、Spring)提供,比如以下代码显示了一个提供依赖查找服务的容器接口

public interface Container {
    // 根据key获取相应的依赖项
    Object getDependency(String key);
}

CDL 通过让组件实现以下代码接口来进行工作

public interface ManagedComponent {
    void performLookup(Container container);
}

组件需要实现该接口,当容器准备好将依赖项传递给组件时,会依次调用每个组件的 performLookup() 方法,然后组件就可以使用 Container 接口查找所需的依赖项

public class ContextualizedDependencyLookup implements ManagedComponent {

    private Dependency dependency;

    @Override
    public void performLookup(Container container) {
        this.dependency = (Dependency) container.getDependency("myDependency");
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

2. 依赖注入

2.1 构造函数注入

当在组件的构造函数中提供依赖项时,就会发生构造函数依赖注入

public class ConstructorInjection {

    private Dependency dependency;

    public ConstructorInjection(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}
2.2 setter 函数注入

Ioc 容器通过 JavaBean 样式的 setter 方法注入组件的依赖项

public class SetterInjection {

    private Dependency dependency;

    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

在 Spring 中,还支持另一种被称为字段注入(field injection)的注入类型,在后面学习使用 @Autowire 注解进行自动装配时将介绍该注入类型


Spring 中的控制反转

1. Bean 和 BeanFactory

Spring 的依赖注入容器的核心是 BeanFactory,它负责管理组件,包括依赖项以及它们的生命周期。如果我们想获得一个组件(Bean),就必须创建一个实现了 BeanFactory 接口的实例,并对其进行配置

虽然 BeanFactory 可以通过编程方式配置,但更常见的做法是使用某种配置文件在外部对其进行配置。Bean 配置可以由实现 BeanDefinition 接口的类的实例来表示,对于任何实现了 BeanDefinitionReader 接口的 BeanFactory 实现类来说,都可以使用 PropertiesBeanDefinitionReader 或 XmlBeanDefinitionReader 从配置文件读取 BeanDefinition 数据

定义一组接口:

public interface Oracle {
    String output();
}

public class OracleImpl implements Oracle {

    @Override
    public String output() {
        return "hello world";
    }
}

接下来我们来看一看,Spring 的 BeanFactory 如何被初始化并用于获取 Bean 实例

public class XmlConfigWithBeanFactory {

    public static void main(String[] args) {
        // DefaultListableBeanFactory是Spring提供的两个主要BeanFactory实现之一
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
        // 使用XmlBeanDefinitionReader从XML文件读取BeanDefinition信息
        rdr.loadBeanDefinitions(new ClassPathResource("spring/xml-bean-factory-config.xml"));
        // 使用在XML配置文件中配置的名称oracle来获取bean
        Oracle oracle = (Oracle) factory.getBean("oracle");
        System.out.println(oracle.getInfo());
    }
}



    


ApplicationContext 接口是 BeanFactory 的一个扩展,除了 DI 服务外,还提供其他如事务和 AOP 等服务。在开发基于 Spring 的应用程序时,建议通过 ApplicationContext 接口与 Spring 交互

2. 设置 Spring 配置

2.1 XML 配置

对于 XML 配置,需要声明应用程序需要的由 Spring 提供的名称空间基础信息,下面所示配置仅声明用于定义 bean 的名称空间




    

    


2.2 注解配置

要想在应用程序使用 Spring 的注解支持,需要在 XML 配置中声明




    


标记告诉 Spring 扫描代码,从而找到 @Component 等注解注入的 bean,以及支持在指定包(及其所有子包)下使用 @Autowire 等注解的 bean

2.3 Java 配置

配置类使用 @Configuration 注解,并包含用 @Bean 注解的方法,这些方法由 IOC 容器直接调用来实例化 bean,bean 名称与用于创建它的方法的名称相同

@Configuration
public class HelloWorldConfiguration {

    @Bean
    public MessageProvider provider() {
        return new HelloWorldMessageProvider();
    }

    @Bean
    public MessageRender render() {
        StandardOutMessageRender render = new StandardOutMessageRender();
        render.setMessageProvider(provider());
        return render;
    }
}

如果想从该类中读取配置信息,需要一个不同的 ApplicationContext 实现

public class HelloWorldSpringAnnotated {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx
                = new AnnotationConfigApplicationContext(HelloWorldConfiguration.class);
        MessageRender render = ctx.getBean("render", MessageRender.class);
        render.render();
    }
}

3. setter 注入

使用 XML 配置来配置 setter 注入,需要在 标记下指定 标记,为其注入一个依赖项



    

    
        
    


如果使用注解,只需要向 setter 方法添加一个 @Autowired 注解

@Service("render")
public class StandardOutMessageRender implements MessageRender {

	...

    @Override
    @Autowired
    public void setMessageProvider(MessageProvider messageProvider) {
        this.messageProvider = messageProvider;
    }
}

4. 构造函数注入

public class ConfigurableMessageProvider implements MessageProvider {

    private String message;

    public ConfigurableMessageProvider(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return null;
    }
}

使用 XML 方式注入




    

    
    
        
    

    
    


使用注解方式

@Service
public class ConfigurableMessageProvider implements MessageProvider {

    private String message;

    @Autowired
    public ConfigurableMessageProvider(
            @Value("hello world") String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return null;
    }
}

你可能感兴趣的:(Spring 控制反转和依赖注入)