您的位置:首页 > 其它

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")){


//需要进行时间监控Bean


Objectproxy=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;


}




@Override


publicStringtoString(){


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;


}




@Override


publicStringtoString(){


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;


}




@Override


publicStringtoString(){


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属性完成注入

//单独数据Bean


publicclassCarInfo{


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配置文件

第一种并列引入多个XML

ApplicationContextapplicationContext=newClassPathXmlApplicationContext("beans1.xml","beans2.xml");


第二种引入总xml文件,在总xml文件引入子xml文件

ApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");


*在applicationContext.xml中

<importresource="classpath:bean1.xml"/>


<importresource="classpath:bean2.xml"/>


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



来自为知笔记(Wiz)



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: