Spring系列二:基于XML配置bean
作者:mmseoamin日期:2023-12-21

💞基于XML配置Bean

  • 💖通过类型获取bean
  • 💖通过指定构造器配置bean
  • 💖通过p名称空间配置bean
  • 💖通过ref配置bean
  • 💖通过内部bean配置属性
  • 💖对集合数组属性进行配置
    • 💕对List属性进行配置
    • 💕对Map属性进行配置
    • 💕对Set属性进行配置
    • 💕对Array属性进行配置
    • 💕对Properties属性进行配置
    • 💖使用utillist进行配置
    • 💖属性级联赋值配置
    • 💖通过静态工厂获取bean
    • 💖bean配置信息重用
    • 💖bean创建顺序
    • 💖bean的单例和多实例
    • 💖bean的生命周期
    • 💖配置bean后置处理器
    • 💖通过属性文件配置bean
    • 💖基于XML的bean的自动装配
    • 💖Spring El 表达式配置Bean

      上文中, 我们学习到了 Spring系列一:spring的安装与使用

      接下来我们学习, 通过XML配置bean

      Spring系列二:基于XML配置bean,在这里插入图片描述,第1张


      Bean管理包括两方面: 创建bean对象, 给bean注入属性

      💖通过类型获取bean

      案例: 通过spring的ioc容器, 获取一个bean对象, 获取方式: 按类型.

      
      
          
          
          
          
      
      

      演示通过bean的类型获取对象

      @Test
      public void getBeanByType() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
          //直接传入class对象/类型
          Monster bean = ioc.getBean(Monster.class);
          System.out.println("bean=" + bean);
      }
      

      细节

      按照类型获取bean, 要求ioc容器中的同一个类的bean只能有一个, 否则会抛出异常 NoUniqueBeanDefinationException

      这种方式的应用场景: 比如XxxAction / Servlet / Controller, 或XxxService在一个线程中只需要一个对象实例(单例)的情况

      在容器配置文件(比如beans.xml)中给属性赋值. 底层是通过setter方法完成的. 所以需要提供setter方法.

      💖通过指定构造器配置bean

      
      
      
         
         
         
      
      
         
         
         
      
      
         
         
         
      
      

      演示通过构造器来设置属性

      @Test
      public void setBeanByConstructor() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
          Monster monster03 = ioc.getBean("monster03", Monster.class);
          System.out.println("monster03=" + monster03);
      }
      

      Spring系列二:基于XML配置bean,在这里插入图片描述,第2张

      Spring系列二:基于XML配置bean,在这里插入图片描述,第3张

      通过index属性来区分是第几个参数;

      通过type属性来区分是什么类型(按照顺序, 这是可以的)

      💖通过p名称空间配置bean

      xmlns:p=“http://www.springframework.org/schema/p”

      
      
      

      演示通过p名称空间来设置属性

      public class SpringBeanTest {
          @Test
          public void setBeanByP() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              Monster monster06 = ioc.getBean("monster06", Monster.class);
              System.out.println("monster06=" + monster06);
          }
      }
      

      💖通过ref配置bean

      引用注入其它bean对象

      在spring的ioc容器, 可以通过ref来实现bean对象的相互引用[ref全称: reference]

      
      
      
      
          
      
      
      package com.zzw.spring.service;
      public class MemberServiceImpl {
          private MemberDaoImpl memberDao;
          public MemberDaoImpl getMemberDao() {
              return memberDao;
          }
          public void setMemberDao(MemberDaoImpl memberDao) {
              this.memberDao = memberDao;
          }
          public void add() {
              System.out.println("MemberServiceImpl add方法被调用...");
              memberDao.add();
          }
      }
      
      package com.zzw.spring.dao;
      public class MemberDaoImpl {
          public MemberDaoImpl() {
              System.out.println("MemberDaoImpl 构造器...");
          }
          public void add() {
              System.out.println("MemberDaoImpl add方法被执行");
          }
      }
      

      测试: 通过ref来设置bean属性

      public class SpringBeanTest {
          @Test
          public void setBeanByRef() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              MemberServiceImpl memberService = ioc.getBean("memberService", MemberServiceImpl.class);
              memberService.add();
          }
      }
      

      Spring系列二:基于XML配置bean,在这里插入图片描述,第4张

      💖通过内部bean配置属性

      引用/注入内部bean对象

      在spring的ioc容器, 可以直接配置内部bean对象

      
      
          
          
              
          
      
      

      通过内部bean, 设置属性

      public class SpringBeanTest {
          @Test
          public void setBeanByPro() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              MemberServiceImpl memberService2 = ioc.getBean("memberService2", MemberServiceImpl.class);
              memberService2.add();
          }
      }
      

      💖对集合数组属性进行配置

      引用/注入 集合/数据类型

      1. 主要掌握List / Map / Properties 三种集合的使用
      2. Properties是Hashtable的子类, 是key-value的形式
      3. 这里的properties的k-v, 都是String类型

      在spring的ioc容器中, 如何给bean对象的 集合/数组 类型的属性赋值

      public class Master {
          private String name;//主人名字
          private List monsterList;
          private Map monsterMap;
          private Set monsterSet;
          //数组
          private String[] monsterName;
          //Java基础
          //这个Properties 是HashMap的子类, 是key-value的存放形式
          //这里Properties key和value 都是String
          private Properties properties;
      	//getter, setter方法
      }
      

      给集合/数组属性进行赋值

      public class SpringBeanTest {
          @Test
          public void setBeanByCollection() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              Master master = ioc.getBean("master", Master.class);
              System.out.println("master=" + master);
          }
      }
      

      💕对List属性进行配置

      
      
          
          
          
              
                  
                  
                  
                  
                  
              
          
      
      

      💕对Map属性进行配置

      
      
          
          
          
              
                  
                      
                          monster04
                      
                      
                      
                  
                  
                      
                          monster03
                      
                      
                      
                  
              
          
      
      

      💕对Set属性进行配置

      
      
          
          
          
              
                  
                  
                  
                  
                  
              
          
      
      

      💕对Array属性进行配置

      
      
          
          
          
              
                  六耳猕猴
                  东海龙王
                  红孩儿
              
          
      
      

      💕对Properties属性进行配置

      
      
          
          
          
              
                  root
                  123456
                  978964140@qq.com
              
          
      
      

      💖使用utillist进行配置

      spring的ioc容器, 可以通过util名称空间来创建list集合

      public class BookStore {
          //书
          private List bookList;
          //无参构造器, 如果没有其它的构造器, 该无参构造器可以不写
          //如果有其它的构造器, 则必须显示地定义一下无参构造器
          public BookStore() {
          }
      	//getter, setter方法
      }
      

      beans.xml

      
      
          三体
          时间简史
          梦的解析
          福尔摩斯探案集
      
      
      
          
      
      

      使用util:list名称空间给属性赋值

      public class SpringBeanTest {
          @Test
          public void setBeanByUtilList() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              BookStore bookStore = ioc.getBean("bookStore", BookStore.class);
              System.out.println("bookStore=" + bookStore);
          }
      }
      

      💖属性级联赋值配置

      spring的ioc容器, 可以直接给对象属性的属性赋值, 即级联属性赋值

      部门

      public class Dept {
          private String name;
          public Dept() {
          }
      	//getter, setter方法
      

      员工

      public class Employee {
          private String name;
          private Dept dept;
          public Employee() {
          }
      	
      	//getter, setter方法
      }
      

      beans.xml

      
      
      
      
          
          
          
          
      
      

      给属性进行级联赋值

      public class SpringBeanTest {
          @Test
          public void setBeanByRelation() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              Employee employee = ioc.getBean("employee", Employee.class);
              System.out.println("employee=" + employee);
          }
      }
      

      💖通过静态工厂获取bean

      在spring的ioc容器, 可以通过静态工厂获取bean对象

      这是一个静态工厂类-可以返回Monster对象

      package com.zzw.spring.factory;
      public class MyStaticFactory {
          private static Map monsterMap;
          //使用static代码块进行初始化 - java基础
          static {
              monsterMap = new HashMap<>();
              monsterMap.put("monster01", new Monster(100, "齐天大圣", "如意金箍棒"));
              monsterMap.put("monster02", new Monster(200, "天蓬元帅", "九齿钉耙"));
          }
          //提供一个方法, 返回Monster对象
          public static Monster getMonster(String key) {
              return monsterMap.get(key);
          }
      }
      
      
      
          
      
      

      通过静态工厂获取bean

      public class SpringBeanTest {
          @Test
          public void getBeanByStaticFactory() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              Monster myMonster01 = ioc.getBean("myMonster01", Monster.class);
              Monster myMonster02 = ioc.getBean("myMonster01", Monster.class);
              System.out.println("myMonster01=" + myMonster01);
              System.out.println(myMonster01 == myMonster02);//true. myMonster01和myMonster02是同一个对象
          }
      }
      

      💖bean配置信息重用

      在spring的ioc容器, 提供了一种继承的方式来实现bean配置信息的重用

      
      
      
      
      
      
      
      

      通过继承, 配置bean

      public class SpringBeanTest {
          @Test
          public void getBeanByExtends() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              
              Monster monster11 = ioc.getBean("monster11", Monster.class);
              System.out.println("monster11=" + monster11);
              Monster monster13 = ioc.getBean("monster13", Monster.class);
              System.out.println("monster13=" + monster13);
          }
      }
      

      💖bean创建顺序

      在spring的ioc容器, 默认是按照配置的顺序创建bean对象

      测试bean创建顺序

      实验1

      
      
      
      
      public class SpringBeanTest {
          @Test
          public void testBeanCreateOrder() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              System.out.println("ok~");
          }
      }
      
      public class Department {
          public Department() {
              System.out.println("Department构造器 被执行");
          }
      }
      
      public class Student {
          public Student() {
              System.out.println("Student构造器 被执行");
          }
      }
      

      ※会先创建student01这个bean对象, 然后创建department01这个bean对象

      运行结果

      Student构造器 被执行
      Department构造器 被执行
      ok~
      

      ※如果这样配置, 会先创建department01对象, 再创建student01对象

      
      
      
      

      运行结果

      Department构造器 被执行
      Student构造器 被执行
      ok~
      

      实验2

      1.先看下面的配置, 请问两个bean创建的顺序是什么? 并分析执行流程

      
      
      
          
      
      

      答案:

      1)先创建 id=memberDao

      2)再创建 id=memberService

      3)调用 memberService.setMemberDao() 完成引用


      运行结果:

      MemberDaoImpl 构造器...
      MemberServiceImpl 构造器被执行
      setMemberDao()...
      

      2.先看下面的配置, 请问两个bean创建的顺序是什么? 并分析执行流程

      
          
      
      
      
      

      答案:

      1)先创建 id=memberService

      2)再创建 id=memberDao

      3)调用 memberService.setMemberDao() 完成引用


      运行结果

      MemberServiceImpl 构造器被执行
      MemberDaoImpl 构造器...
      setMemberDao()...
      

      💖bean的单例和多实例

      在spring的ioc容器中, 默认情况下是按照单例创建的. 即配置一个bean对象后, ioc容器只会创建一个bean对象

      如果我们希望ioc容器配置的某个bean对象, 是以多个实例形式创建的. 则可以通过配置 scope=“prototype” 来指定

      public class Cat {
          private Integer id;
          private String name;
          
          public Cat() {
          //getter, setter方法
      }
      
      
      
          
          
      
      

      测试Scope

      @Test
      public void testBeanScope() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
          Cat cat1 = ioc.getBean("cat", Cat.class);
          Cat cat2 = ioc.getBean("cat", Cat.class);
          Cat cat3 = ioc.getBean("cat", Cat.class);
          System.out.println("cat1=" + cat1);
          System.out.println("cat2=" + cat2);
          System.out.println("cat3=" + cat3);
      }
      

      使用细节

      1)bean默认是单例singleton; 在启动容器时, bean默认就会创建, 并放入到singletonObjects

      2) 当设置为多实例机制后, 该bean是在getBean()时被创建

      3) 如果是单例singleton, 同时希望在getBean时才创建, 可以指定懒加载 lazy-init="true"(注意默认是false)

      4) 通常情况下, lazy-init 就使用默认值false. 在开发看来, 空间换时间是值得的, 除非有特殊要求

      5) 如果scope=“prototype”, 这时你的lazy-init 属性的值不管是true还是false, 都是在getBean的时候才创建这个对象

      💖bean的生命周期

      bean对象创建是由JVM完成的, 然后执行如下方法

      1. 执行构造器
      2. 执行set相关方法
      3. 调用bean的初始化方法(需要配置)
      4. 使用bean
      5. 当容器关闭的时候, 调用bean的销毁方法(需要配置)
      public class House {
          private String name;
          public House() {
              System.out.println("House构造器 被执行...");
          }
          public void setName(String name) {
              System.out.println("House setName()=" + name);
              this.name = name;
          }
          //解读
          //1.这个方法是由程序员来编写的
          //2.根据自己的业务逻辑来写.
          //3.名字也不是固定的
          public void init() {
              System.out.println("House init()....");
          }
          //解读
          //1.这个方法是由程序员来编写的
          //2.根据自己的业务逻辑来写.
          //3.名字也不是固定的
          public void destroy() {
              System.out.println("House destroy()...");
          }
      }
      
      
      
          
      
      

      测试bean的生命周期

      public class SpringBeanTest {
          @Test
          public void testBeanLife() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
              House house = ioc.getBean("house", House.class);
              System.out.println("house=" + house);
              //关闭容器
              //1.ioc的编译类型 ApplicationContext, 运行类型 ClassPathXmlApplicationContext
              //2.因为ClassPathXmlApplicationContext 实现了 ConfigurableApplicationContext
              //3.ClassPathXmlApplicationContext 是有close()的
              //4.将ioc 转成ClassPathXmlApplicationContext, 再调用close()
              //ioc.close()
              //5.关闭ioc容器
              ((ClassPathXmlApplicationContext) ioc).close();
          }
      }
      

      输出

      House构造器 被执行...
      House setName()=上海豪宅
      House init()....
      setMemberDao()...
      house=com.zzw.spring.bean.House@327bcebd
      House destroy()...
      

      使用细节

      1.初始化init方法和destroy方法, 由程序员来指定

      2.销毁方法就是当关闭容器时, 才会被调用

      💖配置bean后置处理器

      1在spring的ioc容器, 可以配置bean的后置处理器

      2.该 处理器/对象 会在bean初始化方法调用前和初始化方法调用后被调用

      3.bean中没有声明初始化方法, 后置处理器依然会发生作用

      3.程序员可以在后置处理器中编写自己的代码

      package com.zzw.spring.bean;
      //ctrl+h 可以查看类的继承关系
      //这是一个后置处理器, 需要实现 BeanPostProcessor接口
      public class MyBeanPostProcessor implements BeanPostProcessor {
          /**
           * 什么时候被调用: 在Bean的init方法前被调用
           * @param bean     传入在IOC容器中 创建/配置 的bean
           * @param beanName 传入在IOC容器中 创建/配置 的bean的id
           * @return Object 是程序员对传入的bean进行修改/处理[如果有需要的话], 返回
           * @throws BeansException
           */
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("postProcessBeforeInitialization()... bean="
                      + bean + " beanName=" + beanName);
              return bean;
          }
          /**
           * 什么时候被调用: 在Bean的init方法后被调用
           * @param bean     传入在IOC容器中 创建/配置 的bean
           * @param beanName 传入在IOC容器中 创建/配置 的bean的id
           * @return Object 是程序员对传入的bean进行修改/处理[如果有需要的话], 返回
           * @throws BeansException
           */
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("postProcessAfterInitialization()... bean="
                      + bean + " beanName=" + beanName);
              return bean;
          }
      }
      

      新建beans02.xml配置文件

      
      
          
      
      
      
      

      测试

      package com.zzw.spring.test;
      public class SpringBeanTest {
          @Test
          public void testBeanPostProcessor() {
              ApplicationContext ioc =
                      new ClassPathXmlApplicationContext("beans02.xml");
              House house = ioc.getBean("house", House.class);
              System.out.println("使用house=" + house);
              //关闭容器
              //ioc不能调用子类的特有的成员
              //因为在编译阶段, 能调用哪些成员, 是由编译类型来决定的
              //ioc编译类型 ApplicationContext, 运行类型 ClassPathXmlApplicationContext
              ((ClassPathXmlApplicationContext) ioc).close();//向下转型
          }
      }
      

      其它说明

      1.怎么执行到这个方法? => 使用AOP(反射+动态代理+IO+容器+注解)

      2.有什么用? => 可以对IOC容器中所有的对象进行统一处理, 比如日志处理/权限校验/安全验证/事务管理.

      -初步体验案例: 如果类型是House的统一改成 上海豪宅

      3.针对容器的所有对象吗? 是的=>切面编程

      4.后面我们会自己实现这个底层机制

      5.这是一个比较难以理解的知识点.

      
      
          
      
      
          
      
      
      
      
      public class MyBeanPostProcessor implements BeanPostProcessor {
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("postProcessBeforeInitialization()... bean="
                      + bean + " beanName=" + beanName);
              //对多个对象进行处理/编程=>切面编程
              if (bean instanceof House) {
                  ((House) bean).setName("上海豪宅~");
              }
              //这里返回一个空值并不会有任何影响
              //return null;
              return bean;
          }
          
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("postProcessAfterInitialization()... bean="
                      + bean + " beanName=" + beanName);
              return bean;
          }
      }
      
      public class SpringBeanTest {
          @Test
          public void testBeanPostProcessor() {
              ApplicationContext ioc =
                      new ClassPathXmlApplicationContext("beans02.xml");
              House house = ioc.getBean("house", House.class);
              House house02 = ioc.getBean("house02", House.class);
              System.out.println("使用house=" + house);
              System.out.println("使用house=" + house02);
              //关闭容器
              //ioc不能调用子类的特有的成员
              //因为在编译阶段, 能调用哪些成员, 是由编译类型来决定的
              //ioc编译类型 ApplicationContext, 运行类型 ClassPathXmlApplicationContext
              ((ClassPathXmlApplicationContext) ioc).close();//向下转型
          }
      }
      

      测试结果(return bean和return null都是下面的输出结果)

      House构造器 被执行...
      House setName()=大豪宅
      postProcessBeforeInitialization()... bean=House{name='大豪宅'} beanName=house
      House setName()=上海豪宅~
      House init()....
      postProcessAfterInitialization()... bean=House{name='上海豪宅~'} beanName=house
      House构造器 被执行...
      House setName()=宫殿
      postProcessBeforeInitialization()... bean=House{name='宫殿'} beanName=house02
      House setName()=上海豪宅~
      House init()....
      postProcessAfterInitialization()... bean=House{name='上海豪宅~'} beanName=house02
      使用house=House{name='上海豪宅~'}
      使用house=House{name='上海豪宅~'}
      House destroy()...
      House destroy()...
      

      💖通过属性文件配置bean

      在spring的ioc容器, 通过属性文件给bean注入值

      在src目录下, 新建配置文件my.properties [配置文件都要写在src目录下]

      monsterId=1000
      name=\u5343\u5e74\u9f9f 
      skill=\u65cb\u8f6c\u6253\u51fb
      

      解决中文乱码问题

      Spring系列二:基于XML配置bean,在这里插入图片描述,第5张

      
      
      
      
          
          
          
      
      
      public class SpringBeanTest {
          //通过属性文件给bean属性赋值
          @Test
          public void setBeanByFile() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");
              Monster monster100 = ioc.getBean("monster100", Monster.class);
              System.out.println("monster100=" + monster100);
          }
      }
      

      💖基于XML的bean的自动装配

      在spring的ioc容器, 可以实现自动装配bean

      这里说的Action就是我们前面学习过的Servlet -> 充当Controller

      Dao

      package com.zzw.spring.dao;
      public class OrderDao { //DAO类
          public void saveOrder() {
              System.out.println("保存一个订单....");
          }
      }
      

      Service

      package com.zzw.spring.service;
      public class OrderService { //Service类
          //OrderDao属性
          private OrderDao orderDao;
          //getter方法
          public OrderDao getOrderDao() {
              return orderDao;
          }
          //setter方法
          public void setOrderDao(OrderDao orderDao) {
              this.orderDao = orderDao;
          }
      }
      

      Action

      package com.zzw.spring.web;
      public class OrderAction { //Servlet就是Controller
          //OrderService属性
          private OrderService orderService;
          
          //getter方法
          public OrderService getOrderService() {
              return orderService;
          }
          
          //setter方法
          public void setOrderService(OrderService orderService) {
              this.orderService = orderService;
          }
      }
      

      bean03.xml

      
      
      
      
      
      
      

      通过自动装配来对属性赋值

      //通过自动装配来对属性赋值
      public class SpringBeanTest {
          @Test
          public void setBeanByAutowire() {
              ApplicationContext ioc =
                      new ClassPathXmlApplicationContext("beans03.xml");
              OrderAction orderAction = ioc.getBean("orderAction", OrderAction.class);
              //验证是否自动装配上OrderService
              System.out.println(orderAction.getOrderService());
              //验证是否自动装配上OrderDao
              System.out.println(orderAction.getOrderService().getOrderDao());
          }
      }
      

      byName方式讲解

      
      
      
      
      

      💖Spring El 表达式配置Bean

      1.Spring Expression Language, Spring表达式语言, 简称SpEL. 支持运行时查询并可以操作对象.

      2.和EL表达式一样, SpEL根据JavaBean风格的getXxx(), setXxx()方法定义的属性访问对象

      3.SpEL使用#{…}作为界定符, 所有在大括号中的字符都被认为是SpEL表达式

      4.不是重点, 能看懂即可.

      public class SpELBean {
          private String name;
          private Monster monster;
          private String monsterName;
          private String crySound;
          private String bookName;
          private Double reuslt;
          public SpELBean() {
          }
          
          //普通方法, 返回字符串
          public String cry(String crySound) {
              return "发出 " + " 的声音";
          }
          //静态方法 返回字符串
          public static String read(String bookName) {
              return "正在读" + bookName;
          }
      	//getter方法, setter方法
      	
          @Override
          public String toString() {
              return "SpELBean{" +
                      "name='" + name + '\'' +
                      "\nmonster=" + monster +
                      "\nmonsterName='" + monsterName + '\'' +
                      "\ncrySound='" + crySound + '\'' +
                      "\nbookName='" + bookName + '\'' +
                      "\nreuslt=" + reuslt +
                      '}';
          }
      }
      

      beans04.xml

      
      
      
      
          
          
          
          
          
          
          
          
          
          
          
          
      
      
      //通过spring el 对属性赋值
      public class SpringBeanTest {
          @Test
          public void setBeanBySpEl() {
              ApplicationContext ioc = new ClassPathXmlApplicationContext("beans04.xml");
              SpELBean spELBean = ioc.getBean("spELBean", SpELBean.class);
              System.out.println("spELBean=" + spELBean);
          }
      }
      

      测试结果

      spELBean=SpELBean{name='赵志伟'
      monster=Monster{monsterId='1', name='齐天大圣', skill='金箍棒'}
      monsterName='齐天大圣'
      crySound='发出 小猫 的声音'
      bookName='正在读安乐传'
      reuslt=1863.3999999999999}
      

      Spring系列二:基于XML配置bean,在这里插入图片描述,第6张

      下乘: Spring系列三:基于注解配置bean