04_IOC容器装配Bean(xml方式)
2015-07-24 22:28
579 查看
IOC容器装配Bean(xml方式)
1.Spring提供配置Bean三种实例化方式
1)使用类构造器实例化(默认无参数)<beanid="bean1"class="cn.itcast.spring.b_instance.Bean1"></bean>
2)使用静态工厂方法实例化(简单工厂模式)
//下面这段配置的含义:调用Bean2Factory的getBean2方法得到bean2
<beanid="bean2"class="cn.itcast.spring.b_instance.Bean2Factory"factory-method="getBean2"></bean>
3)使用实例工厂方法实例化(工厂方法模式)
//先创建工厂实例bean3Facory,再通过工厂实例创建目标bean实例
<beanid="bean3Factory"class="cn.itcast.spring.b_instance.Bean3Factory"></bean>
<beanid="bean3"factory-bean="bean3Factory"factory-method="getBean3"></bean>
2.Bean的其它属性配置
<bean>元素的id属性和name属性的区别早期Spring开发中Bean的id属性,遵守xml语法id约束
*id的命名要满足XML对ID属性命名规范必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号。
*使用name属性,就可以使用很多特殊字符,早期在struts1和spring整合,如<beanname="/login"class="....LoginAction"/>name中含有/,使用id会报错。
**如果元素没有id只有name,name属性值可以作为id使用
<bean>元素scope属性
*scope="singleton"单例,在SpringIoC容器中仅存在一个Bean实例(默认的scope)
*scope="prototype"多例,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
*scope="request"用于web开发,将Bean放入request范围,request.setAttribute("xxx"),在同一个request获得同一个Bean
*scope="session"用于web开发,将Bean放入Session范围,在同一个Session获得同一个Bean
*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销毁方法<beanid="lifeCycleBean"class="cn.itcast.spring.d_lifecycle.LifeCycleBean"init-method="setup"destroy-method="teardown"></bean>
在JavaBean中书写在,<bean>元素里面定义的2个方法setup和teardown
packagecn.itcast.spring.d_lifecycle;
publicclassLifeCycleBean{
publicvoidsetup(){
System.out.println("初始化...");
}
publicvoidteardown(){
System.out.println("销毁....");
}
}
进行测试:
@Test
//测试Spring生命周期
publicvoiddemo(){
ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");
LifeCycleBeanlifeCycleBean=(LifeCycleBean)applicationContext.getBean("lifeCycleBean");
System.out.println(lifeCycleBean);
}
运行结果:
我们发现:运行完程序,销毁方法没有执行。
解析:这个程序运行了,但是Spring容器并不知道何时销毁。
举个例子:例如把一个Spring容器交给tomcat管理时,tomcat停止时,他就会自动调用destroy方法。那么我们就自己来调用这个方法:applicationContext.close();
此时,就会调用销毁方法:
需要注意的问题:
*destroy-method只对scope="singleton"有效
*销毁方法,必须关闭ApplicationContext对象(手动调用),才会被调用
ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");
applicationContext.close();
2)Bean的完整生命周期(十一步骤)【了解内容,但是对于spring内部操作理解有一定帮助】
①instantiatebean对象实例化
②populateproperties封装属性
③如果Bean实现BeanNameAware执行setBeanName
④如果Bean实现BeanFactoryAware或者ApplicationContextAware设置工厂setBeanFactory或者上下文对象setApplicationContext
⑤如果存在类实现BeanPostProcessor(后处理Bean),执行postProcessBeforeInitialization,BeanPostProcessor接口提供钩子函数,用来动态扩展修改Bean。(程序自动调用后处理Bean)
publicclassMyBeanPostProcessorimplementsBeanPostProcessor{
publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)
throwsBeansException{
System.out.println("第八步:后处理Bean,after初始化。");
//后处理Bean,在这里加上一个动态代理,就把这个Bean给修改了。
returnbean;//返回bean,表示没有修改,如果使用动态代理,返回代理对象,那么就修改了。
}
publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)
throwsBeansException{
System.out.println("第五步:后处理Bean的:before初始化!!");
//后处理Bean,在这里加上一个动态代理,就把这个Bean给修改了。
returnbean;//返回bean本身,表示没有修改。
}
}
注意:这个前处理Bean和后处理Bean会对所有的Bean进行拦截。
⑥如果Bean实现InitializingBean执行afterPropertiesSet
⑦调用<beaninit-method="init">指定初始化方法init
⑧如果存在类实现BeanPostProcessor(处理Bean),执行postProcessAfterInitialization
⑨执行业务处理
⑩如果Bean实现DisposableBean执行destroy
⑪调用<beandestroy-method="customerDestroy">指定销毁方法customerDestroy
*为了能够比较清晰的看到上面的每一个步骤,我们模拟真实开发场景,定义一个接口和一个实现类
//用户数据库操作
publicinterfaceUserDAO{
publicvoidadd();
publicvoidsearch();
}
实现类:
//实现DAO方法
publicclassUserDAOImplimplementsUserDAO,BeanNameAware,ApplicationContextAware,InitializingBean,DisposableBean{
privateStringcompany;
publicUserDAOImpl(){
System.out.println("第一步Bean的实例化...");
}
//设置company
publicvoidsetCompany(Stringcompany){
System.out.println("第二步设置Bean的属性");
this.company=company;
}
//如果实现了BeanNameAware接口,那么会将bean的那么设置到程序中,也就是userDao
publicvoidsetBeanName(StringbeanName){
System.out.println("第三步将xml配置Bean的name设置到程序中:"+beanName);
//<beanid="userDAO"class="cn.itcast.spring.d_lifecycle.UserDAOImpl"></bean>
}
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
System.out.println("第四步将整合工厂上下文对象设置到Bean中");
}
publicvoidafterPropertiesSet()throwsException{
System.out.println("第六步属性设置完成后...");
}
publicvoidsetup(){
System.out.println("第七步配置初始化方法...init-method='setup'");
}
//Bean初始化完毕,如果有业务方法,那么就开始执行,以下方法模拟业务方法。
//这是在接口中定义的业务操作方法
publicvoidadd(){
System.out.println("第九步业务操作....添加");
}
//这是在接口中定义的业务操作方法
publicvoidsearch(){
System.out.println("第九步业务操作....查询");
}
//destroy方法必须自己调用closed方法后才会执行。
publicvoiddestroy()throwsException{
//这个destroy无需配置,实现这个接口,就会自动的去调用destroy方法。
System.out.println("第十步无需配置的销毁方法");
}
publicvoidteardown(){
System.out.println("第十一步通过配置设置销毁方法...");
}
}
其中少了第五步和第八步,此项内容在上面对应序号位置找。
配置文件applicationContext.cfg.xml:
<?xmlversion="1.0"encoding="UTF-8"?>
<!--引入约束来自xsd-config.html文件-->
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans'target='_blank'>http://www.springframework.org/schema/beans/spring-beans.xsd">[/code] <beanid="userDAO"class="cn.itcast.spring.d_lifecycle.UserDAOImpl"init-method="setup"destroy-method="teardown"><!--第二步,设置bean的属性--><propertyname="company"value="itcast"></property></bean><!--必须配置后处理Bean,bean没有id因为由Spring框架内部调用--><beanclass="cn.itcast.spring.d_lifecycle.MyBeanPostProccessor"></bean></beans>
编写测试类:@Test//测试Spring生命周期publicvoiddemo2(){ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");UserDAOuserDAO=(UserDAO)applicationContext.getBean("userDAO");
//执行业务方法userDAO.add();userDAO.search();//关闭工厂applicationContext.close();}
运行结果:
分析:
前面前处理Bean和后处理Bean被执行多次,表示:钩子函数会对每个bean进行拦截(前面已经配置了其他的几个Bean,每个Bean都执行2次à前处理Bean后处理bean)。故而执行多次,反复连续的输出五,八。
第三步和第四步,使我们写的Bean了解Spring容器
第五步和第八步,使用BeanPostProcessor就是钩子函数,作用用来对Bean对象进行扩展。
问题:在userDAO对象所有方法上添加运行时间监控【用后处理bean对目标bean在构造时进行代理,对原有方法进行扩展增强!】
我们可以利用后处理bean(BeanPostProcessor)与动态代理一起完成此功能,我们只需要在后处理bean的postProcessAfterInitialization方法里面改动代码即可/***bean就是对象实例beanName就是xml配置Bean的id或者name*/publicObjectpostProcessAfterInitialization(finalObjectbean,StringbeanName)throwsBeansException{System.out.println("第八步执行后处理Bean的初始化完成后方法...");if(beanName.equals("userDAO")){//需要进行时间监控BeanObjectproxy=Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),newInvocationHandler(){publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{if(method.getName().equals("search")){//增强search方法System.out.println("开始时间:"+System.currentTimeMillis());Objectresult=method.invoke(bean,args);System.out.println("结束时间:"+System.currentTimeMillis());returnresult;}else{//不加强returnmethod.invoke(bean,args);}}});returnproxy;
}returnbean;
}
运行测试结果:
我们发现:在业务方法search的前后环绕了增强的功能!
==========================================================================================================================4.Spring的Bean属性的依赖注入
*spring支持构造器注入和setter方法注入
第一种构造器注入,通过<constructor-arg>元素完成注入/***轿车(构造函数注入属性)*/publicclassCar{privateStringname;privatedoubleprice;publicCar(Stringname,doubleprice){super();this.name=name;this.price=price;}@OverridepublicStringtoString(){return"Car[name="+name+",price="+price+"]";}}
配置文件:<!--构造器注入--><beanid="car"class="cn.itcast.spring.e_di.Car"><!--通过构造器参数,完成属性注入--><constructor-argindex="0"type="java.lang.String"value="保时捷"></constructor-arg><!--第一个参数String类型参数--><constructor-argindex="1"type="double"value="1000000"></constructor-arg></bean>
第二种setter方法注入,通过<property>元素完成注入【开发中常用方式】/***通过setter方法完成属性注入*/publicclassCar2{privateStringname;privatedoubleprice;//注入属性时只需要提供set方法publicvoidsetName(Stringname){this.name=name;}publicvoidsetPrice(doubleprice){this.price=price;}@OverridepublicStringtoString(){return"Car2[name="+name+",price="+price+"]";}}
配置文件:<!--setter方法注入--><beanid="car2"class="cn.itcast.spring.e_di.Car2"><!--通过property元素完成属性注入--><propertyname="name"value="宝马"></property><propertyname="price"value="500000"></property></bean>
*使用<property>元素ref属性,引入另一个Bean对象,完成Bean之间注入//员工类publicclassEmployee{privateStringname;//引入Car2对象privateCar2car2;publicvoidsetName(Stringname){this.name=name;}publicvoidsetCar2(Car2car2){this.car2=car2;}@OverridepublicStringtoString(){return"Employee[name="+name+",car2="+car2+"]";}}
配置文件:<beanid="employee"class="cn.itcast.spring.e_di.Employee"><propertyname="name"value="张三"></property><!--ref引用其他Bean的id或者name--><propertyname="car2"ref="car2"></property></bean>
*名称空间p的使用(Spring2.5新特性)
spring2.5版本引入名称空间p,简化属性注入的配置
p:<属性名>="xxx"引入常量值
p:<属性名>-ref="xxx"引用其它Bean对象
1)引入p名称空间<beansxmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans'target='_blank'>http://www.springframework.org/schema/beans/spring-beans.xsd">[/code]
2)改写<property>注入为p名称空间注入<beanid="car2"class="cn.itcast.spring.e_di.Car2"><!--通过property元素完成属性注入--><propertyname="name"value="宝马"></property><propertyname="price"value="500000"></property></bean><beanid="employee"class="cn.itcast.spring.e_di.Employee"><propertyname="name"value="张三"></property><propertyname="car2"ref="car2"></property><!--ref引用其他Bean的id或者name--></bean>
改写<beanid="car2"class="cn.itcast.spring.e_di.Car2"p:name="宝马"p:price="1000000"></bean><beanid="employee"class="cn.itcast.spring.e_di.Employee"p:name="李四"p:car2-ref="car2"></bean>
*spring3.0之后引入spEL表达式
1)完成对象之间注入<propertyname="car2"ref="car2"></property>
改写为<propertyname="car2"value="#{car2}"></property>
2)使用另一个Bean属性完成注入//单独数据BeanpublicclassCarInfo{publicStringgetName(){return"奇瑞QQ";}publicdoublecaculatePrice(){return200000;}}
配置:<beanid="carInfo"class="cn.itcast.spring.e_di.CarInfo"></bean><beanid="car2_2"class="cn.itcast.spring.e_di.Car2"><propertyname="name"value="#{carInfo.name}"></property></bean>
3)使用另一个Bean方法完成注入<beanid="carInfo"class="cn.itcast.spring.e_di.CarInfo"></bean><beanid="car2_2"class="cn.itcast.spring.e_di.Car2"><propertyname="name"value="#{carInfo.name}"></property><propertyname="price"value="#{carInfo.caculatePrice()}"></property></bean>5.集合属性的注入
spring提供专门标签完成List、Set、Map、Properties等集合元素属性注入
1)注入List(数组)<propertyname="hobbies"><list><!--<value>注入简单类型,<ref/>注入复杂类型--><value>音乐</value><value>体育</value></list></property>
2)注入Set<propertyname="numbers"><set><value>10</value><value>6</value><value>15</value></set></property>
3)注入Map<propertyname="map"><map><!--复杂类型<entrykey-ref=""value-ref=""></entry>--><entrykey="name"value="itcast"></entry><entrykey="address"value="北京"></entry></map></property>
4)注入Properties
*java.utils.Properties类继承java.utils.HashTable
Propertieskey和value都是String类型
例如:<propertyname="properties"><props><propkey="company">传智播客</prop><propkey="pnum">100</prop></props></property>6.在Spring框架中引入多个XML配置文件
第一种并列引入多个XMLApplicationContextapplicationContext=newClassPathXmlApplicationContext("beans1.xml","beans2.xml");
第二种引入总xml文件,在总xml文件引入子xml文件ApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");
*在applicationContext.xml中<importresource="classpath:bean1.xml"/><importresource="classpath:bean2.xml"/>
在开发中主要使用第二种,将配置文件分离配置,便于维护管理
来自为知笔记(Wiz)
相关文章推荐
- Rinetd
- LTE 5G UDN 小站的春天?
- Linux文件夹共享(NFS)
- 机器学习-sklearn库的Cross Validation
- 03_Spring工厂接口
- poj2528 Mayor's posters
- 使用 仿射变换动画需注意之处
- 黑马程序员——Java基础---常见对象1
- 捉妖记观后感
- 02_Spring控制反转案例快速入门
- 气功大师王林栽了让我想起自己的特异功能
- 正则表达式基础
- Android View动画效果—透明效果,旋转效果(二)
- 01_Spring概述
- 当本地html能访问的css和js文件在tomcat里访问不了,巧用cygwin解决
- 《ASP.NET》数据绑定—DropDownList、ListBox
- 数据结构实验之求二叉树后序遍历和层次遍历
- VBScript详解(一)
- ch7 多态
- NOIP2011统计单词数