• Spring 控制反转和依赖注入



    1|0控制反转的类型

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

    1|01. 依赖查找

    1|01.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|01.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();
        }
    }
    

    1|02. 依赖注入

    1|02.1 构造函数注入

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

    public class ConstructorInjection {
    
        private Dependency dependency;
    
        public ConstructorInjection(Dependency dependency) {
            this.dependency = dependency;
        }
    
        @Override
        public String toString() {
            return dependency.toString();
        }
    }
    
    1|02.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 注解进行自动装配时将介绍该注入类型


    2|0Spring 中的控制反转

    1|01. 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 交互

    1|02. 设置 Spring 配置

    1|02.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>
    
    1|02.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

    1|02.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();
        }
    }
    

    1|03. 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;
        }
    }
    

    1|04. 构造函数注入

    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;
        }
    }
    


    __EOF__

    本文作者YeeQan
    本文链接https://www.cnblogs.com/Yee-Q/p/15914321.html
    关于博主:评论和私信会在第一时间回复。或者直接私信我。
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
  • 相关阅读:
    Linux READ_ONCE/WRITE_ONCE宏
    Go语言基础学习教程(一)导学部分
    认识etcd
    Leetcode 429:N叉树的层次遍历
    【Rust 入门学习】1.2 使用 Cargo 构建 Rust 项目
    基于非洲秃鹫算法优化概率神经网络PNN的分类预测 - 附代码
    TP5 模型查询的返回值、返回值的判断以及所使用的SQL
    【算法leetcode】222. 完全二叉树的节点个数(rust和go)
    vue nextTick源码分析
    测开 - 自动化测试 selenium - 自动化概念 && 测试环境配置 - 细节狂魔
  • 原文地址:https://www.cnblogs.com/Yee-Q/p/15914321.html