Spring 控制反转和依赖注入
低吟不作语 人气:0控制反转的类型
控制反转(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()); } }
<?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="oracle" name="oracle" class="com.example.OracleImpl"/> </beans>
ApplicationContext 接口是 BeanFactory 的一个扩展,除了 DI 服务外,还提供其他如事务和 AOP 等服务。在开发基于 Spring 的应用程序时,建议通过 ApplicationContext 接口与 Spring 交互
2. 设置 Spring 配置
2.1 XML 配置
对于 XML 配置,需要声明应用程序需要的由 Spring 提供的名称空间基础信息,下面所示配置仅声明用于定义 bean 的名称空间
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="provider" class="com.example.HelloWorldMessageProvider"/> <bean id="render" class="com.example.StandardOutMessageRender" p:messageProvider-ref="provider"/> </beans>
2.2 注解配置
要想在应用程序使用 Spring 的注解支持,需要在 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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.example" /> </beans>
<context:component-scan>
标记告诉 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 注入,需要在 <bean>
标记下指定 <property>
标记,为其注入一个依赖项
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="provider" class="com.example.HelloWorldMessageProvider"/> <bean id="render" class="com.example.StandardOutMessageRender"> <property name="messageProvider" ref="provider"/> </bean> </beans>
如果使用注解,只需要向 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 方式注入
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="oracle" name="oracle" class="com.example.OracleImpl"/> <!-- 使用 <constructor-arg> 标记 --> <bean id="messageProvider" class="com.example.ConfigurableMessageProvider"> <constructor-arg value="hello world" /> </bean> <!-- 使用c名称空间 --> <bean id="provider" class="com.example.ConfigurableMessageProvider" c:message="hello world"/> </beans>
使用注解方式
@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; } }
加载全部内容