zoukankan      html  css  js  c++  java
  • 第2章—装配Bean—自动化装配Bean

    自动化装配Bean

    2.1.Spring配置可选方案

    ​ 装配是依赖注入DI的本质,Spring提供了以下三种注入的装配机制:

    • 在XMl中进行显式配置
    • 在java中进行显式配置
    • 隐式的Bean发现机制和自动装配

    2.2.自动化装配Bean

    ​ Spring从两个角度来实现自动化装配:

    • 组件扫描(component scanning):Spring会自动发现用用上下文中所创建的Bean.
    • 自动装配(autowiring):Spring自动满足bean之间的依赖.

    实例:

    CD和CDPlayer,如果你不将CD放入(注入)到播放器中,那么CD播放器其实是没多大用处的,所以说,CD播放器是依赖于CD才能完成它的使命.

    package com.CDDemo;
    //CD的接口
    public interface CompactDisc {
       public void play();
    }
    
    
    package com.CDDemo;
    import org.springframework.stereotype.Component;
    //CD的实现类 歌曲
    @Component
    public class SgtPeppers implements CompactDisc {
        private String title = "sgt";
        private String song = "Twinkle, twinkle, little start";
        public void play() {
            System.out.println("title:" + title + "song:" + song);
        }
    }
    
    
    package com.CDDemo;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    //播放器的设置
    @Configuration
    @ComponentScan
    public class CDPlayerConfig {
    
    }
    
    
    package com.CDDemo;
    //测试类
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import static org.junit.Assert.assertNotNull;
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = CDPlayerConfig.class)
    public class CDTest {
        @Autowired
        private CompactDisc cd;
        @Test
        public void cdShouldNotBeNull() {
          assertNotNull(cd);
          cd.play();
        }
    }
    
    

    上面需要注意的是:

    @Configuration

    定义了Spring的装配规则

    @ComponentScan

    这个会默认扫描与配置类相同的包.(这里扫描package com.CDDemo;同包以及下面的所有的子包)

    查找出带有@Component注解的类 这样就能发现CompactDisc(因为注解了它的实例) 并为它在Spring中创建一个Bean.

    当然你也可以通过配置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:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    			http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    			http://www.springframework.org/schema/mvc
    			http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
    			 http://www.springframework.org/schema/aop
                 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    			http://www.springframework.org/schema/context
    			http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!--扫描包的配置-->
    <context:component-scan base-package="com.CDDemo"/>
    </beans>
    

    2.2.1.为组件扫描的Bean命名

    通过Spring的注解可以让Spring给注入的Bean一个ID,这个ID默认为类名(首字母小写);

    当然你也可以通过命名来明确给这个类注入的时候带有什么名称的ID;

    1.@Componment("test_SgtPeppers")

    2.@Name("test_SgtPeppers")

    当然我们一般情况下直接用@Componment使用ID默认为类名(首字母小写)就可以了,因为这样用起来比较整洁,特殊情况可以在@Componment()的括号中加入指定ID名称.

    2.2.2.设置组件扫描的基础包

    ​ 在上面的@ComponentScan默认扫描注解类所在的包,那么有没有一种方式能够更为精准有效的扫描我们所需要的包呢?在Spring中有如下几种方式可供选择:

    1.在ComponentScan的Value中直接指明包的名称

    //在ComponentScan中直接指明包的名称
    @ComponentScan("com.CDDemo")
    public class CDPlayerConfig {
    
    }
    

    2.在ComponentScan中basePackages的直接指明包的名称 是复数 可以指定扫描多个包

    @ComponentScan(basePackages = "com.CDDemo")
    public class CDPlayerConfig {
    
    }
    
    @ComponentScan(basePackages = {"com.CDDemo","com.aop"})
    public class CDPlayerConfig {
    
    }
    

    3.在ComponentScan中的basePackageClasses加入扫描的类名(会自动扫描类所在的包)

    @ComponentScan(basePackageClasses = {SgtPeppers.class,CDTest.class})
    public class CDPlayerConfig {
    
    }
    

    2.2.3.通过为Bean添加注解实现自动装配

    ​ 在Spring中还有种方便而且应用广泛的方法,在需要依赖注入的地方加上@Autowired,这个表明当Spring创建CompactDisc Bean的时候,会通过这个传入该接口的实例类的Bean.

    public class CDTest {
        @Autowired
        private CompactDisc cd;
    }
    
    

    ​ @Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上,(但是虽然用在Getter方法上不报错,但没有实际意义, 请看下文的解释)

    @Autowired
        public void setTitle(String title) {
            this.title = title;
        }
    

    先看一下bean实例化和@Autowired装配过程:

    1. 一切都是从bean工厂的getBean方法开始的,一旦该方法调用总会返回一个bean实例,无论当前是否存在,不存在就实例化一个并装配,否则直接返回。(Spring MVC是在什么时候开始执行bean的实例化过程的呢?其实就在组件扫描完成之后)
    2. 实例化和装配过程中会多次递归调用getBean方法来解决类之间的依赖。
    3. Spring几乎考虑了所有可能性,所以方法特别复杂但完整有条理。
    4. @Autowired最终是根据类型来查找和装配元素的,但是我们设置了<beans default-autowire="byName"/>后会影响最终的类型匹配查找。因为在前面有根据BeanDefinition的autowire类型设置PropertyValue值得一步,其中会有新实例的创建和注册。就是那个autowireByName方法。

    另外@Autowired注解还可以用在方法上面,能和Setter方法上使用该注解发挥相同的作用

     @Autowired
        public void insertDisc() {
            System.out.println("注解在方法名上");
        }
    

    以上方法都是有匹配的Bean的情况下有效的,那如果找不到匹配的Bean的话,Spring会抛出一个异常.为了避免出现这种异常,你可以将@Autowired的require属性设置为false,但还是建议根据实际情况找到相应的问题并解决:

    @Autowired(required = false)
        public void insertDisc() {
            System.out.println("注解在方法名上");
        }
    

    但是把required改为false需谨慎,如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性随时可能出现NullPointerException.

    ​ 但你不想在代码中导出使用@Autowired的时候可以考虑用@Inject替换,但这两个之间有些许差别,一般可以相互替换.

    import javax.inject.Inject;
    import javax.inject.Named; 
    @Named
    public class SgtPeppers implements CompactDisc {
        private String title = "sgt";
        private String song = "Twinkle, twinkle, little start";
        public void play() {
            System.out.println("title:" + title + "song:" + song);
        }
        @Inject
        public void insertDisc() {
            System.out.println("注解在方法名上");
        }
    }
    

    请注意:

    1、@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject;实现注入。

    2、@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;

    3、@Inject可以作用在变量、setter方法、构造函数上。

  • 相关阅读:
    IO 单个文件的多线程拷贝
    day30 进程 同步 异步 阻塞 非阻塞 并发 并行 创建进程 守护进程 僵尸进程与孤儿进程 互斥锁
    day31 进程间通讯,线程
    d29天 上传电影练习 UDP使用 ScketServer模块
    d28 scoket套接字 struct模块
    d27网络编程
    d24 反射,元类
    d23 多态,oop中常用的内置函数 类中常用内置函数
    d22 封装 property装饰器 接口 抽象类 鸭子类型
    d21天 继承
  • 原文地址:https://www.cnblogs.com/charlypage/p/9215601.html
Copyright © 2011-2022 走看看