行业知识
Spring系列二:基于XML配置bean
基于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项目
Bean管理包括两方面: 创建bean对象, 给bean注入属性
通过类型获取bean
案例: 通过spring的ioc容器, 获取一个bean对象, 获取方式: 按类型.
演示通过bean的类型获取对象
@Testpublic 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
演示通过构造器来设置属性
@Testpublic void setBeanByConstructor() { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml"); Monster monster03 = ioc.getBean("monster03", Monster.class); System.out.println("monster03=" + monster03);}通过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(); }}通过内部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(); }}对集合数组属性进行配置
引用/注入 集合/数据类型
- 主要掌握List / Map / Properties 三种集合的使用
- Properties是Hashtable的子类, 是key-value的形式
- 这里的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
@Testpublic 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完成的, 然后执行如下方法
- 执行构造器
- 执行set相关方法
- 调用bean的初始化方法(需要配置)
- 使用bean
- 当容器关闭的时候, 调用bean的销毁方法(需要配置)
●代码实现
1.新建com.zzw.spring.bean.House
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()..."); } @Override public String toString() { return "House{" + "name='" + name + '\'' + '}'; }}2.配置src/beans.xml
3.测试bean的生命周期com.zzw.spring.test.SpringBeanTest
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(); }}4.输出
House构造器 被执行...House setName()=上海豪宅House init()....setMemberDao()...house=com.zzw.spring.bean.House@327bcebdHouse destroy()...
使用细节
1.初始化-init方法和destroy方法, 由程序员来指定
2.销毁方法就是当关闭容器时, 才会被调用
配置bean后置处理器
1在spring的ioc容器, 可以配置bean的后置处理器
2.该 处理器/对象 会在bean初始化方法调用前和初始化方法调用后被调用
3.bean中没有声明初始化方法, 后置处理器依然会发生作用
4.程序员可以在后置处理器中编写自己的代码
●代码实现
1.新建com.zzw.spring.bean.MyBeanPostProcessor
//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; }}2.新建src/beans02.xml配置文件
3.测试com.zzw.spring.test.SpringBeanTest
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.这是一个比较难以理解的知识点.
1.修改src/beans02.xml
2.修改com.zzw.spring.bean.MyBeanPostProcessor
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; }}3.测试com.zzw.spring.test.SpringBeanTest
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();//向下转型 }}4.测试结果(return bean和return null都是下面的输出结果), 这是由底层机制决定的
House构造器 被执行...House setName()=大豪宅postProcessBeforeInitialization()... bean=House{name='大豪宅'} beanName=houseHouse setName()=上海豪宅~House init()....postProcessAfterInitialization()... bean=House{name='上海豪宅~'} beanName=houseHouse构造器 被执行...House setName()=宫殿postProcessBeforeInitialization()... bean=House{name='宫殿'} beanName=house02House 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=1000name=\u5343\u5e74\u9f9f skill=\u65cb\u8f6c\u6253\u51fb
解决中文乱码问题
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系列三:基于注解配置bean
上一篇:点歌机歌曲下载指南
下一篇:服务器怎么防护文件丢失?