Spring入门(到自定义类型转化器)

第一章

第二章

1.第一个Spring

2.Spring的核心API

总结

第三章Spring与日志框架整合

第四章 注入

1.什么是注入

1.1为什么需要注入

1.2如何进行注入

2.set注入

2.1jdk内置类型注入

2.2自定义类型

3.set注入属性简化

3.1基于属性简化

3.2基于命名空间p简化

4.构造注入

1.开发步骤

2.构造方法重载

5.注入总结

第五章:反转与依赖注入

1.反转控制(ioc)

2.依赖注入(Dependency injection)

第六章 Spring工厂创建复杂对象

1.Spring工厂创建复杂对象的3种方式

1.1FactoryBean接口

1.2实例工厂

1.3静态工厂

第七章控制Spring工厂创建对象的次数

1.如何控制简单对象的创建次数

2.控制复杂对象的创建次数

3.控制对象次数的用途

3.1只会创建一次对象

3.2每一次都需要创建的对象

第八章对象的生命周期

1.什么是对象的生命周期

2.为什么要学习到对象的生命周期

3.生命周期的3个阶段

3.1创建阶段

3.2初始化阶段

3.3销毁阶段

4.对象生命周期总结

第九章配置文件参数化

配置文件参数开发步骤

第十章自定义类型转换器

1.类型转换器

2.转换异常

3.实现类型转换器

4.自定义转换器的细节

4.1依赖注入日期格式

4.2注册类型转换器的id

4.3Spring框架内置日期类型的转换器

第十一章后置处理bean

1.BeanPostProcessor接口

2.BeanPostProcessor开发步骤

3.开发完整步骤

4.注意点


第一章

1.EJB缺陷

1.运行环境苛刻  需要运行在EJB容器内
    tomcat里面不包含EJB容器
    需要在 wbelogic ApplicationServer容器中运行其中包含EJB   但是这个服务器收费
    或Websphere ApplicationServer 也收费  并且不是开源的不能定制自己的
2.代码移植性差
    wbelogic容器里面与Websphere容器不是互通的  是两家公司分别开发的
    两个容器内接口是不一样的
    
所以EJB是一个重量级的框架

2.什么是Spring

Spring是一个轻量级的javaEE解决方案整合多个优秀设计模式
    轻量级:
        1.对运行环境没有要求的
            开源:tomcat resion jetty
            收费:wbelogic   wbelogic
        2.代码移植性高
            不需要实现额外的接口
            
    javaEE的解决方案
        javaEE是需要分层来开发的  以前的框架只是解决某一层的缺陷 
            struts2可以解决Controller层
            mybatis可以解决DAO层
        但是Spring统一解决:
            SpringMvc解决Controller层
            Aop解决Service层
            整合Mybatis解决Dao层

3.整合的设计模式

1.工厂(核心)
2.代理
3.模板
4.策略

4.设计模式

广义概念:
    面向对象设计中 解决特定问题的经典代码
狭义概念:
    定义的23种设计模式:
        工厂,适配器,装饰器,门锁,代理,模板。。。。
Spring里面重中之重是工厂设计模式

4.1工厂设计模式

4.1.1什么是工厂设计模式

1.通过工厂类创建对象
    正常时通过new来创建对象
    
2.使用工厂好处是:解耦合
    耦合:定义代码间的强关联关系  一方改变必定会影响到另外
        比如:
            一个main函数的控制器:Controllermain{}用来调用业务对象   也就是service
            正常的时创建service对象 然后调用service的impl类 
            UserService userService=new UserServiceImpl  强关联代码就是后面的new
            当serviceimpl改变时候控制器代码也会跟着改变
        最大问题就是不利于代码维护  就是耦合  通过工厂可以解耦合

4.1.2简单的工厂的设计 核心

通过工厂设计模式来解耦合
    创建一个类 类里面写工厂方法 解工厂方法 
    BeanFactory
        public static UserService getUserService(){//返回值时UserService  他是工具类的一种 
            return new UserServiceimpl();
        }
        
    在测试类种通过工厂类来创建对象   没有耦合的了
    UserService userService=BeanFactory.getUserService
    
    当然在工厂里面来看还是有耦合的  当然也是可以解决的
    
    
    
    简单的工厂设置:   这种简单工厂会存在大量的代码冗余
    public class BeanFactory {
        
    private static Properties env = new Properties();
    
        static{
            try {
                //第一步 获得IO输入流 
                    InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
                 //第二步 文件内容 封装 Properties集合中  与配置文件中key 和value是对应的
                //输入流传入
                    env.load(inputStream);
​
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
​
        }
​
        /*
           对象的创建方式:
               1. 直接调用构造方法 创建对象  UserService userService = new UserServiceImpl();
               2. 通过反射的形式 创建对象 解耦合
               Class clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
               UserService userService = (UserService)clazz.newInstance()
               通过反射这一种方式不能完全解决耦合问题  因为全限定名称指的某一个类也是可能被更换的
            所以要将耦合的全限定名字符串信息进行处理;
         */
         
        public static UserService getUserService() {
            UserService userService = null;
            try {
                //com.baizhiedu.basic.UserServiceImpl
                //调用方法来q
                Class clazz = Class.forName(env.getProperty("userService"));
                userService = (UserService) clazz.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return userService;
        }
​
        public static UserDAO getUserDAO(){
            UserDAO userDAO = null;
            try {
                Class clazz = Class.forName(env.getProperty("userDAO"));
                userDAO = (UserDAO) clazz.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return userDAO;
        }
    }
    
​

4.1.3反射工厂

解决工厂类里面的耦合:
    public static UserService getUserService(){//返回值时UserService  他是工具类的一种 
            return new UserServiceimpl();
    }
        工厂类里面有耦合的原因是创建impl方法时候通过了new  impl这个类  这种创建方式一定会有耦合
    对象创建方式
        1.直接通过构造方法来创建   就是new一个对象  这种一定会有耦合
        2.通过反射创建对象  解耦合
            1.Class class=Class.dorName("全限定名(带包带类的名称)")
            2.UserService uservice=Class.newInstance();  
        通过反射这一种方式不能完全解决耦合问题  因为全限定名称指的某一个类也是可能被更换的
            所以要将耦合的全限定名字符串信息进行处理  可以进行jdbc用小的配置文件来进行解决问题
                创建resources包 创建一个applicationContext配置文件  通过key=value方式来写
                    可以用Properties类型的集合来储存Properties文件内容
                    Properties类型是一个特殊的map  但是  key=String value=String
                    Properties {userService =全限定名}
                    Properties.getProperty{"userService"}//获取值
                    userService=全限定名
                
                通过反射来获取
                    Class clazz = Class.forName(env.getProperty("userService"));
                    userService = (UserService) clazz.newInstance();

4.1.4通用工厂设计

解决了简单工厂里面代码的冗余:
通用的工厂代码:
    public class BeanFactory {
    private static Properties env = new Properties();
    static{
            try {
                //第一步 获得IO输入流
                InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
                //第二步 文件内容 封装 Properties集合中 key = userService value = com.baizhixx.UserServiceImpl
                env.load(inputStream);
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
         /*
          key 小配置文件中的key [userDAO或者userService 用户传入那个就会获取对应的全限定名]         
          */
         public static Object getBean(String key){
             Object ret = null;
             try {
                 Class clazz = Class.forName(env.getProperty(key));
                 ret = clazz.newInstance();
             } catch (Exception e) {
                e.printStackTrace();
             }
             return ret;
         }
    }

4.1.5通用工厂的使用方式

1.定义类型 (类)
2.通过配置文件的配置告知工厂
    applicationContext.properties 中 key = value;
3.通过工厂获得类的对象
    Object ret = BeanFactory.getBean("key");

总结

Spring本质:工厂applicationContext(applicationContext.xml

第二章

1.第一个Spring

官方网站:www.Spring.io

1.1环境搭建

设置pom依赖
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
​
配置文件
    1.配置文件放置位置    任意位置
    2.配置文件的命名     没有要求  但是建议  appilcationContext.xml
        创建配置文件时在XML.cationContext下面Spring
    日后应用Spring框架 需要进行配置文件路径位置

2.Spring的核心API

1.applicationContext

工厂applicationContext:
    作用:Spring提供的ApplicationCintext这个工厂     用于对象创建
    好处:解耦合

1.1applicationContext是接口类型

接口:屏蔽实现差异
    1.非web环境: ClassPathXmlApplocationContext (main函数  或  单元测试)
    2.web环境 :  XmlWebApplocationContext

1.2applicationContext重量级资源

applicationContext这个工厂他会占用大量内存 
不会频繁创建对象  一个应用只有一个工厂对象
applicationContext工厂:一定是线程安全的可以多线程访问

2.程序开发

1.创建类型(创建类)
    persion对象
2.配置文件的配置(applicationContext.xml)
     <!--id属性:唯一
        class属性:配置类的全限定名-->
    <bean id="person" class="com.Persion"/>
3.通过工厂类获得对象
  //获取工厂//里面的值写配置文件全限定名
   ApplicationContext context=new ClassPathXmlApplicationContext("/appilcationContext.xml");
   //通过工厂来获得对象.
   Persion persion= (Persion) context.getBean("person");//Spring配置文件名字  应该就是里面id的值

3.细节分析

3.1名词解释

1.Spring工厂创建对象叫做bean或者主键

3.2 Spring工厂相关方法

通过getbean传入配置文件id 获得组件也就是Spring工厂创建的对象
    类名 xxx= context.getBean("person")

3.3Spring工厂中的其他方法

1.getBean里面第一个String类型的
    context.getBean("person");//Spring配置文件名字  应该就是里面id的值
    
2.getBean里面有一个Objeck类型重载  
    context.getBean("persion",Persion.class);不需要强转
    
3.getBean直接传入class
    context.getBean(Persion.class);也不需要强转   
    注意: 注意此时在Speing工厂的配置文件中 只能有一个bean标签是person类型
            如果有多个bean标签的person 通过这个方法获取person的话会抛出异常  
            所以要保证使用该方法只能有一个bean标签是person类型
            
4.context.getBeanDefinitionNames方法:拿到配置文件里面所有bean标签的id值
    是获取bean定义的名字   也就是配置文件里面bean的id值     因为或许有多个bean所以要使用一个数组接受
        String[] BeanDefinitionNames =context.getBeanDefinitionNames();
         for (String BeanDefinitionName:BeanDefinitionNames){  循环打印
            System.out.println(BeanDefinitionName);  拿到配置文件里面所以bean标签的id值 打印出来
        }
        
5.context.getBeanNamesForType()方法:获取指定类型的bean的id值
     也是获取多个bean的id值  用数组接受                            指定类型
      String[] BeanDefinitionNames =context.getBeanNamesForType(Persion.class)
        for (String BeanDefinitionName:BeanDefinitionNames){
            System.out.println(BeanDefinitionName);拿到指定类型的id值
        }
​
6.context.containsBeanDefinition:返回时一个布尔值  判断是否存在id值的存在
                                                /里面的值写配置文件全限定名
     ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
        if (context.containsBeanDefinition("person")){    //判断是否存在id值是person的存在
            System.out.println(true);
        }else {
            System.out.println(false);
        }
        
        
7.context.containsBean:也是判断是否存在id值的存在   效果暂时与containsBeanDefinition一样
    if (context.containsBean("person")){
            System.out.println(true);
        }else {
            System.out.println(false);
        }

3.4配置文件中需要注意的

1.只配置class属性   
    <bean class=""/>
    测试是否有id
        String[] BeanDefinitionNames =context.getBeanDefinitionNames();
        for (String BeanDefinitionName:BeanDefinitionNames){
            System.out.println(BeanDefinitionName);
        }
    1.Spring会给一个默认的id  默认id是全限定名#+数字    数字是从0开始的
    2.应用场景是如果这个bean只会使用一次可以省略id值,但是如果会使用多次或被其他bean引用则需要设置id值
2.name属性:在Spring的配置文件中为bean对象定义别名
    使用方式:
       与id的使用方式类似
          Persion persion= (Persion) context.getBean("person");//别名
            System.out.println(persion);
        }
    相同:
        都可以通过getBean(获取到对象)   id=name   即便是标签里面没有设置id
    区别:
        1.别名可以定义多个  别名间通过,分割      但是 id属性只能是唯一的
        2.xml里面:
            id的值必须要以字母开头  与设置属性名规则一样
            但是name属性 命名时候是没有要求的   会在特殊命名的场景下进行使用
        3.代码上区别:
            当使用到name的时候  containsBean与containsBeanDefinition的区别就出来了
                containsBeanDefinition:只能判断id  如果写入name别名的话是判断不出来的
                containsBean:是可以判断出name的值的

4.Speing工厂底层实现原理

底层原理最为核心的就是
    1.通过ClassPathXmlApplicationContext工厂读取配置文件appilcationContext.xml
    2.通过反射创建对象 Class<?> class=Class.forName(Class的值);
反射的底层是会调用构造方法(无参构造方法)
    1.类里面提供一个无参构造
        public Persion() {
            System.out.println("xxxxxx");
        }
    2.测试   反射创建对象是会调用对象自己的构造方法
        @Test
            public  void  test1() {
                ApplicationContext context = new CPXlAC("/appilcationContext.xml");
                Persion persion = context.getBean("person", Persion.class);
                System.out.println(persion);
            }
     
使用反射创建对象等同于 new  一个对象
​
如果反射调用的无参构造方法是一个私有方法  也是会调用的

总结

开发过程中基本上所有  对象都会交给Spring工厂来创建
    但是 实体对象(entity)不会交给Spring工厂来创建    是由持久层框架来创建的  因为他需要拿到数据

第三章Spring与日志框架整合

Spring与日志框架进行整合  日志框架可以在控制台中 输出Spring框架运行过程中一些重要的信息
好处:了解Spring框架的运行过程利于程序的调试

1.Spring如何整合日志框架

默认:
    Spring1   2   3  这三个版本早期是COMMONS-logging.jar 整合
    Spring5.x 默认整合框架是 logback  log4j2
Spring 整合log4j
    1.引入jarbao
    2.引入Properties配置文件
引入pom依赖:
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.30</version>
    </dependency>
​
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
配置文件:
    log4j.rootLogger=debug , console
    
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 

第四章 注入

1.什么是注入

通过Spring工厂及配置文件 为所创建对象的成员变量赋值

1.1为什么需要注入

1.因为通过代码进行赋值有耦合:
    //用于测试注入
    @Test
        public  void  test9() {
            ApplicationContext context
                  =new   ClassPathXmlApplicationContext("/appilcationContext.xml");
            Persion persion= (Persion) context.getBean("persion");
            persion.setId(1);
            persion.setName("xxx");
            System.out.println(persion);
        }

1.2如何进行注入

1.类要为成员变量进行get/set
      private Integer id;属性
      public void setId(Integer id) { set方法
        this.id = id;
      }
2.配置对应的配置文件
    bean id="persion" name="person,p1" class="com.Persion">
        <!--name属性的值与类的成员变量对应-->
        <property name="id">
            <value>10</value>
        </property>
        <property name="name">
            <value>xxxxxx</value>
        </property>
    </bean>
3.测试配置文件解耦合
    @Test
        public  void  test9() {
            ApplicationContext context
                  =new   ClassPathXmlApplicationContext("/appilcationContext.xml");
            Persion persion= (Persion) context.getBean("persion");
            System.out.println(persion);
        }
注入的主要目的就是解耦合,如何数据不满意可以直接改配置文件  不需要在代码层面更改
​
Spring会通过底层调用对象对应的set方法完成成员变量赋值  

2.set注入

如果类的成员变量类型不止是int或String类型话  配置文件里面:
     <property name="name">
            //里面嵌套的value需要改变  可能是集合  数组  或者自定义类型
        </property>
注入时候成员变量注入的类型是不一样的  导致赋值标签发生改变 
    set注入:
        jdk内置类型:
            8种基本类型+String
            数组
            set集合
            map集合
            list集合
            properites集合
        用户自定义:
            自定义

2.1jdk内置类型注入

2.1.1String+8种基本类型

1.<value>suns</value>   

2.1.2数组

配置文件:只需要在value外面嵌套一个list就可以了
       <property name="emails">
            <list>
                <value>
                    <!--对应的数组的元素-->
                    xxx@zzzz.com.cn
                </value>
            </list>
        </property>
测试:
    //用于测试数组成员变量的赋值
    @Test
        public  void  test11() {
          ApplicationContext context=
       new ClassPathXmlApplicationContext("/appilcationContext.xml");
           Persion persion= (Persion) context.getBean("persion");
           String[] emails= persion.getEmails();
           for (String email:emails){
               System.out.println(email);
           }
        }

2.1.3set集合

配置文件:如果没有规范泛型的话那么就是Objcke类型  在set标签里面嵌套什么都行
     <property name="stringSet">
            <set>
                <!--设置set里面每一个元素  
                set本身是无序的
                set对于重复的数据会过滤调-->
                <value>1001</value>
            </set>
        </property>
测试:
    @Test
        public  void  test12() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            Persion persion= (Persion) context.getBean("persion");
            Set<String>strings=persion.getStringSet();
            for (String email:strings){
                System.out.println(email);
            }
        }
配置文件里面set里面嵌套的value可能会改变  因为set<String> 里面规定的泛型
    作为set集合来讲 set标签是核心  但是里面嵌套读取set元素的标签是不固定的

3.1.4list集合

集合:list是可以重复的 是有序的  
    配置文件里面list里面嵌套的value可能会改变  因为list<String> 里面规定的泛型
    作为list集合来讲 list标签是核心  但是里面嵌套读取list元素的标签是不固定的
        如果没有规范泛型的话那么就是Objcke类型  在list标签里面嵌套什么都行
 <property name="list">
        <list>
            <value>1xxxxxxx</value>
            <value>2xxxxxxx</value>
            <value>2xxxxxxx</value>
        </list>
    </property>
​
测试:
    //用于测试list集合成员变量的赋值
    @Test
        public  void  test13() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            Persion persion= (Persion) context.getBean("persion");
            List<String> strings=persion.getList();
            for (String email:strings){
                System.out.println(email);
            }
        }

3.1.5Map集合

Map集合最大的特定是map由键值对组成的
配置:
    <property name="map">
        <map>   map集合用map标签
            <entry>  一个entry代表一个键值对
                <key>
                    <!--此时key的泛型是String
                        以后可能改变-->
                    <value>名</value>
                </key>
                <value>小行星</value>
            </entry>
        </map>
    </property>
    
测试:
     //用于测试Map集合成员变量的赋值
     @Test
        public  void  test14() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            Persion persion= (Persion) context.getBean("persion");
            Map<String,String> strings=persion.getMap();
            Set<String>key=strings.keySet();
            for (String keys:key){
                System.out.println(keys+":"+strings.get(keys));
            }
        }

3.1.6Properites

Properites类型  特殊的map  key=String  value必须是String
配置“:值就直接写在prop标签里面  因为Properites的值只能是字符串类型
    <property name="properties">
            <props>
                <prop key="班级">一班</prop><!--一个prop代表一个键值对-->
                <prop key="年级">一年级</prop>
            </props>
        </property>
测试:
    

2.2自定义类型

2.2.1第一种

为成员 变量提供get/set
​
<bean id="userService" class="userDaoImpl">
   <property name="userDao">
       <!--指定类型  将dao对象赋值给dao成员变量-->
       <bean class="userDaoImpl"/>
    </property>
</bean>

2.2.2第二种(推荐)

如过多个service都需要userDao的话第一种方法就明显的不适合了  代码冗余严重
而且如果用第一种方法的话写一次就会创建一个dao对象  所以不适和
​
进行get/set
配置文件:
    先创建出来userdao
    <bean id="userDao"class="全限定名称impl">
    第一个:
        <bean id="userService" class="全限定名">
           <property name="userDao">
               <ref bean="dao对象定义id的值  userdao">
            </property>
        </bean>
    第二个:
        <bean id="MapperService" class="全限定名">
           <property name="userDao">
               <ref bean="dao对象定义id的值  userdao">
            </property>
        </bean>
dao对象只创建了一次  其他service需要的可以直接引用

3.set注入属性简化

3.1基于属性简化

1.jdk类型注入:
        <property name="emails">
             <value>
                xxx@zzzz.com.cn
             </value>
        </property>
    简化: 注意只能简化8种基本类型+String
        <property name="emails" value="值"/>
​
2.用户自定义:简化
    <property name="emails" ref="userdao"/>

3.2基于命名空间p简化

对应着上面未简化时候的代码:
​
    xml导入xmlns:p="http://www.springframework.org/schema/p"
    在bean标签里面添加p:属性=“值”
​
    案例:  适用于8种基本类型和String
        <bean id="Persion" class="com.Persion" p:name="xiao" p:id="12"/>
        在p后面直接写属性和对应的值
        p:id=12   等同于     <property name="id" value="12">
            但是p: 直接在bean标签里面写   property是在bean标签嵌套
​
    案例2:自定义类型的
        <bean id="userService" class="全限定名" p:us erDao-ref="userDao"/>
​

4.构造注入

注入:通过Spring的配置文件 为成员变量赋值
set注入:Spring调用set方法通过配置文件为成员变量赋值
构造方法:Spring调用构造得道通过配置文件为成员变量赋值

1.开发步骤

提供构造方法
    public class Customer implements Serializable {
        private String name;
        private int age;
​
        public Customer(String name, int age) {
            this.name = name;
            this.age = age;
        }
​
        @Override
        public String toString() {
            return "Customer{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
提供配置文件:
    <bean id="Customer" class="com.demo.Customer">
        <!--一个标签代表一个构造方法中的一个构造参数
        顺序和个数都要个构造参数是一样 的-->
        <constructor-arg>
<!--里面是具体的赋值  是什么类型就用对应的标签-->
            <value>xxxxxx</value>
        </constructor-arg>
​
        <constructor-arg>
            <value>12</value>
        </constructor-arg>
    </bean>

2.构造方法重载

2.1构造参数个数不同处理

案例
    public class Customer implements Serializable {
    private String name;
    private int age;
​
    public Customer(String name) {
        this.name = name;
    }
​
    public Customer(int age) {
        this.age = age;
    }
​
    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
配置文件:
    <bean id="Customer" class="com.demo.Customer">
        <constructor-arg>
            <value>asdf</value>
        </constructor-arg>
    </bean>
构造参数个数不同时候我们要通过配置文件中<constructor-arg>标签来区分
    没有被赋值的成员变量会直接根据类型使用默认值:
    
如果想要调用age的构造方法
    <bean id="Customer" class="com.demo.Customer">
        <constructor-arg>
            <value>12</value>
        </constructor-arg>
    </bean>
这时候Spring会根据value来找满足要求的 
    因为都是8种类型加字符串的  所以 12会直接被赋值给name  因为name符合要求 
    所以就需要在<constructor-arg>里面指定类型
    
    <bean id="Customer" class="com.demo.Customer">
        <constructor-arg type="int">
            <value>12</value>
        </constructor-arg>
    </bean>    
当个数一样时候只能区分类型了

5.注入总结

两种注入那个常用的
set注入更常用
    1.构造注入重载比较麻烦
    2.Spring框架里面大量应用了set注入

第五章:反转与依赖注入

1.反转控制(ioc)

1.控制:对于成员变量的控制权
    没有Spring时候:
        在impl里面是直接通过new来完成对成员变量的赋值的
            prucate Userdao user=new UserdaoImpl
    通过Spring
        是在配置文件中通过配置文件完成
        private UserDao  userdao;
        <bean id="xxx" class="全限定名"><bean>
原本控制权从代码中反转到配置文件中  好处就是减去了耦合
底层实现:工厂设计模式

2.依赖注入(Dependency injection)

依赖注入是一种Spring的编程方式
    注入:通过Spring工厂及配置文件  为对象的成员变量赋值
    依赖:比如Service依赖userdao获取数据库信息
        如果发现A  依赖   B     那么b就可以作为a的成员变量   
        进而就可以通过get/set或配置文件  进行赋值注入
        
    好处就是解耦合   

第六章 Spring工厂创建复杂对象

简单对象:指的就是可以直接通过new构造方法创建的对象就是简单对象 
复杂对象:不能直接通过new构造方法创建的对象就是复杂对象   比如 jdbc中连接对象  或sqlsessionfactory对象

1.Spring工厂创建复杂对象的3种方式

1.1FactoryBean接口

实现FactoryBean接口
    public class a implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        //用于书写创建复杂对象的代码  将复杂对象作为方法的返回值  
        比如创建connection的话  那么创建代码就写道这里面 
        return null;
    }
    @Override
    public Class<?> getObjectType() {
        //返回所创建复杂对象的Class对象   比如 返回connection的class  
        创建什么对象就返回什么对象的class
        return null;
    }
    @Override
    public boolean isSingleton() {
            根据所创建对象的特定来决定创建次数
        //如果复杂对象只需要创建一次  就返回true
        //如果复杂对象需要创建多次  就返回false
        return false;
    }
}
Spring配置文件   
    <bean id="conn" class="com.FactoryBean.ConnectionFactoryBean"/>
    虽然配置文件与获取简单对象一样但是含义却发生了改变    
    FactoryBean接口实现类ConnectionFactoryBean   通过工厂getbean(conn)获得的是它所创建的复杂对象
    
测试类:
     //用于测试FactoryBean接口
    @Test
        public  void  test17() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            Connection conn=(Connection) context.getBean("conn");
            System.out.println(conn);
        }

1.1.1细节

1.FactoryBean接口实现类ConnectionFactoryBean   通过工厂getbean(conn)获得的是它所创建的复杂对象也就是Connection  但是如果只是想获取ConnectionFactoryBean 那应该这么办
    应该在通过工厂getbean(&conn)这样就可以了,,当然他的类型都是需要改变的
        ConnectionFactoryBean conn=(ConnectionFactoryBean) context.getBean("&conn");
​
​
​
 //用于测试FactoryBean接口
    @Test
    public  void  test18() {
        ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
        ConnectionFactoryBean conn=(ConnectionFactoryBean) context.getBean("&conn");
        System.out.println(conn);
    }
2.ConnectionFactoryBean类里面isSingleton方法
    返回true只会创建一个对象
    返回false每一次都创建对象
        如果这个复杂对象大家可以共同使用那么就可以返回true  不能就返回false
        比如连接对象是不可以共用的所以  返回false 
            因为如果两个用户同时使用一个连接对象   如果其中一个用户的程序进行比较快   先提交了事务那么会影响到另外一个用户的操作
3.mysql高版本连接创建时候需要制定  ssl证书  没有的话就会报错  解决方式
    在url="jdbc:mysql://localhost:3306/数据库?usessl=false"   就可以解决了
4.依赖注入:DI  我需要你,我就是依赖你  我依赖你就可以把你当成我的成员变量,通过Spring的配置文件来完成赋值  完成注入   
    数据库连接时候需要的数据也可以通过注入来写:
        public class ConnectionFactoryBean implements FactoryBean<Connection>{
        private String driveClassname;
        private String url;
        private String username;
        private String password;
        get/set
          @Override
            public Connection getObject() throws Exception {
                Class.forName(driveClassname);
                Connection conn= DriverManager.getConnection(url,username,password);
                return conn;
            }
​
            @Override
            public Class<?> getObjectType() {
                return Connection.class;
            }
​
            @Override
            public boolean isSingleton() {
                return false;
            }
        }
  
  
  <bean id="conn" class="FactoryBean.ConnectionFactoryBean">
            <property name="driveClassname" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/student"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
      </bean>
把依赖的字符串给通过注入来完成   好处就是解耦合

总结

Spring中用于创建复杂对象的一种方式也是Spring原生提供的后续讲解Speing整合其他框架大量应用到FactoryBean

1.2实例工厂

用于创建复杂对象
    1.避免Spring框架的侵入
        预防离开FactotyBean
    2.整合遗留系统
        拿不到java源码没办法通过FactotyBean来创建

1.2.1实现步骤

遗留代码:
    public class ConnectionFactoryBean1 {
        Connection conn= null;
        public Connection getObject()  {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/student","root","root");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
    }
配置文件:
    <bean id="conn" class="FactoryBean.ConnectionFactoryBean1"></bean>
    通过 绑定conn(也就是遗留代码) 让后使用类里面的方法   完成创建  conn1  复杂对象
    <bean id="conn1" factory-bean="conn" factory-method="getObject"/>
​
​
   //用于测试实例
    @Test
        public  void  test19() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            Connection conn=(Connection) context.getBean("conn1");
            System.out.println(conn);
        }

1.3静态工厂

静态方法创建复杂对象
    public static Connection getObject()  {
        Connection conn= null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/student","root","root");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    
    
<bean id="conn" class="FactoryBean.ConnectionFactoryBean2" factory-method="getObject"/>
+
  //用于测试静态
    @Test
    public  void  test20() {
        ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
        Connection conn=(Connection) context.getBean("conn");
        System.out.println(conn);
    }

第七章控制Spring工厂创建对象的次数

1.如何控制简单对象的创建次数

1.数据连接的代码  
  public class ConnectionFactoryBean2 {
        public static Connection getObject()  {
            Connection conn= null;
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/student","root","root");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
    }
    
2.    
里面scope="singleton"  是创建一次对象   不写这个属性  默认值就是这个
    <bean id="account" class="scope.Account" scope="singleton"/>
里面scope="prototype" 是每次都创建一个新的
    <bean id="account" class="scope.Account" scope="prototype"/>
    
    
3.测试类:
    //用于控制简单对象的创建次数
    @Test
        public  void  test21() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            Account conn=(Account) context.getBean("account");
            Account conn1=(Account) context.getBean("account");
            System.out.println(conn);
            System.out.println(conn1);
        }

2.控制复杂对象的创建次数

只需要在FactoryBean接口中isSingleton方法中它分为true和false(创建一次/每次创建新的)
​
​
如果没有isSingleton方法   还是通过scope属性  进行对象次数的创建

3.控制对象次数的用途

因为有公用的对象   这种对象创建一次就可以
不能公用的对象   这种对象需要每次都需要创建
​
好处:就是节省不必要的资源浪费
​
如果是公用的创建一次(最好是线程安全的  或者能解决线程安全的)  不是公用的反之(线程不安全的)

3.1只会创建一次对象

里面scope="singleton"  是创建一次对象   不写这个属性  默认值就是这个
    <bean id="account" class="scope.Account" scope="singleton"/>
​
SqlSessionFactory:这个工厂只能创建一次   因为它是重量级的对象  一个项目就创建一次
DAO:一般创建一次   因为大家操作都是一样的    都是插入数据或者查询
Service:大量的都是无状态的Service 一般创建一次都被大家公用 

3.2每一次都需要创建的对象

里面scope="prototype" 是每次都创建一个新的
    <bean id="account" class="scope.Account" scope="prototype"/>
​
connection 连接对象 不能被公用 因为需要控制事务之类的
SqlSession | Session  封装了连接  也是不可以的
​
Struets2里面 Action这个也是不能的   每次都需要创建新的

第八章对象的生命周期

1.什么是对象的生命周期

指的是一个对象创建   存活   消亡    一个完整的过程

2.为什么要学习到对象的生命周期

创建一个对象那就new一个对象   一直到进程结束时候才会进行结束
现在由Speing来控制对象的生命周期   了解生命周期更利于我们使用Spring

3.生命周期的3个阶段

3.1创建阶段

1.Spring工厂什么是创建对象
    1.只会被创建一次的话
        Spring在工厂创建同时完成  对象的创建
        
            类public class product {
                public product(){
                    System.out.println("xxxxxxxxxxxxxxxxxxx");
                  }
              }
            配置文件:  配置文件里面使用只会创建一次时候   在创建工厂时候  对象创建
                <bean id="product" scope="singleton" class="life.product"/>
            测试文件:
                @Test
                public  void  test22() {
            ApplicationContext context = new ClassPathXmlApplicationContext("配置文件")
                }
            
            因为工厂创建时候需要用到无参构造所以要在无参构造里面添加
            
            
            
    2.每次都创建的话
        Speing工厂在获取对象的同时来创建对象
            比如使用getBean  这时候才会创建对象  
            
            工厂在获取对象的同时来创建对象
            类public class product {
                public product(){
                    System.out.println("xxxxxxxxxxxxxxxxxxx");
                  }
              }
            配置文件:  配置文件里面使用只会创建一次时候   在创建工厂时候  对象创建
                <bean id="product" scope="singleton" class="life.product"/>
            测试文件:
                @Test
                public  void  test22() {
    ApplicationContext context = new ClassPathXmlApplicationContext("配置文件")
     product p =(product)context.getBean("product"); 只有在这个时候才创建对象
                }
                
                
                
                
如果它只会创建一次的话  不想让工厂创建的同时创建对象   想让它在获取对象同时创建那么怎么办
​
    在配置文件中加入一个属性  lazy-init="true"   他的意思是懒加载初始化  这样他就会在获取对象同时创建

3.2初始化阶段

Spring工厂在创建完对象后  调用对象的初始化方法   完成对应的初始化操作
    1.初始化方法是需要自己提供初始化方法   完成一些初始化工作
    2.初始化是由Spring工厂来调用的

3.2.1InitializingBean接口

第一种

初始化代码写到 afterProperitesSet()  中
最终会由Spring来调用这个方法(接口时Spring提供的所以  会被Sping调用)
​
实例:
    product类:
        public class product implements InitializingBean {
            public product(){
                System.out.println("xxxxxxxxxxxxxxxxxxx");
            }
​
            @Override
            public void afterPropertiesSet() throws Exception {
                //这里面就是初始化操作  Spring会调用
                System.out.println("afterPropertiesSet");
            }
        }
           
      测试类:
            //演示初始化
                @Test
                public  void  test23() {
                    ApplicationContext context =
                        new ClassPathXmlApplicationContext("/appilcationContext.xml");
                }
在product类里面写的会在创建工厂时候调用  就是初始化

第二种

只需要在对象中提供一个普通的方法
     public void xxx()  {
            //这个方法功能跟上面afterPropertiesSet一样   不过自己定义的方法不能被Spring直接调用
     }
     配置文件:init-method="xxx"   这个属性就是给Spring指定方法  让Spring读取配置文件后可以调用该方法
        <bean id="product" class="xxx.product" init-method="xxx">
     
     
     测试初始化:
        //演示初始化第二种方法
            @Test
            public  void  test24() {
                ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
                // product p =(product)context.getBean("product");
            }

细节分析

1.如果一个对象实现了两种方式

两种都满足  那么顺序是什么
    他会先执行Spring的接口   就是第一种方法   在执行自己的方法
案例:两种方法一起使用
类:
    public class product  implements InitializingBean{
        public product(){
            System.out.println("xxxxxxxxxxxxxxxxxxx");
        }
​
        @Override
        public void afterPropertiesSet() throws Exception {
            //这里面就是初始化操作  Spring会调用
            System.out.println("afterPropertiesSet");
        }
       public void xxx()  {
           //这个方法功能跟上面afterPropertiesSet一样   不过自己定义的方法不能被Spring直接调用
           System.out.println("xxxxxxxxxxxxxxxx123");
       }
    }
配置文件:
    <bean id="product" class="life.product" init-method="xxx"/>
测试类:
     @Test
       public  void  test24() {
           ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
          // product p =(product)context.getBean("product");
       }

2.注入一定发生在初始化前面

案例:
类:
    public class product  implements InitializingBean{
        private String name;
​
        public String getName() {
            return name;
        }
​
        public void setName(String name) {
            //如果这个第一个打印那么就是  她在初始化之前打印
            System.out.println("set");
            this.name = name;
        }
​
        public product(){
            System.out.println("xxxxxxxxxxxxxxxxxxx");
        }
​
        @Override
        public void afterPropertiesSet() throws Exception {
            //这里面就是初始化操作  Spring会调用
            System.out.println("afterPropertiesSet");
        }
       public void xxx()  {
           //这个方法功能跟上面afterPropertiesSet一样   不过自己定义的方法不能被Spring直接调用
           System.out.println("xxxxxxxxxxxxxxxx123");
       }
    }
配置问价:
    <bean id="product" class="life.product" init-method="xxx">
            <property name="name" value="ss"/>
     </bean>
测试类:
     @Test
        public  void  test24() {
            ApplicationContext context = new
                              ClassPathXmlApplicationContext("/appilcationContext.xml");
            // product p =(product)context.getBean("product");
        }
结果:
     xxxxxxxxxxxxxxxxxxx
    set
    afterPropertiesSet
    xxxxxxxxxxxxxxxx123

3.初始化操作是什么:

资源初始化: 数据库   io    网络 。。。

总结

第一种:依赖与Spring框架  完成Spring接口
第二种:不需要Spring框架  但是需要额外的一段配置

3.3销毁阶段

Spring销毁对象前会调用对象的销毁方法  完成销毁操作

3.3.1.什么时候销毁

会在工厂关闭时候销毁也就是   ctx.close();
在工厂关闭之前他就会销毁他的对象   在销毁之前就会调用销毁方法  完成销毁操作

3.3.2.销毁方法

根据自己需求来完成销毁方法     
    调用时Spring工厂来完成的

3.3.3怎么定义销毁方法

3.3.3.1DisposableBean

Spring规定的接口  完成接口来实现
    案例
        类:
            public class product  implements DisposableBean {
                public product(){
                    System.out.println("xxxxxxxxxxxxxxxxxxx");
                }
                @Override
                public void destroy() throws Exception {
                    //销毁操作就是资源释放的
                    System.out.println("资源释放");
                }
            }
          测试:
             //演示销毁资源的第二种方式
            @Test
            public  void  test26() {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
  //这个方法定义在他的子类中   它不能调用  所以方法换成ClassPathXmlApplicationContext
                context.close();//只有在关闭工厂时候才能释放资源
            }
结果
 xxxxxxxxxxxxxxxxxxx
 资源释放

3.3.3.2自己定义一个普通的方法作为销毁方法

自己定义一个普通的方法作为销毁方法   当然这个也是需要自己去写配置文件来完成的
​
类:
    public class product   {
        public product(){
            System.out.println("xxxxxxxxxxxxxxxxxxx");
        }
        public void xxxx() throws Exception {
            System.out.println("资源释放1");这个是自己定义的
        }
    }
配置文件:
    <bean id="product" class="life.product" destroy-method="xxxx">  指定方法
测试:
    //演示销毁资源的第二种方式
        @Test
        public  void  test26() {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext.xml");
            //这个方法定义在他的子类中   它不能调用  所以方法换成ClassPathXmlApplicationContext
            context.close();//只有在关闭工厂时候才能释放资源
        }
结果就是资源释放掉
​
 xxxxxxxxxxxxxxxxxxx
 资源释放1
​

3.3.3.3.如果实现两种方法

还是会先执行Spring的接口
    1DisposableBean
结果:
xxxxxxxxxxxxxxxxxxx
资源释放
资源释放1

细节分析

1.销毁的局限性

它只是适用于Scope=”singleton“这种对象 
    也就是只能创建一次的公用对象(singleton)  
    如果是每次都创建一次(prototype)的那么他是不起作用的
​
    类:
    public class product  implements DisposableBean {
        public product(){
            System.out.println("xxxxxxxxxxxxxxxxxxx");
        }
        @Override
        public void destroy() throws Exception {
            //销毁操作就是资源释放的
            System.out.println("资源释放");
        }
        public void xxxx() throws Exception {
            System.out.println("资源释放1");
        }
    }
  
  配置文件:
    <bean id="product" scope="prototype" class="life.product" destroy-method="xxxx">
  测试类:
         //演示使用prototype  多次创建这种  对象销毁情况
            @Test
            public  void  test27() {
                ClassPathXmlApplicationContextcontext=
                    new  ClassPathXmlApplicationContext("/appilcationContext.xml");
                context.close();//只有在关闭工厂时候才能释放资源
            }
  这时候销毁就不管用了  它工厂会关闭  但是他不会销毁 不会执行
    初始化操作没有这个要求

2.什么是销毁操作

资源的释放操作  比如io关闭   连接关闭

4.对象生命周期总结

类里面写的初始化和销毁方法
        public class product  implements DisposableBean ,InitializingBean{
            public product(){
                System.out.println("xxxxxxxxxxxxxxxxxxx");
            }
​
            @Override
            public void destroy() throws Exception {
                //销毁操作就是资源释放的
                System.out.println("这个是Spring框架接口的销毁工作");
            }
            public void xxxx() throws Exception {
                System.out.println("这个是自己定义的销毁方法");
            }
​
            @Override
            public void afterPropertiesSet() throws Exception {
                System.out.println("这个是Spring框架接口的初始化方法");
            }
            public void xxxx1() throws Exception {
                System.out.println("这个是自己定义的初始化方法");
            }
        }

第九章配置文件参数化

把Spring配置文件里面需要经常修改的字符串信息  转移到一个更小 的配置文件中、
​
    1.Spring配置文件里面需要经常修改的字符串信息:
        比如连接数据库  连接数据库里面字符串需要经常改变
    2. 不利于项目的维护
    3.可以将连接数据库字符串配置到properties文件中
        原本的字符串要换成${里面是配置文件的值}
利于项目维护

配置文件参数开发步骤

1.提供配置文件 (.properties)  配置文件位置放在resources下面
    内容
        jdbc.driveClassnam=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/student
        jdbc.username=root
        jdbc.password=root
2.配置文件整合      指定配置文件
        <context:property-placeholder location="classpath:/student.properties"/>
       
3.获取对应的值
    <bean id="conn" class="FactoryBean.ConnectionFactoryBean">
          <property name="driveClassname" value="${jdbc.driveClassnam}"/>
          <property name="url" value="${jdbc.url}"/>
          <property name="username" value="${jdbc.username}"/>
          <property name="password" value="${jdbc.password}]"/>
    </bean>
​
4.测试
    //演示配置文件参数化
        @Test
        public  void  test28() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext1.xml");
            Connection connection=(Connection)context.getBean("conn");
            System.out.println(connection);
        }

第十章自定义类型转换器

1.类型转换器

例如  通过配置文件进行赋值
     <bean id="product" class="life.product">
            <property name="id" value="12"/>
      </bean>
注意此时value里面的值是字符串类型的  而它要把字符串类型的赋值给int类型的id   它是如何进行赋值的
​
在赋值的过程中  Spring通过内置类型转换器(Converter接口)完成了类型的转换
​
作用:Spring通过内置类型转换器把配置文件中字符串类型的数据转换成对象中成员变量对应类型的数据,进而完成了注入     转换器就相当于完成 integer.parselnt("1")这样的操作

2.转换异常

根据自己需求来定义转换器    
   Spring内部没有提供特定的类型转换器  可是在开发过程中还是需要
​
比如    Speing没有定义日期的类型转换器   导致进行注入日期时候会产生异常  
    案例:
        类:
            public class Person implements Serializable {
                private String name;
                private Date birthday;
​
                public String getName() {
                    return name;
                }
​
                public void setName(String name) {
                    this.name = name;
                }
​
                public Date getBirthday() {
                    return birthday;
                }
​
                public void setBirthday(Date birthday) {
                    this.birthday = birthday;
                }
            }
       配置文件:
              <bean id="person" class="converter.Person">
                <property name="name" value="xxx"/>
                <property name="birthday" value="2022-1-18"/>   无法将字符串转换成日期
              </bean>
       测试类:
             //演示自定义类型转换器
            @Test
            public  void  test29() {
                ApplicationContext context =
                    new  ClassPathXmlApplicationContext("/appilcationContext2.xml");
                Person p=(Person)context.getBean("person");
                System.out.println(p);
             }
结果是会报错的  类型转换异常  不能将字符串转换成日期模式   这时候就需要自定义转换器
    报错: to required type 'java.lang.String' to required type 'java.util.Date'

3.实现类型转换器

实现步骤
    1.实现converter接口 接口规定泛型  第一个类型是原类型  第二个类型是转换类型
        
    2.Spring配置文件进行注册(通过配置让Spring框架读取这是一个类型转换器)  
​
s代表配置文件中需要转换类型的日期字符串  
​
return返回的是已经转换好的Date  做为方法的返回值  Spring会自动注入对应属性
1.实现converter接口 接口规定泛型  第一个类型是原类型  第二个类型是转换类型
​
        public class MyDateConverter implements Converter<String, Date> {
            /*
            * 核心作用就是转换类型
            * */
            @Override
            public Date convert(String s) {
                Date date= null;
                try {
                    SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
                    date = simpleDateFormat.parse(s);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                return date;
            }
        }
2.在配置文件中进行配置
    1.创建出来MyDateConverter对象创建出来
        <bean id="MyDateConverter" class="xxxx.MyDateConverter"/>
    2.类型转换器的注册    告诉Spring框架MyDateConverter这个是一个类型转换器
    
            <bean id="person" class="converter.Person">
                <property name="name" value="xxx"/>
                <property name="birthday" value="2022-1-18"/>
            </bean>
​
            <bean id="myDateConverter" class="converter.MyDateConverter"/>
            <!--用于注册类型转换器-->】
             <bean id="conversionService"
                class="org.springframework.context.support.ConversionServiceFactoryBean">
            <!--converters是ConversionServiceFactoryBean的一个set属性-->
                <property name="converters">
                    <set>
                        <ref bean="myDateConverter"/><!--指定对象-->
                    </set>
                </property>
            </bean>
3.测试类:
        //演示自定义类型转换器
        @Test
        public  void  test29() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext2.xml");
            Person p=(Person)context.getBean("person");
            System.out.println(p.getBirthday());
        }

4.自定义转换器的细节

4.1依赖注入日期格式

1.在自定义类型中  定义类型的格式是非常重要的
    SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
    依赖于这个格式  既然依赖那么可以作为成员变量  让后通过Spring配置文件来进行注入
​
MyDateConverter中的日期格式通过依赖注入的方式  由配置文件完成赋值    好处就是解耦合
​
转换器:
    public class MyDateConverter implements Converter<String, Date> {
        private String pattern;//这个就是规定格式的类
​
        public String getPattern() {
            return pattern;
        }
​
        public void setPattern(String pattern) {
            this.pattern = pattern;
        }
        @Override
        public Date convert(String s) {
            Date date= null;
            try {
                SimpleDateFormat simpleDateFormat=new SimpleDateFormat(pattern);
                date = simpleDateFormat.parse(s);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
配置文件:   因为myDateConverter对象 里面有pattern属性  只需要输入对应的value就可以了
       <!--创建myDateConverter对象-->
    <bean id="myDateConverter" class="converter.MyDateConverter">
        <property name="pattern" value="yyyy-MM-dd"/>
    </bean>
测试类:
     //演示自定义类型转换器
        @Test
        public  void  test29() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext2.xml");
            Person p=(Person)context.getBean("person");
            System.out.println(p.getBirthday());
        }

4.2注册类型转换器的id

以下是关于类型转换器的配置文件:
​
​
    <bean id="person" class="converter.Person">
        <property name="name" value="xxx"/>
        <property name="birthday" value="2022-1-18"/>
    </bean>
​
    <!--创建myDateConverter对象-->
    <bean id="myDateConverter" class="converter.MyDateConverter">
        <property name="pattern" value="yyyy-MM-dd"/>
    </bean>
注意:
    如果使用ConversionServiceFactoryBean来完成注册操作的化那么  
        bean的id是必须规定是conversionService   如果是其他的Spring框架是不会注册类型转换器的
    
    <!--用于注册类型转换器-->
    <bean id="conversionService"
          class="org.springframework.context.support.ConversionServiceFactoryBean">
     <!--converters是ConversionServiceFactoryBean的一个set属性-->
        <property name="converters">
            <set>
                <ref bean="myDateConverter"/><!--指定对象-->
            </set>
        </property>
    </bean>
​

4.3Spring框架内置日期类型的转换器

日期格式:2222/01/01    年/月/日
如果写这种格式就不需要进行手工写转换
​
<bean id="person" class="converter.Person">
        <property name="name" value="xxx"/>
        <property name="birthday" value="2022/1/18"/>
    </bean>

第十一章后置处理bean(了解)

核心:BeanPostProcessor作用:对于Spring工厂所创建的对象   进行再加工
​
BeanPostProcessor是一个接口    将要加工的内容写到BeanPostProcessor规定的方法中就好了

1.BeanPostProcessor接口

规定了两个方法:
    1.PostProcessBeforelnitialization(Objeck bean,String beanName){
        Speing创建对象并进行注入后   进行对对象进行加工   通过方法参数获得对象  
        通过返回值交给Spring
        retum bean
    }
    2.PostProcessAfterlnitialization(Objeck bean,String beanName){
        Spring初始化后进行加工    通过方法参数获取对象
        通过返回值交换给Spring
        retum bean
    }
    
程序流程:
    1.调用构造方法 创建对象(进行注入完成后)
    2.交给PostProcessBeforelnitialization这个方法进行加工   retun返回对象
    3.进行初始化(或者自定义初始化)
    4.PostProcessAfterlnitialization方法进行再次加工
    5.交给客户
    
开发中很少使用到Spring初始化
    所以没必要对两个方法进行区分   只需要实现After方法即可 
注意:虽然Before不需要操作  但是需要将Spring传入的值返回

1.1PostProcessBeforelnitialization方法

对刚刚创建好的构造方法进行加工操作:
PostProcessBeforelnitialization(Objeck bean,String beanName){
    加工操作
    //获取构造方法创建的对象  通过参数获取对象   
        第一个bean代表工厂刚刚创建好的对象 
        第二个beanName代表对象的id值  传进来
    //通过方法的返回值  将值返回给Spring
    return bean
}

1.2.PostProcessAfterlnitialization方法

PostProcessAfterlnitialization(Objeck bean,String beanName){
    通过参数获取参数  进行加工    返回值到Spring
    return bean
}

2.BeanPostProcessor开发步骤

2.1实现接口

类   实现BeanPostProcessor接口:
    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
​
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            Categroy categroy=(Categroy)bean;
            categroy.setId(20);
            return categroy;
        }
    }

2.2Spring配置文件中进行配置

<bean id="mybean" class="beanpost.MyBeanPostProcessor"/>

3.开发完整步骤

1.实体类:
    public class Categroy {
        private Integer id;
        private String name;
​
        public Integer getId() {
            return id;
        }
​
        public void setId(Integer id) {
            this.id = id;
        }
​
        public String getName() {
            return name;
        }
​
        public void setName(String name) {
            this.name = name;
        }
    }
    
2.配置文件进行注入
    <bean id="c" class="beanpost.Categroy">
        <property name="id" value="10"/>
        <property name="name" value="xxx"/>
    </bean>
    <bean id="mybean" class="beanpost.MyBeanPostProcessor"/>
​
3.实现BeanPostProcessor
    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            //这个是在注入后执行的  直接返回创建的对象
            return bean;
        }
​
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            //通过bean获取参数对象   让后进行赋值   返回categroy对象  让后交给客户
            Categroy categroy=(Categroy)bean;
            categroy.setId(20);
            return categroy;
        }
    }
​
4.测试:
     //演示后置对象BeanPostProcessor接口
        @Test
        public  void  test30() {
            ApplicationContext context = new ClassPathXmlApplicationContext("/appilcationContext3.xml");
            Categroy p=(Categroy)context.getBean("c");
            System.out.println(p.getName()+p.getId());
        }

4.注意点

在开发过程中BeanPostProcessor会对Spring工厂中所有创建对象进行加工
    比如:
        <bean id="c" class="beanpost.Categroy"/>
        <bean id="a" class="beanpost.Categroy1"/>
    有这两个创建 对象  那么会对c加工   也会对a加工   只要这个配置文件里面的都会进行加工 
        所以要进行判断一下  需要保护一下
             @Override
                public Object postProcessAfterInitialization
                    (Object bean, String beanName) throws BeansException {
                   if (bean instanceof Categroy){
                       Categroy categroy=(Categroy)bean;
                       categroy.setId(20);
                   }
                    return bean;
                }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值