IOC容器装配Bean(xml方式)

1.Spring 提供配置Bean三种实例化方式

1)使用类构造器实例化(默认无参数)

  1. <bean id="bean1" class="cn.itcast.spring.b_instance.Bean1"></bean>
    2)使用静态工厂方法实例化(简单工厂模式)
  1. //下面这段配置的含义:调用Bean2Factory的getBean2方法得到bean2
  2. <bean id="bean2" class="cn.itcast.spring.b_instance.Bean2Factory" factory-method="getBean2"></bean>
    3)使用实例工厂方法实例化(工厂方法模式)
  1. //先创建工厂实例bean3Facory,再通过工厂实例创建目标bean实例
  2. <bean id="bean3Factory" class="cn.itcast.spring.b_instance.Bean3Factory"></bean>
  3. <bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
 

2.Bean的其它属性配置

<bean>元素的id属性和name属性的区别

早期Spring开发中Bean的 id属性 ,遵守xml语法id约束

* id 的命名要满足XML对ID属性命名规范 必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号。

* 使用name属性,就可以使用很多特殊字符,早期在struts1和spring整合 ,如<bean name="/login" class="....LoginAction" />  name中含有/ ,使用id会报错。

**如果元素没有id只有name ,name 属性值可以作为id 使用

<bean>元素scope属性

  1. * scope="singleton" 单例 ,在Spring IoC容器中仅存在一个Bean实例 (默认的scope)
  2. * scope="prototype" 多例 ,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean()
  3. * scope="request" 用于web开发,将Bean放入request范围 ,request.setAttribute("xxx") , 在同一个request 获得同一个Bean
  4. * scope="session" 用于web开发,将Bean 放入Session范围,在同一个Session 获得同一个Bean 
  5. * scope="globalSession" 一般用于Porlet应用环境 , 分布式系统存在全局session概念 ,如果不是porlet环境,globalSession 等同于Session 
在开发中主要使用 scope="singleton"、 scope="prototype" 

如果在applicationContext.cfg.xml配置文件中的bean,未指定scope属性,那么默认为singleton

3.Bean的生命周期

1)在配置 <bean> 元素,通过 init-method 指定Bean的初始化方法,通过 destroy-method 指定Bean销毁方法

  1. <beanid="lifeCycleBean"class="cn.itcast.spring.d_lifecycle.LifeCycleBean"init-method="setup"destroy-method="teardown"></bean>
在JavaBean中书写在,<bean>元素里面定义的2个方法setup和teardown
  1. package cn.itcast.spring.d_lifecycle;
  2. publicclassLifeCycleBean{
  3. publicvoid setup(){
  4. System.out.println("初始化...");
  5. }
  6. publicvoid teardown(){
  7. System.out.println("销毁....");
  8. }
  9. }
    进行测试:
  1. @Test
  2. // 测试Spring 生命周期
  3. publicvoid demo(){
  4.     ClassPathXmlApplicationContext applicationContext =newClassPathXmlApplicationContext("applicationContext.xml");
  5.     LifeCycleBean lifeCycleBean =(LifeCycleBean) applicationContext.getBean("lifeCycleBean");
  6.     System.out.println(lifeCycleBean);
  7. }
    运行结果:
 
 

我们发现:运行完程序,销毁方法没有执行。

解析:这个程序运行了,但是Spring容器并不知道何时销毁。

举个例子:例如把一个Spring容器交给tomcat管理时,tomcat停止时,他就会自动调用destroy方法。那么我们就自己来调用这个方法:applicationContext.close();

此时,就会调用销毁方法:
 
 
需要注意的问题:

*  destroy-method 只对 scope="singleton" 有效

*  销毁方法,必须关闭ApplicationContext对象(手动调用),才会被调用

  1. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. applicationContext.close();
 

2)Bean的完整生命周期 (十一步骤)【了解内容,但是对于spring内部操作理解有一定帮助】

①instantiate bean对象实例化

②populate properties 封装属性

③如果Bean实现BeanNameAware 执行 setBeanName

④如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext

⑤如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization,BeanPostProcessor接口提供钩子函数,用来动态扩展修改Bean。(程序自动调用后处理Bean)

  1. publicclassMyBeanPostProcessorimplementsBeanPostProcessor{
  2. publicObject postProcessAfterInitialization(Object bean,String beanName)
  3. throwsBeansException{
  4. System.out.println("第八步:后处理Bean,after初始化。");
  5. //后处理Bean,在这里加上一个动态代理,就把这个Bean给修改了。
  6. return bean;//返回bean,表示没有修改,如果使用动态代理,返回代理对象,那么就修改了。
  7. }
  8. publicObject postProcessBeforeInitialization(Object bean,String beanName)
  9. throwsBeansException{
  10. System.out.println("第五步:后处理Bean的:before初始化!!");
  11. //后处理Bean,在这里加上一个动态代理,就把这个Bean给修改了。
  12. return bean;//返回bean本身,表示没有修改。
  13. }
  14. }
  15. 注意:这个前处理Bean和后处理Bean会对所有的Bean进行拦截。
⑥如果Bean实现InitializingBean 执行 afterPropertiesSet 

⑦调用<bean init-method="init"> 指定初始化方法 init

⑧如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization

⑨执行业务处理

⑩如果Bean实现 DisposableBean 执行 destroy

⑪调用<bean destroy-method="customerDestroy"> 指定销毁方法 customerDestroy

* 为了能够比较清晰的看到上面的每一个步骤,我们模拟真实开发场景,定义一个接口和一个实现类

  1. // 用户数据库操作
  2. publicinterfaceUserDAO{
  3. publicvoid add();
  4. publicvoid search();
  5. }
实现类:
  1. // 实现DAO 方法
  2. publicclassUserDAOImplimplementsUserDAO,BeanNameAware,ApplicationContextAware,InitializingBean,DisposableBean{
  3. privateString company;
  4. publicUserDAOImpl(){
  5. System.out.println("第一步 Bean的实例化 ...");
  6. }
  7. // 设置company
  8. publicvoid setCompany(String company){
  9. System.out.println("第二步 设置Bean的属性");
  10. this.company = company;
  11. }
  12. //如果实现了BeanNameAware接口,那么会将bean的那么设置到程序中,也就是userDao
  13. publicvoid setBeanName(String beanName){
  14. System.out.println("第三步 将xml配置Bean的name设置到程序中:"+ beanName);
  15. // <bean id="userDAO" class="cn.itcast.spring.d_lifecycle.UserDAOImpl"></bean>
  16. }
  17. publicvoid setApplicationContext(ApplicationContext applicationContext)throwsBeansException{
  18. System.out.println("第四步 将整合工厂上下文对象设置到 Bean中 ");
  19. }
  20. publicvoid afterPropertiesSet()throwsException{
  21. System.out.println("第六步 属性设置完成后...");
  22. }
  23. publicvoid setup(){
  24. System.out.println("第七步 配置初始化方法...init-method='setup'");
  25. }
  26.  
  27. //Bean初始化完毕,如果有业务方法,那么就开始执行,以下方法模拟业务方法。

  28. //这是在接口中定义的业务操作方法
  29. publicvoid add(){
  30. System.out.println("第九步 业务操作 .... 添加");
  31. }
  32. //这是在接口中定义的业务操作方法
  33. publicvoid search(){
  34. System.out.println("第九步 业务操作 .... 查询");
  35. }
  36.  
  37. //destroy方法必须自己调用closed方法后才会执行。
  38. publicvoid destroy()throwsException{
  39. // 这个destroy无需配置,实现这个接口,就会自动的去调用destroy方法。
  40. System.out.println("第十步 无需配置的销毁方法");
  41. }
  42. publicvoid teardown(){
  43. System.out.println("第十一步 通过配置设置销毁方法...");
  44. }
  45. }
其中少了第五步和第八步,此项内容在上面对应序号位置找。

配置文件applicationContext.cfg.xml:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- 引入约束 来自xsd-config.html文件 -->
  3. <beansxmlns="http://www.springframework.org/schema/beans"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="
  7. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  8. <beanid="userDAO"class="cn.itcast.spring.d_lifecycle.UserDAOImpl"init-method="setup"destroy-method="teardown">
  9. <!--第二步,设置bean的属性-->
  10. <propertyname="company"value="itcast"></property>
  11. </bean>
  12. <!-- 必须配置后处理Bean , bean没有id 因为由 Spring框架内部调用 -->
  13. <beanclass="cn.itcast.spring.d_lifecycle.MyBeanPostProccessor"></bean>
  14. </beans>
编写测试类:
  1. @Test
  2. // 测试Spring 生命周期
  3. publicvoid demo2(){
  4.     ClassPathXmlApplicationContext applicationContext =newClassPathXmlApplicationContext("applicationContext.xml");
  5.     UserDAO userDAO =(UserDAO) applicationContext.getBean("userDAO");
  6. //执行业务方法
  7.     userDAO.add();
  8.     userDAO.search();
  9.     // 关闭工厂
  10.     applicationContext.close();
  11. }
运行结果:

分析:

前面前处理Bean和后处理Bean被执行多次,表示:钩子函数会对每个bean进行拦截(前面已经配置了其他的几个Bean,每个Bean都执行2次à前处理Bean后处理bean)。故而执行多次,反复连续的输出五,八。

第三步和第四步,使我们写的Bean了解Spring容器

第五步和第八步,使用BeanPostProcessor 就是钩子函数,作用用来对Bean对象进行扩展。

问题: 在userDAO对象所有方法上 添加运行时间监控  【用后处理bean对目标bean在构造时进行代理,对原有方法进行扩展增强!】

我们可以利用后处理bean(BeanPostProcessor)与动态代理一起完成此功能,我们只需要在后处理bean的postProcessAfterInitialization方法里面改动代码即可

  1. /**
  2. * bean 就是对象实例 beanName 就是xml 配置Bean的id 或者 name
  3. */
  4. publicObject postProcessAfterInitialization(finalObject bean,String beanName)throwsBeansException{
  5.     System.out.println("第八步 执行后处理Bean 的初始化完成后方法...");
  6. if(beanName.equals("userDAO")){
  7.     // 需要进行时间监控Bean
  8. Object proxy =Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(),newInvocationHandler(){
  9.     publicObject invoke(Object proxy,Method method,Object[] args)throwsThrowable{
  10.     if(method.getName().equals("search")){
  11.     // 增强search方法
  12. System.out.println("开始时间:"+System.currentTimeMillis());
  13. Object result = method.invoke(bean, args);
  14. System.out.println("结束时间:"+System.currentTimeMillis());
  15. return result;
  16. }else{
  17. // 不加强
  18. return method.invoke(bean, args);
  19. }
  20. }
  21. });
  22. return proxy;
  23. }
  24. return bean;
  25. }
运行测试结果:
    
我们发现:在业务方法search的前后环绕了增强的功能!

==========================================================================================================================

4.Spring的Bean属性的依赖注入

*spring支持构造器注入和setter方法注入

第一种 构造器注入,通过 <constructor-arg> 元素完成注入

  1. /**
  2. * 轿车 (构造函数注入属性)
  3. */
  4. publicclassCar{
  5. privateString name;
  6. privatedouble price;
  7. publicCar(String name,double price){
  8. super();
  9. this.name = name;
  10. this.price = price;
  11. }
  12. @Override
  13. publicString toString(){
  14. return"Car [name="+ name +", price="+ price +"]";
  15. }
  16. }
    配置文件:
  1. <!-- 构造器注入 -->
  2. <beanid="car"class="cn.itcast.spring.e_di.Car">
  3. <!-- 通过构造器参数,完成属性注入 -->
  4. <constructor-argindex="0"type="java.lang.String"value="保时捷"></constructor-arg><!-- 第一个参数 String类型参数 -->
  5. <constructor-argindex="1"type="double"value="1000000"></constructor-arg>
  6. </bean>
 

第二种 setter方法注入, 通过<property> 元素完成注入  【开发中常用方式】

  1. /**
  2. * 通过setter方法完成属性注入
  3. */
  4. publicclassCar2{
  5. privateString name;
  6. privatedouble price;
  7. // 注入属性时 只需要提供set方法
  8. publicvoid setName(String name){
  9. this.name = name;
  10. }
  11. publicvoid setPrice(double price){
  12. this.price = price;
  13. }
  14. @Override
  15. publicString toString(){
  16. return"Car2 [name="+ name +", price="+ price +"]";
  17. }
  18. }
    配置文件:
  1. <!-- setter方法注入 -->
  2. <beanid="car2"class="cn.itcast.spring.e_di.Car2">
  3.     <!-- 通过 property 元素完成属性注入 -->
  4.     <propertyname="name"value="宝马"></property>
  5.     <propertyname="price"value="500000"></property>
  6. </bean>
    * 使用 <property> 元素 ref属性,引入另一个Bean对象,完成Bean之间注入 
  1. // 员工类
  2. publicclassEmployee{
  3. privateString name;
  4. // 引入Car2对象
  5. privateCar2 car2;
  6. publicvoid setName(String name){
  7. this.name = name;
  8. }
  9. publicvoid setCar2(Car2 car2){
  10. this.car2 = car2;
  11. }
  12. @Override
  13. publicString toString(){
  14. return"Employee [name="+ name +", car2="+ car2 +"]";
  15. }
  16. }
    配置文件:
  1. <beanid="employee"class="cn.itcast.spring.e_di.Employee">
  2.     <propertyname="name"value="张三"></property>
  3. <!--ref引用其他Bean的id或者name-->
  4.     <propertyname="car2"ref="car2"></property>
  5. </bean>
 

* 名称空间 p的使用  (Spring2.5 新特性)

spring2.5版本 引入名称空间p, 简化属性注入的配置

p:<属性名>="xxx" 引入常量值

p:<属性名>-ref="xxx" 引用其它Bean对象

1)引入p名称空间

  1. <beansxmlns="http://www.springframework.org/schema/beans"
  2. xmlns:p="http://www.springframework.org/schema/p"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
2) 改写<property>注入为 p名称空间注入 
  1. <bean id="car2" class="cn.itcast.spring.e_di.Car2">
  2.     <!-- 通过 property 元素完成属性注入 -->
  3.     <property name="name" value="宝马"></property>
  4.     <property name="price" value="500000"></property>
  5. </bean>
  6. <bean id="employee" class="cn.itcast.spring.e_di.Employee">
  7.     <property name="name" value="张三"></property>
  8.     <property name="car2" ref="car2"></property> <!-- ref引用其他Bean的id或者name -->
  9. </bean>
改写
  1. <bean id="car2" class="cn.itcast.spring.e_di.Car2" p:name="宝马" p:price="1000000"></bean>
  2. <bean id="employee" class="cn.itcast.spring.e_di.Employee" p:name="李四" p:car2-ref="car2"></bean>
 

* spring3.0之后引入 spEL 表达式

1) 完成对象之间注入

  1. <property name="car2" ref="car2"></property>
改写为
  1. <property name="car2" value="#{car2}"></property>
 
2) 使用另一个Bean属性完成注入 
  1. // 单独数据Bean
  2. publicclassCarInfo{
  3. publicString getName(){
  4. return"奇瑞QQ";
  5. }
  6. publicdouble caculatePrice(){
  7. return200000;
  8. }
  9. }
配置:
  1. <bean id="carInfo" class="cn.itcast.spring.e_di.CarInfo"></bean>
 
  1. <bean id="car2_2" class="cn.itcast.spring.e_di.Car2">
  2.     <property name="name" value="#{carInfo.name}"></property>
  3. </bean>
 

3) 使用另一个Bean方法完成注入

  1. <bean id="carInfo" class="cn.itcast.spring.e_di.CarInfo"></bean>
  2. <bean id="car2_2" class="cn.itcast.spring.e_di.Car2">
  3.     <property name="name" value="#{carInfo.name}"></property>
  4.     <property name="price" value="#{carInfo.caculatePrice()}"></property>
  5. </bean>
 

5. 集合属性的注入

spring提供专门标签完成 List、Set、Map、Properties 等集合元素属性注入

1) 注入List (数组)

  1. <property name="hobbies">
  2.     <list>
  3.         <!-- <value>注入简单类型,<ref />注入复杂类型 -->
  4.         <value>音乐</value>
  5.         <value>体育</value>
  6.     </list>
  7. </property>
 

2)  注入Set

  1. <property name="numbers">
  2.     <set>
  3.         <value>10</value>
  4.         <value>6</value>
  5.         <value>15</value>
  6.     </set>
  7. </property>
 

3) 注入Map

  1. <property name="map">
  2.     <map>
  3.         <!-- 复杂类型<entry key-ref="" value-ref=""></entry> -->
  4.         <entry key="name" value="itcast"></entry>
  5.         <entry key="address" value="北京"></entry>
  6.     </map>
  7. </property>
 

4)  注入Properties

* java.utils.Properties 类继承 java.utils.HashTable

Properties key和value都是String类型

例如:

  1. <property name="properties">
  2.     <props>
  3.         <prop key="company">传智播客</prop>
  4.         <prop key="pnum">100</prop>
  5.     </props>
  6. </property>
 

6.在Spring框架中引入多个XML配置文件

第一种 并列引入多个XML

  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans1.xml", "beans2.xml");
    
    第二种 引入总xml文件,在总xml文件引入 子xml文件 
  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    * 在applicationContext.xml 中 
  1. <import resource="classpath:bean1.xml"/>
  2. <import resource="classpath:bean2.xml"/>
 

在开发中主要使用第二种 , 将配置文件分离配置,便于维护管理

04_IOC容器装配Bean(xml方式)的更多相关文章

  1. 05_IOC容器装配Bean(注解方式)

    IOC容器装配Bean(注解方式) 1.使用注解方式进行Bean注册 xml 方式: <bean id="" class=""> spring2.5 ...

  2. Spring IOC 一——容器装配Bean的简单使用

    下文:SpringIOC 二-- 容器 和 Bean的深入理解 写在前面 这篇文章去年写的,缘起于去年某段时间被领导临时"抓壮丁"般的叫过去做java开发,然后在网上找了一个 Sp ...

  3. Spring框架(3)---IOC装配Bean(注解方式)

    IOC装配Bean(注解方式) 上面一遍文章讲了通过xml来装配Bean,那么这篇来讲注解方式来讲装配Bean对象 注解方式需要在原先的基础上重新配置环境: (1)Component标签举例 1:导入 ...

  4. spring IOC容器实例化Bean的方式与RequestContextListener应用

    spring IOC容器实例化Bean的方式有: singleton 在spring IOC容器中仅存在一个Bean实例,Bean以单实例的方式存在. prototype 每次从容器中调用Bean时, ...

  5. Spring框架(2)---IOC装配Bean(xml配置方式)

    IOC装配Bean (1)Spring框架Bean实例化的方式提供了三种方式实例化Bean 构造方法实例化(默认无参数,用的最多) 静态工厂实例化 实例工厂实例化 下面先写这三种方法的applicat ...

  6. Spring容器装配Bean的三种方式

    欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...

  7. spring IOC装配Bean(注解方式)

    1 Spring的注解装配Bean (1) Spring2.5 引入使用注解去定义Bean @Component 描述Spring框架中Bean (2) Spring的框架中提供了与@Componen ...

  8. Spring 框架 详解 (四)------IOC装配Bean(注解方式)

    Spring的注解装配Bean Spring2.5 引入使用注解去定义Bean @Component  描述Spring框架中Bean Spring的框架中提供了与@Component注解等效的三个注 ...

  9. SpringIOC容器装配Bean

    Spring 的core Container(Spring的核心容器)有四大部分:bean.context.core.expression 在进行Bean的配置时候,需要添加四个jar包 如下: 分别 ...

随机推荐

  1. iBatis面试题

    1) Ibatis中使用like ‘%#filedName#%’ 时,有什么问题? 在xml映射文件中,如果直接按如上写法,会报异常:java.sql.SQLException: Invalid ar ...

  2. OpenStack 镜像密码修改办法

    Contents [hide] 1 场景 2 方案一 3 方案二 4 方案三 5 目前采用方案三 场景 用户将实例里的root密码修改了,/root/.ssh/的公钥文件删除了,然后把密码忘记了,需要 ...

  3. Android横竖屏切换继续播放视频

    只需要重新onSaveInstanceState方法,在其里面记住我们要记录的参数 package com.bawei.day07_videoview; import android.os.Bundl ...

  4. Mongodb 笔记08 了解应用的动态、数据管理、持久性

    了解应用的动态 1. 了解正在进行的操作:db.currentOp() , 可以加过滤条件,从而只显示符合条件的结果. 1). 寻找有问题的操作:db.currentOp() 最常见的操作就是用来寻找 ...

  5. webssh software

    shellinabox是由Markus Gutschke开发的一款自由开源的基于Web的Ajax的终端模拟器.它使用AJAX技术,通过Web浏览器提供了类似原生的 Shell 的外观和感受. yum ...

  6. selenium启动PhantomJS错误

    from selenium import webdriverbrowser = webdriver.PhantomJS(executable_path="D:\PhantomJS\phant ...

  7. Jetty 嵌入式开发(实例)

    我尝试了jetty几个版本,类的使用有些差异,在此记录下jettyVersion = 9.0.2.v20130417 的部分实例 maven 依赖及配置: <properties> < ...

  8. Jquery知识

    1.窗口自适应调整 (设置layout的fit属性值为true即可) //窗口自适应调整 $(function() { windowResize(); //文档载入时加载 $(window).resi ...

  9. React.js入门小案例

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ...

  10. ACM题目————还是畅通工程

    Submit Status Description 某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离.省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路 ...