• IOC容器装配Bean(xml方式)


    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 的命名要满足XMLID属性命名规范 必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号。

    * 使用name属性,就可以使用很多特殊字符,早期在struts1spring整合 ,如<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.SpringBean属性的依赖注入

       

    *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引用其他Beanid或者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提供专门标签完成 ListSetMapProperties 等集合元素属性注入 

     

    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 keyvalue都是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"/>
     

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

     

    博采众长才能相互印证,故步自封必将粗陋浅薄!
  • 相关阅读:
    WebStorm下配置supervisor热部署NodeJS
    Node.js如何使用MySQL的连接池实例
    node.js中mysql连接池的使用
    VMware10下CentOS7的详细安装图解
    APP数据埋点
    (转载) Mysql----Join用法(Inner join,Left join,Right join, Cross join, Union模拟Full join)及---性能优化
    Python3 _ 读取大文件
    Python3 -- PySQL -- 将函数封装在类中
    Python3 MySQL 数据库连接
    js--同步运动json上
  • 原文地址:https://www.cnblogs.com/tangwan/p/4674969.html
Copyright © 2020-2023  润新知