zoukankan      html  css  js  c++  java
  • Spring学习之旅(三)--装配Bean

    装配 Bean 的方式

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

    Spring 提供了以上三种方式进行 Bean 的配置,可以根据自己的需求选择一种或者混合使用。但是我的个人建议还是尽可能的使用自动配置机制,毕竟显式的配置越少越方便。但如果必须要显示的配置 bean 的时候,推荐使用比 XML 类型安全更好的 JavaConfig 方式。

    自动化装配 Bean

    Spring 通过如下两个方式来实现自动化装配:

    • 组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的 bean

    • 自动化装配(autowiring): Spring 自动满足 bean 之间的依赖。

    实例

    1.添加 maven 依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.20.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.20.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    

    2.创建一个 bean

    @Component
    public class CompactDisc {
    
        public void play(){
            System.out.println("开始播放");
        }
        public void pause(){
            System.out.println("暂停播放");
        }
    
        public void stop(){
            System.out.println("停止播放");
        }
    }
    

    @Component 注解表示申明这个类为组件类。


    3.创建一个配置类并开启组件扫描

    @Configuration
    @ComponentScan
    public class ApplicationConfig {
    
    
    }
    

    @Configuration 表示这是一个配置类
    @ComponentScan 表示开启组件扫描


    4.创建单元测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes={ApplicationConfig.class})
    public class CompactDiscTest {
    
        @Autowired
        private CompactDisc compactDisc;
        @Test
        public void test(){
            compactDisc.play();
        }
    
    }
    

    运行,查看控制台输出:

    开始播放
    

    bean 命名

    Spring 中如果没有明确给 bean 指定一个 ID 的话,Spring 会将类名首字母小写作为它的默认 ID。 如果要指定 ID 的话,可以采用如下形式的注解:

    @Component("指定ID名")
    

    @ComponentScan

    像上面的实例中,没有指定扫描包的路径的话,会默认将当前类所在的包当作基础包来扫描(即:只扫描当前包和子包)。

    如果需要扫描其他包的话需要使用 basePackagesbasePackageClasses 属性,两者的区别在于一个接收的参数是字符型而另一个接收的参数是类。并且它们两个都是复数的形式,表示它们可以接收多个值。

    @ComponentScan(basePackages = {"com.marklogzhu.bean","com.marklogzhu.service"})
    
    @ComponentScan(basePackageClasses = {CompactDisc.class,UserService.class})
    

    basePackageClasses 相比于 basePackages 属性更安全,不会因为重构包名导致路径错误,basePackageClasses 申明类所在的包就是作为扫描的基础包。

    注:可以在这些包里新建一个与功能无关的空接口来作为 basePackageClasses 的值,避免因为功能重构导致 类/接口 被移除。


    自动装配

    在实例中我们通过 @Autowired 注解 实现了 bean 的自动装配。除了属性之外还可以在方法上也增加 @Autowired 注解 实现 bean 的注入。

    如果没有匹配到 bean 的话,Spring 将会抛出一个 UnsatisfiedDependencyException 异常。为了避免此异常的出现,可以将 @Autowiredrequired 属性设置为 false。当 Spring 匹配不到 bean 的时候会将这个 bean 设置为未装配状态。 要注意的是如果你的代码没有对这个 bean 进行 null 检查的话,就会抛出 NullPointerException 异常,所以请谨慎使用这个属性。

    如果匹配到多个 bean 的话,Spring 也会抛出一个 NoUniqueBeanDefinitionException 异常,表示没有明确指明使用哪个 bean 来进行自动装配。

    @AutowiredSpring 特有的注解,如果你不想使用它的话,也可以使用 jsr330规范@Inject 注解,两者的功能在大多数情况下都是一样的。


    JavaConfig 显式装配

    在使用第三方库的时候就无法使用自动化配置,只能采用显式装配。显式装配有两种方式:

    • JavaConfig
    • XML

    这里我们先讲 JavaConfigJavaConfig 从语法上和普通的 Java 代码没有区别,但是概念上却有所区别,它不应该包含任何业务逻辑。一般来说都会将这些配置类单独放到一个包下,使其和业务逻辑相分离。


    实例

    1.JavaConfig 显式声明 Bean

    我们移除之前的 ApplicationConfig 类上的 @ComponentScan 注解。采用 JavaConfig 的方式申明 Bean

    @Configuration
    public class ApplicationConfig {
    
        @Bean
        public CompactDisc compactDisc(){
            return new CompactDisc();
        }
    
    }
    

    2.运行之前的单元测试类,查看控制台输出

    开始播放
    

    可以看到结果跟之前的一致。


    通过 @Bean 注解创建的 beanId 默认就是方法名,如果需要命名成不同的也可以使用 @Bean 注解name 属性。

    如果一个 bean 的创建需要另一个 bean 的话,可以采用如下的方式声明:

    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc){
        return new CDPlayer(compactDisc);
    }
    

    XML 显式装配

    Spring 刚刚出现的时候,XML 是描述配置的主要方式,但是现在已经有了自动化配置JavaConfig 显式配置XML 的使用应该只是用于维护老项目而不是使用到新的项目中去。

    实例

    1.创建 xml 文件 等同于 @Configuration 注解

    <?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">
    
    
    </beans>
    

    2.申明 bean 等同于 @Bean 注解

    <bean id="compactDisc" class="com.marklogzhu.bean.CompactDisc"/>
    

    可以没有设置 Id 的话,默认名就会是 com.marklogzhu.bean.CompactDisc#0 。其中 #0 是一个计数的形式,用于和其他 bean 区分。

    3.新建单元测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class XmlTest {
    
        @Autowired
        private CompactDisc compactDisc;
        
        @Test
        public void test() {
            compactDisc.play();
        }
    
    }
    

    运行查看控制台输出:

    开始播放
    

    有这么一个班级类,它里面有班级名称、一个老师、一群学生和课程列表,我们来看看怎么通过 **xml ** 形式注入:

    public class Class {
    
        private String name;
    
        private Teacher teacher;
    
        private List<Student> students;
    
        private List<String> courses;
    
        public Class(){
            
        }
    
        public Class(String name) {
            this.name = name;
        }
    
        public Class(String name, Teacher teacher) {
            this(name);
            this.teacher = teacher;
        }
    
        public Class(String name, Teacher teacher, List<String> courses) {
            this(name,teacher);
            this.courses = courses;
        }
    
        public Class(String name,Teacher teacher, List<String> courses, List<Student> students) {
            this(name,teacher,courses);
            this.students = students;
        }
    
        public String getName() {
            return name;
        }
    
        public Teacher getTeacher() {
            return teacher;
        }
    
        public List<Student> getStudents() {
            return students;
        }
    
        public List<String> getCourses() {
            return courses;
        }
        public void setName(String name) {
            this.name = name;
        }
    
        public void setTeacher(Teacher teacher) {
            this.teacher = teacher;
        }
    
        public void setStudents(List<Student> students) {
            this.students = students;
        }
    
        public void setCourses(List<String> courses) {
            this.courses = courses;
        }
    }
    
    
    public class Teacher {
    
        public String startWorking() {
            return "老师教学";
        }
    }
    
    public class Student {
    
        public String startWorking() {
           return "学生学习";
        }
    }
    

    构造器注入--字符串

    <bean id="aClass" class="com.marklogzhu.bean.xml.Class">
        <constructor-arg  name="location" value="B栋二楼"/>
    </bean>
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class XmlTest {
    
        @Autowired
        private Class javaClass;
    
        @Test
        public void test_01() {
            Assert.assertEquals(javaClass.getName(),"Java学习");
        }
      
    }
    

    注:constructor-arg 不显式声明 name 属性那么将会按先后顺序赋值。

    构造器注入--对象

    <bean id="teacher" class="com.marklogzhu.bean.xml.Teacher"/>
    
    <bean id="aClass" class="com.marklogzhu.bean.xml.Class">
        <constructor-arg name="name" value="Java学习"/>
        <constructor-arg name="teacher" ref="teacher"/>
    </bean>
    
    @Test
    public void test_02() {
        Assert.assertEquals(javaClass.getName(),"Java学习");
        Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
    }
    

    构造器注入--字符串 List

    <bean id="aClass" class="com.marklogzhu.bean.xml.Class">
        <constructor-arg name="name" value="Java学习"/>
        <constructor-arg name="teacher" ref="teacher"/>
        <constructor-arg name="courses">
            <list>
                <value>JavaSe</value>
                <value>Sql</value>
                <value>JS</value>
            </list>
        </constructor-arg>
    </bean>
    
    @Test
    public void test_03() {
        Assert.assertEquals(javaClass.getName(),"Java学习");
        Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
        Assert.assertEquals(javaClass.getCourses().size(),3);
    }
    

    构造器注入--对象 List

    <bean id="aClass" class="com.marklogzhu.bean.xml.Class">
        <constructor-arg name="name" value="Java学习"/>
        <constructor-arg name="teacher" ref="teacher"/>
        <constructor-arg name="courses">
            <list>
                <value>JavaSe</value>
                <value>Sql</value>
                <value>JS</value>
            </list>
        </constructor-arg>
        <constructor-arg name="students">
            <list>
                <ref bean="student"/>
                <ref bean="student"/>
            </list>
        </constructor-arg>
    </bean>
    
    @Test
    public void test_04() {
        Assert.assertEquals(javaClass.getName(),"Java学习");
        Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
        Assert.assertEquals(javaClass.getCourses().size(),3);
        Assert.assertEquals(javaClass.getStudents().size(),2);
    }
    

    属性注入

    <bean id="aClass" class="com.marklogzhu.bean.xml.Class">
        <property name="name" value="Java学习"/>
        <property name="teacher" ref="teacher"/>
        <property name="courses">
            <list>
                <value>JavaSe</value>
                <value>Sql</value>
                <value>JS</value>
            </list>
        </property>
        <property name="students">
            <list>
                <ref bean="student"/>
                <ref bean="student"/>
            </list>
        </property>
    </bean>
    
    @Test
    public void test_05() {
        Assert.assertEquals(javaClass.getName(),"Java学习");
        Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
        Assert.assertEquals(javaClass.getCourses().size(),3);
        Assert.assertEquals(javaClass.getStudents().size(),2);
    }
    

    可以看到我们把之前的 constructor-arg 替换为 property 了,但是单元测试还是可以通过,说明属性注入成功了。

    注:属性注入的前提是类中有 setXX 方法存在。

    JavaConfig 显式装配 和 XML 配置混合使用

    在 JavaConfig 显式装配中引用 XML 配置

    @ImportResource("classpath:applicationContext.xml")
    

    在 XML 配置中引用 JavaConfig 显式装配

      <bean id="compactDisc" class="com.marklogzhu.bean.CompactDisc"/>
    
  • 相关阅读:
    PHP 快递单号查询api接口源码指导
    电商平台适用基础快递查询api接口对接demo解决方案
    智能物流查询api接口demo(php示例)
    解决在TP5中无法使用快递鸟查询API接口方案
    解析快递鸟在线预约取件API接口对接编码
    快递鸟批量打印电子面单接口及控件安装
    「note」原根照抄
    「atcoder
    Solution -「NOI 2021」轻重边
    Solution Set -「ARC 124」
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/11400339.html
Copyright © 2011-2022 走看看