3 基于XML管理bean

Spring入门

  • 首先创建一个类
    • 我创建了一个hello类
  • 在resources下创建spring配置文件
    • 名字当前还是可以自己随便取的
  • 配置spring配置文件
<?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">
<!--    第一行是XMl文件的声明,接着几行是XML文件的约束,定义了XML的命名空间,所为命名空间就是规定了这个XML文件中你能使用的标签名,
因为一般XML文件不都是自由使用标签嘛-->
    <beans>
<!--这里beans里面可以管理bean,你可以说是spring中一个个组件也可以说是一个个对象-->
<!--        bean标签将配置一个bean对象交给IOC容器管理,id属性为唯一标识,class属性表示bean对象对应的类型,这个class属性是我们spring使用反射来创建对象的关键,而且要求对应的类要有无参构造器,而且这个要求类不能是接口-->
        <bean id="helloword" class="com.cn.zt.myspring.hello"></bean>
    </beans>
</beans>
  • 测试
        //使用ClassPathXmlApplication实现类来获取IOC容器,参数为类路径下的对应springXML文件
        ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取IOC容器中的bean,主要有三种获取方式getBean(Class<T> requiredType),getBean(String name, Class<T> requiredType),getBean(String name)
        hello helloworld = (hello)ioc.getBean("helloword");
        helloworld.sayhello();

获取bean的三种方式

  • 上面的例子有三种在ioc容器中获取bean对象的方法
  • 底层都是通过bean标签中的class属性利用反射获取实例对象
    • getBean(String name)
      • 通过xml文件中beanid获取
    • getBean(Class<T> requiredType)
      • 通过类型来获取
      • 在SpringXML标签中bean标签中不能存在多个类型匹配的bean,就是多个beanid对应一个同一个类
        • 不然会报NOUniqueBeanDefinitionExceptin异常
      • 而且在bean标签中必须有一beanid对应该类,才能用类型获取
        • 不然会报NOSuchBeanDefinitioinException异常
    • getBean(String name, Class<T> requiredType)
      • 根据bean的id和类型来获取
      • 这个允许多个beanid对应同一个类,因为他是通过id和类型获取,这样不会造成混淆
    • 最常用的是getBean(Class<T> requiredType)
      • 因为它不用获取bean后强转类型,而且就算发生了多个id同个类,也可以用bean标签中scope属性来设置单例多例
    • 扩展
      • 如果组件类实现了接口,根据接口类型可以获取bean
        • 就是如果你写在xml文件里bean标签对应的那个类它实现了一个接口,你可在getbean中的参数填接口的类型来获取相应的bean,前提是你的bean是唯一的,就是要求你这个接口的实现类中只有一个类配置了bean标签
    • 结论
      • 如果你使用类型来获取bean是,在满足bean唯一性的前提下,其实只要看[对象 instanceof 指定的类型] 的返回结果是否为true

依赖注入DI

  • 依赖注入就是为类的的属性赋值
依赖注入的方式
  1. 依赖注入之用setter注入(使用property标签)
    <bean id="student" class="com.cn.zt.myspring.Student">
        <property name="age" value="12"></property>
        <property name="gender" value="男"></property>
        <property name="name" value="zt"></property>
    </bean>

这种方式是通过实体类中的set方法注入,要求实体类中有这个属性的set方法,通过这种方式为属性赋值

  • name是需要赋值的属性名(注意区别属性和成员变量)
  • value就是为属性赋的值
  1. 构造器注入(使用constructor-arg标签)
  • 我们可以对有参构造器注入
    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    <bean id="student" class="com.cn.zt.myspring.Student">
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="gender" value="男"></constructor-arg>
        <constructor-arg name="name" value="ykk"></constructor-arg>
    </bean>
  • construtor中可以不写name属性,如果我不写,那spring就会帮我注入到一个有三个参数的构造器中,第一个constructor标签注入到以一个参数…,但是它不区分构造器,如果你有两个参数个数都是三的构造器,要区分就还是要加name属性
依赖注入特殊值
  1. 字面量赋值
    • 字面量一般指的是基本数据类型及其包装类和String
    • 它赋值给属性就是value=“字面量” <property name="age" value="12">
  2. 赋值null(是赋值空对象而不是null字符串)
    • 比如我想为属性age赋值null
    • <property name="age" value="12">
      • <null></null>
    • </property>
    • 这是用setter方法注入null,构造器也是这样注入null,在标签内加一个null子标签
  3. 特殊符号赋值
    • 如果我们赋的值带有特殊符号,<property name="gender" value="<男>"></property>
    • 不能直接使用这类符号,要使用实体替换
      • < 对应 $lt
      • > 对应 $gt
    • 或者使用CDATA节
      • 语法<![CDATA[ 文本数据 ]]>
      • 使用CDATA节不能在注入标签中用属性value赋值,必须在标签内使用子标签的形式赋值,因为CDATA节它也是一个标签,不能写到属性中
      • <constructor-arg name="gender">
        • <value><![CDATA[<男>]]></value>
      • </constructor-arg>
      • CDATA节能原样展示其中的内容
  4. 为类类型赋值
    • 我在上面的student类中加了一个clazz对象属性
    • 第一种方式,引用外部bean
      • <property name="clazz" ref="clazz"></property>
      • ref属性的值是外部bean的id,他是用来引用ioc容器中的bean的
      • <bean id="clazz" class="com.cn.zt.myspring.Clazz">
        • <property name="cid" value="123"></property>
        • <property name="cname" value="456"></property>
      • </bean>
      • 这样就可以为clazz对象属性注入了
    • 第二种方式,级联方式
      • <property name="clazz.cname" value="555"></property>
      • <property name="clazz.cid" value="777"></property>
      • 如果直接这样写会报空指针错,因为在student中没有实例化clazz,而直接使用clazz来获取属性会报错
      • 所以我们要在前面加一条
        • <property name="clazz" ref="clazz"></property>
      • 这样是不是和第一种方式类似呢
      • 所以第二种方式一般用来修改对象属性中的值,引入外部的bean,然后修改其中的属性值
    • 第三种方式,使用内部bean,只能能在bean的内部使用,不能直接用ioc容器获取
      • <property name="clazz">
        • <bean id="clazzInner" class="com.cn.zt.myspring.Clazz">
          • <property name="cid" value="999"></property>
          • <property name="cname" value="111"></property>
        • </bean>
      • </property>
      • 这种方式相当于在bean中建立了一个内部bean,跟内部类很像,它不能直接被ioc容器获取,只能在该bean中访问这个内部bean
  5. 数组类型赋值
    • 在property标签中有array标签,用来为数组类型属性注入赋值
    • <property name="hooby">
      • <array>
        • <value>抽烟</value>
        • <value>喝酒</value>
        • <value>烫头</value>
      • </array>
    • </property>
    • 如果数组中的值都是字面量类型,就是使用value
    • 如果数组中的值是类类型,就使用ref标签来引入外部bean
      • <property name="clazzes">
        • <array>
          • <ref bean="clazz"></ref>
        • </array>
      • </property>
  6. 集合类型的属性赋值
    • list集合属性赋值
      • 第一种方式
        • 使用property标签中的list标签
        • <property name="list">
          • <list>
            • <ref bean="student1"></ref>
          • </list>
        • </property>
        • 在list标签中使用ref还是value取决于是否是字面量
        • 还有一件事,我犯了一个错误,我在之前的Student bean中有一个clazz的类类型属性,它引用的是id为clazz的bean,但是我在id为clazz的bean中有一个id为student的bean,它们两个套娃了,就会报错
      • 第二种方式,引用list集合bean
        • 首先提一个错误的做法
          • 直接创建一个bean,它的class为Arraylist,这样<bean id="list" class="java.util.ArrayList"></bean>
          • 这样是不行的,这个只能让我们向Arraylist中的属性赋值,而不是往里面添加数据
        • 配置一个集合类型的bean,需要使用util的约束,你直接使用util约束中的标签名,idea会自动在xml中导入约束
          • 由于util约束中可能会带有重名标签,所以我们要在标签名中带前缀
          • <util:list id="studentlist">
            • <ref bean="student1"></ref>如果是字面量,可以用value标签
            • <ref bean="student2"></ref>
          • </util:list>
          • 这样就建立好了一个集合类型的bean,我们可以在其他bean中引用这个集合,把它赋值它那个bean的集合类型属性中
          • <property name="list" ref="studentlist"></property>
          • 直接这样引用就可以了
    • map集合属性赋值
    • 创建一个map集合属性private Map<String,Teacher> map;
      • map集合属性赋值和list集合属性赋值大体相似,也是两种方式
        • 一个是在property标签中使用map标签
          • <property name="map">
            • <map>
              • <entry key="123" value-ref="teacher1"></entry>
              • <entry key="222" value-ref="teacher2"></entry>
            • </map>
          • </property>
          • 解释一下,map中的entry标签表示每一个键值对
          • 在enry标签中有四个属性,key,value,key-ref,value-ref
          • 分别表示key和value是否是字面量,是字面量就使用key=xxx,不是就是key-ref=beanid
        • 一个是使用 util:map 创建一个map集合类型bean,再引入
          • <util:map id="teachermap">
            • <entry key="111" value-ref="teacher1"></entry>
            • <entry key="123" value-ref="teacher2"></entry>
          • </util:map>
          • 引用
          • <property name="map" ref="teachermap"></property>

Spring管理数据源和引入外部属性

  • 使用spring配置文件管理数据源
    • 首先在resources下创建一个管理数据源的配置文件,名字自取,我取的是spring-datasource.xml
    • 接着在新建好的配置文件中设置bean对象,这个bean对象对应的是你用的数据源,这里我用的是druid数据源
      • <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    • 在bean标签中使用property标签注入属性
      • <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
      • <property name="url" value="JDBC:mysql://localhost:3306/create?serverTimezone=UTC"></property>
      • <property name="username" value="root"></property>
      • <property name="password" value="root"></property>
      • 这四个属性是需要我们注入的,其他的属性都有默认值,想修改也可以通过property标签注入修改
    • 测试使用
      • 在测试方法中通过xml创建ioc容器
      • 用ioc容器获取druid的bean对象
      • 使用druid的getconnection方法看是否能返回连接地址
  • 在spring数据源配置文件中引用properties配置文件
    • 这个主要是方便我们在数据源配置中的属性赋值,使用properties配置文件中的值
    • 首先,创建jdbc.properties配置文件,在配置文件中写好键值对
      • jdbc.driver=com.mysql.cj.jdbc.Driver
      • jdbc.url=JDBC:mysql://localhost:3306/create?serverTimezone=UTC
      • jdbc.username=root
      • jdbc.password=root
    • 然后再spring数据源文件中使用新的xml约束(命名空间) context
      • <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
      • 然后再context:property-placeholder 标签的属性location中引入jdbc配置文件,这样我们就可以在spring数据源配置文件中使用${} 来访问jdbc配置文件中的键值对了
        • <property name="driverClassName" value="${jdbc.driver}"></property>
        • <property name="url" value="${jdbc.url}"></property>
        • <property name="username" value="${jdbc.username}"></property>
        • <property name="password" value="${jdbc.password}"></property>

bean相关

  • bean的作用域
    • 在IOC容器中,无论是哪种获取bean的方式,你获取的bean都是同一个实例,因为ioc容器管理的bean默认是单例的
      • <bean id="student" class="com.cn.zt.myspring.Student" scope="singleton">
      • bean标签中的scope属性可以设置这个bean是单例还是多例
      • 默认情况下,scope属性为singleton(单例),也可以设置多例(prototype),多例就是会生成新对象
  • bean的生命周期
    • bean的生命周期流程
      • 第一步,通过反射调用无参构造实例化对象
      • 第二步,依赖注入属性
      • 第三步,初始化
      • 第四步,使用bean
      • 第五步,销毁
      • 然后提一下,初始化方法和销毁方法都需要在bean标签中使用属性指定
        • <bean id="student" class="com.cn.zt.myspring.Student" init-method="toString" destroy-method="toString">
        • 分别使用init-method和destroy-method来指定,指定的方法是bean对象中的方法,就是bean对应类例写的方法
      • 销毁方法只有再ioc容器关闭时才会输出,ApplicationContext接口中没有关闭和刷新方法,所以要转为ConfigurableApplicationContext接口来使用关闭方法关闭容器
  • bean的作用域对生命周期的影响
    • 若bean的作用域为单例时,所有bean的实例化,依赖注入,初始化都会在获取ioc容器时候执行
    • 若bean的作用域为多例时,bean的前三个步骤会在获取bean的时候执行
  • bean的后置处理器(BeanPostProcesssor)
    • bean在初始化时会触发后置处理器
    • 后置处理器中的两个方法对应初始化之前和初始化之后,我们可以在里面写功能
      • public Object postProcessAfterInitialization(Object bean, String beanName)
      • public Object postProcessBeforeInitialization(Object bean, String beanName)
    • 后置处理器使用方法
      • 首先创建一个类实现后置处理器接口(BeanPostProcessor)
      • 然后重写两个方法,添加自定义功能
      • 在spring配置文件中写bean标签<bean id="beanPostProcess" class="com.cn.zt.myspring.myBeanPostProcess"></bean>
      • 完成后,用这个配置文件获取的ioc容器中的所有bean对象在初始化的时候都会触发后置处理器中你写的功能
  • 经过后置处理器后,bean生命周期流程变成 实例化->依赖注入->初始化之前的后置器操作->初始化操作->初始化之后的后置器操作

FactoryBean

FactoryBean和BeanFactory区别很大,beanFactory是ioc容器的基本实现,而Factory Bean是一个bean

  • xxxFactory是什么意思,其实就是一个生产对象工厂,它可以返回xxx对象
  • Factorybean是一个接口,需要一个类实现这个接口,它里面有几个重写方法
    • getObject() 创建一个对象交给ioc容器管理
    • getObjectType() 设置所提供对象的类型
    • isSingleton() 所提供的对象是否是单例
  • 当把FactoryBean的实现类配置到bean标签中后,它会有个神奇作用,我演示一下
    • 首先我创建一个user类
    • 然后我创建一个userfactory实现factorybean接口
    • 在userfactory中重写了factorybean中的方法,让getObject返回user对象
    • 然后在spring配置文件中配置userfactory的bean
    • 使用ioc容器获取userfactory的bean对象
    • 发现返回的对象是user对象
    • 这就是Factorybean的作用
  • 如果不用factorybean,我们需要先配置userfactory,用ioc容器获取到userfactory的bean后,再用这个bean获取user,使用factorybean后,我们直接配置userfactory的bean标签,直接可以获取到userfactory提供的user对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值