springioc基于xml的di详细配置全解两万字(代码片段)

刘Java 刘Java     2023-01-02     532

关键词:

详细介绍了Spring IoC基于XML的DI详细配置全解。

前面的文章Spring IOC容器的概念以及基于XML的IoC装配中,我们学习了IoC容器的概念以及依赖注入的两种方式,现在我们来看看一些更详细的配置。本文基于Spring5.2.8,案例基于上一篇文章的案例。

1 value字面量

对于基本类型、String、包装类类型的属性,我们可以直接使用value属性的字符串值来描述具体的值,这样可读性也更强。在最后注入的时候Spring的转换服务会将这些值从 String 转换为属性或参数的实际类型。

并且Spring支持使用< value >标签表示具体的字面量值:

<bean id="simpleSetterBased" class="com.spring.core.SimpleSetterBased">
    <constructor-arg name="property1">
        <value>xxx</value>
    </constructor-arg>
    <constructor-arg name="property2">
        <value>yyy</value>
    </constructor-arg>
    
    <!--setter方法 name表示属性名 value 表示属性值-->
    <property name="property3">
        <value>123</value>
    </property>
    <property name="property4">
        <value>false</value>
    </property>
</bean>

1.1 Properties快捷转换

Spring容器支持通过PropertyEditor直接解析value中的特定格式的字符串字面量值,并转换为一个Properties集合。后面我们也会学习集合的注入方式,但是这是一个非常好用的快捷方式!

我们来测试一下,首先有一个PropertiesDI类,内部有一个Properties(Hashtable是Properties的父类)属性:

/**
 * @author lx
 */
public class PropertiesDI 

    private Hashtable properties;

    /**
     * setter
     */
    public void setProperties(Properties properties) 
        this.properties = properties;
    

    @Override
    public String toString() 
        return "PropertiesDI" +
                "properties=" + properties +
                '';
    

配置如下:

<!--properties-->
<bean class="com.spring.core.PropertiesDI" id="propertiesDI">
    <property name="properties">
        <!--直接写配置即可,自动转换为Properties-->
        <value>
            ! 注释
            # 注释
            # “#”“!”开头的一行被算作注释不会解析。
            # key和value可以使用 “=”、“:”、“ ”等符号分割,详见properties说明


            key=value
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
            ccc:ddd
            aaa bbb
            eee    fff
        </value>
    </property>
</bean>

测试:

@Test
public void properties() 
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("propertiesDI", PropertiesDI.class));

结果如下,成功注入:

[propertiesDI]
PropertiesDIproperties=jdbc.url=jdbc:mysql://localhost:3306/mydb, jdbc.driver.className=com.mysql.jdbc.Driver, eee=fff, key=value, aaa=bbb, ccc=ddd

2 引用其他bean

2.1 ref引用

在< constructor-arg >、< property >、< entry >标签中有一个ref属性,用于将bean的指定属性的值设置为对容器管理的另一个bean的引用。这就是引用类型属性依赖的设置方式。被引用的bean是要设置其属性的bean的依赖项,在设置该属性之前,需要对其进行初始化。ref属性的值需要与引用的目标bean的id或者name属性中的一个值相同。

当然还有一个< ref >标签,可以作为< constructor-arg >、< property >以及某些集合标签的子标签,通过< ref >标签的bean属性也可以来指定引用的目标bean。< ref >标签允许在同一容器或父容器中创建对任何bean的引用,而不管它是否在同一XML文件中。bean属性的值需要与引用的目标bean的id或者name属性中的一个值相同。

如下案例,首先有一个RefDI类,用于ref测试:

/**
 * @author lx
 */
public class RefDI 

    private HelloSpring helloSpring1;
    private HelloSpring helloSpring2;

    public RefDI(HelloSpring helloSpring1, HelloSpring helloSpring2) 
        this.helloSpring1 = helloSpring1;
        this.helloSpring2 = helloSpring2;
    

    @Override
    public String toString() 
        return "RefDI" +
                "helloSpring1=" + helloSpring1 +
                ", helloSpring2=" + helloSpring2 +
                '';
    

配置文件:

<!--ref-->
<!--定义一个Bean-->
<bean name="helloSpring3" class="com.spring.core.HelloSpring"/>

<bean class="com.spring.core.RefDI" id="refDI">
    <!--使用ref属性引用helloSpring3的bean-->
    <constructor-arg name="helloSpring1" ref="helloSpring3"/>

    <!--使用ref标签引用helloSpring3的bean-->
    <constructor-arg name="helloSpring2">
        <ref bean="helloSpring3"/>
    </constructor-arg>
</bean>

测试:

@Test
public void ref() 
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("refDI", RefDI.class));

成功注入了其他bean:

初始化
[helloSpring3, refDI]
RefDIhelloSpring1=HelloSpringhello='hello', helloSpring2=HelloSpringhello='hello'

2.2 parent继承

< bean >、< ref >等标签中还有一个parent属性,这个属性用于指定目标bean将使用父bean的属性和配置,除了autowire、scope和lazy init属性,parent属性用于相同属性以及值的复用。parent属性的值与目标父bean的id属性或name属性中的一个值相同。

对于parent继承,这里又分几种情况:

  1. 如果父bean有class属性,而子bean没有class属性,那么子bean就是和父bean同一个class类型,相当于创建两个相同的对象。
  2. 如果父bean有class属性,而子bean也有class属性,那么允许它们是不同的类型,但是子bean必须含有父bean中定义的所有的注入方式。
  3. 如果父bean没有class属性,那么子bean必须定义class属性,这个父bean实际上类似于一个属性和值的模版,仅仅被值bean引用,实现配置复用,不能实例化,(这时父bean必须添加abstract="true"属性,表示父bean不会被创建,类似于于抽象类,否则启动容器会尝试父bean,但是由于父bean没有class而抛出异常:No bean class specified on bean definition)。这种情况下,子bean同样必须含有父bean中定义的所有的注入方式。
  4. 这里的父bean和子bean 以及“继承”,并不是Java中的继承关系,仅仅是复用了注入方式,精简了代码!
  5. 如果子bean和父bean中注入对相同依赖同时注入的值的话,那么可能会相互覆盖对方的值。这根据依赖注入的先后顺序:父bean的构造器注入->子bean的构造器注入->父bean的setter注入->子bean的setter注入,排序在后面的对相同依赖的注入值将会覆盖之前注入的值!

如下案例,首先有三个类:

/**
 * @author lx
 */
public class ParentOne 
    private String property1;

    public void setProperty1(String property1) 
        this.property1 = property1;
    

    @Override
    public String toString() 
        return "ParentOne" +
                "property1='" + property1 + '\\'' +
                '';
    

一个有意思的地方是,虽然ParentTwo继承了ParentOne,但是并没有继承私有属性property1,不过由于继承了setProperty1方法,因此仍然能够正常工作,这就是前面说的“子bean必须含有父bean中定义的所有的注入方式”的含义,对于setter方法注入来说,你没这个属性没关系,只要有个同名方法,参数类型能够兼容(从String转为参数类型)就不会报错,与返回值无关(见ParentThree)!

3 idref引用校验值

< idref >标签通常可以作为< constructor-arg >、< property >以及某些集合标签的子标签,用于将容器中另一个 bean的id或者name的字符串值(并不是引用)传递给< constructor-arg >、< property >以及某些集合标签,同时使用idref容器在部署的时候还会验证这个名称的bean是否真实存在(被定义了),这是一种防止错误的方法。该标签目前用的比较少。

如下案例,首先有一个IdrefCheck类,用于校验bean是否被定义了:

public class IdrefCheck 

    private String targetName;

    public void setTargetName(String targetName) 
        this.targetName = targetName;
    

    @Override
    public String toString() 
        return "IdrefDI" +
                "targetName='" + targetName + '\\'' +
                '';
    

配置文件:

<!--idref-->
<!--定义一个Bean-->
<bean name="helloSpring3" class="com.spring.core.HelloSpring" />

<!--idrefCheck校验bean-->
<bean class="com.spring.core.IdrefCheck" name="idrefCheck">
    <!--实际上就等于<property name="targetName" value="helloSpring3">-->
    <!--但是多了bean校验的功能-->
    <property name="targetName">
        <idref bean="helloSpring3"/>
    </property>
</bean>

实际上< idref >的bean属性引用的值就是等于一个String类型的值,都是字符串,但是< idref >多了一个校验对应名称的bean是否存在的功能!

在idea中,如果idref的bean属性指定的bean名字不存在容器中,那么直接报红,如果运行,那么会抛出:Invalid bean name 'helloSpring3' in bean reference for bean property 'targetName'

@Test
public void idref() 
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("idrefDI", IdrefCheck.class));

结果如下:

org.springframework.beans.factory.BeanCreationException: Error creating 
bean with name 'idrefDI' defined in class path resource [DI.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean name 'helloSpring3' in bean reference for bean property 'targetName'

在很久之前(Spring2.0之前的版本中),< idref >标签的一个常用的作用是在用在ProxyFactoryBean中定义的AOP拦截器里。指定拦截器名称时使用< idref >元素可防止你拼写错误拦截器ID。但是目前用的比较少了!

4 内部bean

< bean >标签的内部可以使用< constructor-arg >、< property >以及某些集合标签,表示依赖注入。同样,在< constructor-arg >、< property >以及某些集合标签中也可以使用< bean >子标签,表示一个内部bean。原因很简单,如果我们注入的是一个对象,并且我们不想要通过ref引用其他已存在的bean,那么只有定义自己的内部的bean。

和“外部”bean的区别是,内部bean不需要定义id或者name属性,因为这个对象就相当于一个外部bean自己的对象。就算指定了,容器也不会使用这些值作为bean的名字,我们也不能通过IoC容器获取。容器在创建时也会忽略内部bean的scope作用域属性(后面会讲),因为内部 bean 始终是匿名的,并且始终使用外 bean 创建。无法独立访问内部bean,也无法将它们注入其他外部bean中。

如下案例,首先有一个InnerBean类,用于内部bean测试:

/**
 * 内部bean
 *
 * @author lx
 */
public class InnerBean 
    private InnerBeanInner innerBeanInner1;
    private InnerBeanInner innerBeanInner2;

    public void setInnerBeanInner1(InnerBeanInner innerBeanInner1) 
        this.innerBeanInner1 = innerBeanInner1;
    

    public void setInnerBeanInner2(InnerBeanInner innerBeanInner2) 
        this.innerBeanInner2 = innerBeanInner2;
    


    @Override
    public String toString() 
        return "InnerBean" +
                "innerBeanInner1=" + innerBeanInner1 +
                ", innerBeanInner2=" + innerBeanInner2 +
                '';
    

    public static class InnerBeanInner 
        private String property1;
        private int property2;


        public void setProperty1(String property1) 
            this.property1 = property1;
        

        public void setProperty2(int property2) 
            this.property2 = property2;
        

        @Override
        public String toString() 
            return "InnerBeanInner" +
                    "property1='" + property1 + '\\'' +
                    ", property2=" + property2 +
                    '';
        
    

配置文件:

<!--内部bean-->
<bean id="innerBean" class="com.spring.core.InnerBean">
    <property name="innerBeanInner1">
        <!--内部bean 不需要指定id或者name-->
        <bean class="com.spring.core.InnerBean.InnerBeanInner">
            <property name="property1" value="aaa"/>
            <property name="property2" value="111"/>
        </bean>
    </property>
    <property name="innerBeanInner2">
        <!--内部bean 指定id或者name也没用,不能通过容器获取到-->
        <bean id="innerBeanInner" class="com.spring.core.InnerBean.InnerBeanInner">
            <property name="property1" value="bbb"/>
            <property name="property2" value="222"/>
        </bean>
    </property>
</bean>

测试:

@Test
public void innerBean() 
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    InnerBean innerBean = ac.getBean("innerBean", InnerBean.class);
    System.out.println(innerBean);

结果:

[innerBean]
InnerBeaninnerBeanInner1=InnerBeanInnerproperty1='aaa', property2=111, innerBeanInner2=InnerBeanInnerproperty1='bbb', property2=222

5 集合注入

5.1 注入方式

Spring提供了详细的集合注入方式。< list >、< set >、< map >、< props >、< array >标签分别用来注入Java中的List、Set、Map、Properties、array类型的集合,主要是用于集合类型的依赖项的注入。因为集合的元素既可以是基本类型也可以是对象甚至集合,因此集合注入非常灵活。另外集合注入支持泛型转换,注入的时候会自动将value的字符串值转换为对应泛型类型!

如下案例,首先有一个CollectionDI类,用于集合注入测试:

/**
 * 集合注入
 *
 * @author lx
 */
public class CollectionDI 

    //集合属性注入

    private List list;
    private Set set;
    private Map map;
    private Properties properties;
    private Object[] array;

    public CollectionDI(List list, Set set, Map map, Properties properties, Object[] array) 
        this.list = list;
        this.set = set;
        this.map = map;
        this.properties = properties;
        this.array = array;
    

    static class CollectionInner 
        private String property1;
        private int property2;


        public void setProperty1(String property1) 
            this.property1 = property1;
        

        public void setProperty2(int property2) 
            this.property2 = property2;
        

        @Override
        public String toString() 
            return "CollectionInner" +
                    "property1='" + property1 + '\\'' +
                    ", property2=" + property2 +
                    '';
        
    

    @Override
    public String toString() 
        return "CollectionDI" +
                "\\n" + "list=" + list +
                "\\n" + ", set=" + set +
                "\\n" + ", map=" + map +
                "\\n" + ", properties=" + properties +
                查看详情  

springioc—基于注解的di(总结)(代码片段)

基于注解的DI使用spring提供的注解,完成java对象创建,属性赋值。先总结:1.创建对象的注解@Component:普通java对象@Respository:dao对象,持久层对象,表示对象能访问数据库。@Service:service... 查看详情

spring的学习二(springioc和di,aop简介)

一:SpringIoc,DI,AOP简介IOC控制反转:将原本在程序中手动创建对象的控制权,交由Spring框架来管理         原理:读取标注或者配置文件,看看Shoe依赖的是哪个Source,拿到类名使用反射的API,基于类... 查看详情

基于 Spring 注释的 DI 与 xml 配置?

】基于Spring注释的DI与xml配置?【英文标题】:Springannotation-basedDIvsxmlconfiguration?【发布时间】:2012-01-1517:44:07【问题描述】:最近在我们的团队中,我们开始讨论在代码中使用spring注释来定义spring依赖项。目前我们正在使用contex... 查看详情

springioc和di

 (1)什么是IOC(InversionOfControl控制反转)?对象之间的依赖关系应该由容器来建立。 (2)什么是DI(DependencyInjection依赖注入)?容器可以通过调用set方法或者构造器来建立对象之间的依赖关系。 注:IOC是目标,DI是手段。 (3)... 查看详情

springioc容器(代码片段)

IoC容器Spring容器是Spring框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为SpringBeans。通过阅... 查看详情

基于xml的di

三、集合属性注入(包含:为数组注入值、为List注入值、为Set注入值、为Map注入值、为Properties注入值)集合类定义如下: xml定义如下:仔细看下面是执行代码:  四、对于域属性的自动注入(包括:byName方式自动注... 查看详情

springioc容器初始化源码—容器初始化入口以及setconfiglocations设置容器配置信息一万字

  基于最新Spring5.x,对于基于XML的SpringIoC容器初始化过程中的setConfigLocations设置容器配置信息方法的源码进行了详细分析,最后给出了比较详细的方法调用时序图!  Spring5.x源码解析系列的第一篇文章,主要... 查看详情

springioc官方文档学习笔记之基于注解的容器配置(代码片段)

1.基于注解的配置与基于xml的配置(1)在xml配置文件中,使用context:annotation-config</context:annotation-config>标签即可开启基于注解的配置,如下所示,该标签会隐式的向容器中添加ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcess... 查看详情

springioc官方文档学习笔记之基于java的容器配置(代码片段)

1.@Bean与@Configuration(1)标注于类之上的@Configuration注解与标注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解标注的方法用于实例化bean并将其注入至容器中,它与基于xml配置中的<bean/>标签起着相同的作用,@Bean... 查看详情

spring依赖注入基于xml的di(代码片段)

DI:给属性赋值spring调用类的无参数构造方法,创建对象。对象创建后给属性赋值。给属性赋值可以使用:xml配置文件中的标签和属性使用注解DI分类:set注入,也叫做设值注入构造注入set注入publicclassStudent //... 查看详情

java--spring之ioc控制反转;基于xml配置文件的di(代码片段)

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架控制反转(InversionofControl,缩写IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。依赖:classA类中含有classB的... 查看详情

spring基于xml配置的aop框架详细讲解(代码片段)

学习Spring中的AOP,就是通过配置的方式(有基于XML配置的,以及基于注解配置的),来实现相关的拦截切入功能。对原有的操作进行加强,但不影响原本的操作。目录学习Spring中的AOP,就是通过配置的... 查看详情

spring基于xml配置的aop框架详细讲解(代码片段)

学习Spring中的AOP,就是通过配置的方式(有基于XML配置的,以及基于注解配置的),来实现相关的拦截切入功能。对原有的操作进行加强,但不影响原本的操作。目录学习Spring中的AOP,就是通过配置的... 查看详情

springioc(di)

 软件152刘昊翰一、IOC(DI) 概念IOC(InversionofControl,控制倒转)Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。 DI—DependencyInjection,即“依赖注入”:组件之间依赖关系由容器在... 查看详情

spring事务控制--基于xml的声明式事务控制:详细配置(代码片段)

...控制–编程式事务控制相关对象02:Spring事务控制–基于XML的声明式事务控制:环境搭建03:Spring事务控制–基于XML的声明式事务控制:详细配置04:Spring事务控制–基于注解的声明式事务控制1.基于XML的声明式... 查看详情

springioc(di)

1什么是IOCIOC—InversionofControl,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。2IOC和DIDI—DependencyInjection,即&ld... 查看详情

springioc

控制反转IOC*把创建对象的权力交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入DI和依赖查找DL+目的是削减程序之间的耦合Application常用的三个实现类:用来读取配置文件*ClassPathXmlApplicationContext:加... 查看详情