Spring系列-控制反转

109 阅读13分钟

spring学习笔记

第一章 引言

1、EJB(Enterprise Java Bean)的缺陷

  • 运行环境苛刻

  • 代码移植性差

总结:EJB是一个重量级的框架

2、什么是spring?

Spring是一个轻量级的JavaEE的解决方案,整合众多优秀的设计模式

  • 轻量级:

    1.对于运行环境是没有额外要求的,可以选择开源的服务器,也可以选择收费的服务器,运行在servlet引擎中;

    2.代码移植性高:不需要实现额外的接口

  • JavaEE的解决方案:

    整合设计模式:

    ​ 1、 工厂设计模式

    ​ 2、 代理设计模式

    ​ 3、 模板设计模式

    ​ 4、 策略设计模式

3、设计模式

  • 广义概念:

    面向对象设计中,解决特定问题的经典代码

  • 狭义概念:

    GOF4人帮定义的23中设计模式:工厂、适配器、装饰器、门面、代理、模板……

4、 工厂设计模式

4.1 概念:通过工厂类创建对象
  • ​ 好处:解耦合

  • ​ 耦合:指的是代码之间的强关联关系,一方改变会影响另一方

    4.2 通用工厂的使用方式

​ 4.2.1 定义类型(类)

​ 4.2.2 通过配置文件的配置告知工厂

​ 4.2.3 通过工厂获得类的对象

5、总结

  • ​ Spring的本质:工厂 ApplicationContext

第二章 spring

1、软件版本

​ 1、jdk 1.8

​ 2、Maven 3.5

​ 3、idea

​ 4、SpringFramework 5.1.4

​ 官网:www.spring.io

2、环境搭建

  • spring的jar包
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.4.RELEASE</version>
    </dependency>
    
  • spring的配置文件
    1、配置文件的放置位置:任意位置,没有硬性要求
    2、配置文件的命名:没有硬性要求。建议:applicationContext.xml
    3、Spring的核心API
    • ApplicationContext

      1. 作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
      2. 好处:解耦合
        • ApplicationContext接口类型
          1. 接口:屏蔽实现的差异

          2. 非web环境:ClassPathXmlApplicationContext(main junit)

          3. web环境:XmlWebApplicationContext

    • 重量级资源

      1. ApplicationContext工厂的对象占用大量内存。
      2. 不会频繁的创建对象:一个应用只会创建一个工厂对象。
      3. ApplicationContext工厂:一定是线程安全的(多线程并发访问)
    4、程序开发
    1. 创建类型

    2. 配置文件的配置 applicationContext.xml

    3. 通过工厂类,获得对象

      1. ApplicationCOntext

        1. ClassPathXmlApplicationContext

        2、ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");

      ​ Person person = (person) ctx.getBean("person");

5、细节分析
  • ​ 名词解释

​ 1、Spring工程创建的对象,叫做bean或者组件(component)

  • ​ Spring工厂的相关方法

  • ​ 配置文件中需要注意的细节

    • ​ 只配置class属性 :

      • 上述这种配置 有id值:com.baizhiedu.basic.Person#0

      • 应用场景:如果这个bean只需要使用一次,那么就可以省略id值

        ​ 如果这个bean会使用多次,或者被其他bean引用则需要设置id

    • ​ name属性

      • 作用:用于在Pring的配置文件中,为bean对象定义别名

      • 相同:等效于id

      • 区别:别名可以定义多个,但是id属性只能有一个值

        ​ containsBeanDefinition方法:不能判断name值,只能判断id值

        ​ containsBwan方法:不仅能判断name值,也能判断id值

6、Spring工厂的底层实现原理(简易版)

​ Spring工厂是可以调用对象私有的构造方法创造对象的

​ 核心:

  1. 通过ClassPathXmlApplicationContext工厂读取配置文件apploicationContext.xml

  2. 获取bean标签的相关信息di的值、class的值;通过反射创建对象

    1. Clsss<?> clazz = Class.forName(class的值);
    2. id 的值 = clazz.newInstance();
  3. 反射创建对象底层一会调用对象自己的构造方法

第三章 Spring5.x与日志框架的整合

Spring与日志框架进行整合,日志框架就可以在控制台中,输出Spring框架运行过程中的一些重要的信息。

好处:便于了解Spring框架的运行过程, 利于程序的调试

  • Spring如何整合日志框架

    默认

    1. Spring1.2.3早期都是与commons-logging.jar

    2. Spring5.x默认整合日志框架logback log4j2

      Spring5.x整合log4j

      1. 引入log4j jar包

      2. 引入log4.properties配置文件

    • pom
    • log4j.properties

第四章 注入(injection)

1、什么是注入

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

    1. 为什么需要注入

      通过编码的方式,为成员变量进行赋值,存在耦合

    2. 如何进行注入

      类的成员变量提供get、set方法

      配置spring的配置文件

    3. 注入的好处

      解耦合

2、Spring注入的原理分析(简易版)

  • Spring通过底层调用对象属性对应的set方法,完成成员变量的赋值,这种方法也称之为set注入

第五章 set注入详解

1、针对不同类型的成员变量,在标签,需要嵌套其他标签

2、jdk内置类型

  1. 8种基本类型+String

    • roy
  2. 数组

    <property name="emails">
    	<list>
        	<value>sun@hfja.com.cn</value>
        	<value>ssafun@sfa.com.cn</value>
        	<value>wet@sfwwr.com.cn</value>
        </list>    
    </property>
    
  3. Set集合:无序、不可重复的

    <property>
    	<set>
    		<value>111111</value>
    		<value>222222</value>
    	</set>
    </property>
    
  4. List集合:有序、可重复的

    <property>
    	<list>
    		<value>111111</value>
    		<value>222222</value>
    	</list>
    </property>
    
  5. Map集合

    <map>
    	<entry>
    		<key><value>name</value></key>
    		<value>sun</value>
    	</entry>
    	<entry>
    		<key><value>id</value></key>
    		<value>1</value>
    	</entry>
    </map>
    
  6. Properties

    Properties类型,特殊的Map key=String ,value=String,值只能是String

    <props>
    	<prop key="key1">value1</prop>
    	<prop key="key2">value2</prop>
    </props>
    
  7. 复杂的jdk类型(Date)

    需要自定义类型转换器

3、用户自定义类型

  1. 第一种方式

    • 为成员变量提供set get方法

    • 配置文件进行注入(赋值)

    <bean id="userService" class="xxxx.UserServiceImpl">
    	<prpperty name="userDao">
        	<bean class="xxxx.UserServiceImpl"/>
        </property>
    </bean>
    
  2. 第二种方式

    • 第一种赋值方式存在的问题

    1. 配置文件代码冗余

    2. 被注入的对象(UserDao),多次创建,浪费(JVM)内存资源

    • 为成员变量提供set get方法
    • 配置文件中进行配置
    <bean id="userDao" class="xxx.UserDaoImpl"></bean>
    <bean id="userService" class="xxx.UserServiceImpl">
    	<property name="userDao">
    		<ref bean="userDao"/>
    	</property>
    </bean>
    
  3. Set注入的简化方法

    1. 基于属性简化
    JDK类型注入
    <property name="name">
    	<value>suns</value>
    </property>
    
    <property name="name" value="suns">
    注意:value属性只能简化8种基本类型+String 注入标签
    
    用户自定义类型 
    <peoperty name="userDao">
    	<ref bean="userDao">
    </peoperty>	
    
    <property name="userDao" ref="userDao"/>
    
     2. 基金p命名空间简化
    
    JDK类型注入
    <bean id="person" class="xxx.Person">
    	<property name="name">
    		<value>suns</value>
    	</property>
    </bean>
    
    <bean id="person" class="xxx.Persom" p:name="suns"/>
    注意:value属性 只能简化 8种基本类型+String 注入标签
    
    用户自定义类型
    <bean id="userService" class="xxxx.UserServiceImpl">
    	<property name="userDao">
    		<ref bean="userDao">
    	</property>
    </bean>
    
    <bean id="userService" class="xxxx.UserServiceImpl" p:userDao-ref="userDao"/>
    

第六章 构造注入

1、注入:通过Spring的配置文件,为成员变量赋值
2Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值
3、构造注入: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 ;
	}
}
  • Spring的配置文件
<bean id="costomer" class="xxxx.Customer">
	<constructor-arg>
		<value>suns</value>
	</constructor-arg>
	<constructor-arg>
		<value>18</value>
	</constructor-arg>
</bean>

2、构造方法重载

  1. 参数个数不同时
    1. 通过控制标签的数量进行区分
  2. 构造参数个数相同时
    1. 通过在标签引入type属性,进行类型的区分

3、注入的总结

  1. 未来实战中,应用set注入还是构造注入?
  2. 答案:set注入会更多
    1. 构造注入麻烦(重载)
    2. Spring框架底层 大量应用了 set注入

第七章 反转控制 与 依赖注入

1、反转(转移)控制(IOC--inverce of control)

  • 控制:对于成员变量赋值的控制权

  • 反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中

    好处:解耦合

    底层实现:工厂设计模式

2、依赖注入(Dependency Injecttion DI)

  • 注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值

  • 依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)。

    好处:解耦合

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

Spring工厂:

  • 简单对象:指的就是可以直接通过new构造方法创建对象

    • UserService
    • UserDAO
    • Customer
    • Person
  • 复杂对象:指的是不能直接通过new构造方法创建的对象

    • Connection

      Class.forName("com.mysql.jdbc.Driver");

      conn = DriverManager.getConnection();

1、什么是复杂对象

  • 复杂对象:指的就是不能直接通过new构造方法创建的对象
    • Connection
    • SqlSessionFactory

2、Spring工厂创建复杂对象的3种方式

2.1 FactoryBean接口
  • 开发步骤

    • 实现FactoryBean接口

      public class ConnnectionFactoryBean implements FactoryBean{
      	@Override
      	public Object getIbject() throws Exception{
      	
      	}
      	@Override
      	public Class<?> getObjectType(){
      	
      	}
      	@Override
      	public boolean isSingleton(){
      	
      	}
      }
      
  • Spring配置文件的配置

    # 如果Class中指定的类型是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象 Connection
    <bean id="conn" class="com.xxxx.factorybean.ConnectionFactoryBean"/>
    

细节:

  • 如果就像获得FactoryBean类型的对象 ctx.getBean("&conn")

    获得就是ConnectionFactoryBean对象

  • isSingleton方法

    • 返回true只会创建一个复杂对象
    • 返回false每一次都会创建新的对象
    • 问题:根据这个对象的特点,决定是返回true(SqlSessionFactory)还是false(Connection)
  • mysql共版本连接创建时,需要指定SSL证书,解决问题的方式

    url = "jdbc:mysql://localhost:3306/suns?useSSL=false"
    
  • 依赖注入的体会(DI)

    把ConnectionFactoryBean中依赖的4个人字符串信息,进行配置文件的注入
    好处:解耦合
    <bean id="conn" class="com.xxxx.factorybean.ConnectionFactoryBean">
    	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    	<property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/>
    	<property name="username" value="root"/>
    	<property name="password" value="123456"/>
    </bean>
    
  • FactoryBean的实现原理【简易版】

    接口回调
    1、为什么Spring规定FactoryBean接口  实现 并且getObject()?
    2、ctx.getBean("conn")获得是复杂对象Connection而没有获得ConnectionFactoryBean
    
    Spring内部运行流程
    1、通过conn获得ConnectionFactoryBean类的对象,进而通过instenceOf判断出是FactoryBean接口的实现类
    2、Spring按照规定  getObject --->  Connection
    3、返回Connection
    
  • FactoryBean总结

    Spring中用于创建复杂对象的一宗方式,也是Spring原生提供的,Spring整合其他框架,大量应用了FactoryBean
    
2.2 实例工厂
1、避免Spring框架的侵入
2、整合遗留系统
  • 开发步骤

    <bean id="connFactory" class="com.xxxx.factory.ConnectionFactory"></bean>
    <bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
    
2.3 静态工厂
  • 开发步骤

    <bean id="conn" class="com.xxxx.factoryBean.StaticConnectionFactory" factory-method="getConnection"/>
    

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

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

<bean id="account" scope="singleton|prorotype" class="xxxx.Account">
singleton:只会创建一次简单对象,默认值
prototype:每一次都会创建新的对象

2、如何控制复杂对象的创建次数

FactoryBean{
	isSingleton(){
		return true   只会创建一次
		return false  每一次都会创建新的
	}
}
如没有isSingleton方法   还是通过scope属性   进行对象创建次数的控制

3、为什么要控制对象的创建次数?

好处:节省不必要的内存浪费
  • 什么样的对象只创建一次?

    1、SqlSessionFactory
    2、DAO
    3、Service
    
  • 什么样的对象 每一次都要创建新的?

    1、Connection
    2、SqlSession  |   Session
    3、Struts2   Action
    

第十章 对象的生命周期

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

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

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

由Spring负责对象的创建、存活、销毁,了解生命周期,有利于我们使用好Spring为我们创建的对象

3、生命周期的3个阶段

  • 创建阶段

    Spring工厂何时创建对象
    
    • scope="singlenton"

      Spring工厂创建的同时,对象的创建
      
      注意:设置scope="singlenton" 这种情况下,也需要在获取对象的同时,创建对象
      <bean lazy-init="true">   懒加载
      
    • scope="prototype"

      Spring工厂会在获取对象的同时,创建对象
      ctx.getBean("")
      
  • 初始化阶段

    Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作
    
    1、初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
    2、初始化方法调用:Spring工厂进行调用
    
    • InitializingBean接口

      // 程序员根据需求,实现的方法,完成初始化操作
      public void afterPropertiesSet(){
          
      }
      
    • 对象中提供一个普通的方法

      public void myInit(){
          
      }
      <bean id="product" class="xxx.Product" init-method="myInit"/>
      
    • 细节分析

      1. 如果一个对象既实现InitializingBean ,同时又提供的普通初始化方法,顺序如何?

        1.InitializingBean
        2.普通初始化方法
        
      2. 注入一定发生在初始化操作的前面

      3. 什么叫做初始化操作

        资源的初始化:数据库、IO、网络......
        
  • 销毁阶段

    Spring销毁对象前,会调用对象的销毁方法,完成销毁操作
    1.Sprig什么时候销毁所创建的对象?
    	ctx.close();
    2.销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
    	调用:Spring工厂完成调用
    
    • DisposableBean

      public void destory()throws Exception{
          
      }
      
    • 定义一份普通的销毁方法

      public void myDestroy()throws Exception{
          
      }
      <bean id="" class="" init-method="" destory-method="myDestroy"/>
      
    • 细节分析

      1. 销毁方法的操作只适用于scope="singleton"

      2. 什么叫做销毁操作?

        主要指的就是 资源的释放操作  io.close()   connnection.close()
        

第十一章 配置文件参数化

把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中
1. Spring的配置文件中存在需要经常修改的字符串?
	存在,以数据库连接相关的参数  代表
2. 经常变化字符串,在Spring的配置文件中,直接修改
	不利于项目维护(修改)
3. 转移到一个小的配置文件(.properties)
	利于维护(修改)
	
配置文件参数化:利于Spring配置文件的维护(修改)

1、配置文件参数化的开发步骤

  • 提供一个小的配置文件(.properties)

    名字:随便
    放置位置:随变
    
    jdbc.driverClassName = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/suns?useSSL=false
    jdbc.username = root
    kdbc.password = 123456
    
  • Spring的配置文件与小配置文件进行整合

    applicationContext.xml
    <context:property-placeholder location="classpath:/db.properties"/>
    
  • 在Spring配置文件中通过${}获取小配置文件中对应的值

    <!-- Spring配置文件与小配置文件的整合-->
    
    <context:property-placeholder location="classpath:/db.properties"/>
    
    <bean id="conn" class="com.xxxx.factoryBean.ConnectionFactoryBean">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}">
    </bean>
    

    第十二章 自定义类型转换器

1、类型转换器

作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入

2、自定义类型转换器

  • 类 implements Converter接口

    public class MyDateConverter implements Converter<String, Date>{
        
        @Override
        public Date Convert(String source){
            Date date = null;
            try{
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
                date = sfa.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    
  • 在Spring的配置文件中进行配置

    • MyDateConverter对象创建出来

      <bean id="myDateConverter" class="xxxx.MyDateConverter"/>
      
    • 类型转换器的注册

      <!-- 用于注册类型转换器-->
      <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
          <property name="converters">
              <set>
              	<ref bean="myDateConverter"/>
              </set>
          </property>
      </bean>
      

3、细节

  • 日期格式的配置

    <!--Spring创建MyDateConverter类型对象-->
    <bean id="myDateConverter" class="com.xxxx.converter.MyDateConverter">
    	<property name="pattern" value="yyyy-mm-dd">
    </bean>
    
  • ConversionServiceFactoryBean 定义id属性值必须是:conversionService

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

    只支持:日期格式:2020/05/01
    

第十三章 后置处理Bean

BeanPostProcessor作用:对Spring工厂所创建的对象进行再加工

AOP底层实现:

注意:BeanPostProcessor接口
  • 后置处理bean的运行分析

    Object postProcessBeforeInitiallization(Object bean, String beanName)
    作用:Spring创建完对象,并进行注入后,可以运行Before方法进行加工
    获得Spring创建好的对象:通过方法的参数
    最终通过返回值交给Spring框架
    Object postProcessAfterInitiallization(Object bean, String beanName)
    作用:Spring执行完对象的初始化操作后,可以运行After方法进行加工
    获得Spring创建好的对象:通过方法的参数
    最终通过返回值交给Spring框架
    
    实战中:
    很少处理Spring的初始化操作:没有必要区分Before  After,只需要实现其中一个After方法即可
    注意:return bean对象
    
  • BeanPostProcessor的开发步骤

    1. 累实现BeanPostProcessor接口

      public class MyBeanPostProcessor implements beanPostProcessor{
          @Override
          public Object postProcessBeforeInitializstion(Object bean, String beanName){
              return bean;
          }
          @Override
          public Obejct poseProcessorAfterInitialization(Object bean, String beanName){
              if(bean instanceof Categroy){
                  Categroy categroy = (Categroy) bean;
              	categroy.setName("roy");
              }
              return bean;
          }
      }
      
    2. Spring的配置文件中进行配置

      <bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
      
    3. BeanPostProcessor细节

      BeanPostProcessor会对Spring工厂中所有创建的对象进行加工