Spring IOC
第一章 引言
1.什么是Spring
Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式
-
轻量级
1.对于运行环境没有额外要求 2.代码移植性高 不需要实现额外接口 -
JavaEE的解决方案
Controller Service DAO 一站式服务 -
整合了设计模式
1.工厂模式 2.代理模式 3.模板模式 4.策略模式
2.设计模式
1.广义概念
面向对象设计中,解决特定问题的经典代码
2.狭义概念
GOF4人帮定义的23种设计模式:工厂,适配器,装饰器,门面,代理....
3.工厂设计模式
3.1什么是工厂模式
1.概念:通过工厂类,创建对象
User user = new User();
2.好处:解耦合
耦合:指代码间的强关联关系,一方的改变会影响另一方
问题:不利于代码维护
简单:把接口的实现类,硬编码在程序中
UserService userService = new UserServiceImpl();
3.2简单工厂设计
public class BeanFactory {
private static Properties properties = new Properties();
static {
try{
//获取IO输入流
InputStream in = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//文件内容封装到Properties集合中
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService(){
UserService userService = null;
try {
userService =(UserService) Class.forName(properties.getProperty("userService")).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userService;
}
public static UserDao getUserDao(){
UserDao userDao =null;
try {
userDao= (UserDao)Class.forName(properties.getProperty("userDao")).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userDao;
}
}
3.3通用工厂的设计
-
问题
简单工厂会产生大量的冗余 -
通用工厂的代码
//创建一切想要的对象 public static Object getBean(String key){ Object object = null; try { object=Class.forName(properties.getProperty(key)).newInstance(); } catch (Exception e) { e.printStackTrace(); } return object; }
3.4通用工厂的使用方式
1. 定义类型(类)
2. 通过配置文件的配置告知工厂
key=value
3. 通过工厂获得类的对象(反射)
4.总结
Spring本质:工厂 ApplicationContext(applicationContext.xml)
第二章 第一个Spring程序
1.环境搭建
-
Spring的jar包
设置maven依赖 <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.9.RELEASE</version> </dependency> -
Spring的配置文件
1.配置文件的放置位置:任意位置,没有硬性要求 2.配置文件的命名:没有硬性要求,建议:applicationContext.xml 思考:日后应用Spring框架时,需要进行配置文件的路径的设置
2.Spring的核心API
-
ApplicationContext
作用:Spring提供的ApplicationContext这个工厂,用于对象的创建 好处:解耦合-
ApplicationContext接口类型
接口:屏蔽实现二点差异 非web环境:ClassPathXmlApplicationContext (main junit) web环境:XmlWebApplicationContext -
重量级资源
ApplicationContext工厂的对象占用大量内存 不会频繁的创建对象:一个应用只会创建一个工厂对象 ApplicationContext工厂:一定是线程安全的(多线程并发访问)
-
3.程序开发
1.创建类型
2.配置文件的配置 applicationCOntext.xml
<bean id="person" class="com.hyy.basic.Person"></bean>
3.通过工厂类,获得对象
ApplicationContext
|- ClassPathApplicationContext
//创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
//获取对象
Person person = (Person)context.getBean("person");
4.细节分析
-
名词解释
Spring工厂创建的对象,叫做bean或者组件(componet) -
Spring工厂的相关方法
@Test public void test3(){ //创建工厂 ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml"); //根据名字和类型取bean Person person = context.getBean("person",Person.class); System.out.println(person); //配置文件必须只能有一个bean Person person = context.getBean(Person.class); System.out.println(person); //获取Spring工厂配置的所有的bean标签的id值 String[] beanDefinitionNames = context.getBeanDefinitionNames(); for(String st:beanDefinitionNames){ System.out.println(st); } //根据类的类型获取Spring工厂配置的所有的bean标签的id值 String[] beanNamesForType = context.getBeanNamesForType(Person.class); for(String st:beanNamesForType){ System.out.println(st); } //判断是否存在指定id的bean 只能判断id,不能判断name boolean person = context.containsBeanDefinition("person"); //判断是否存在指定id的bean 能判断id和name boolean person = context.containsBean("person"); } -
配置文件中需要注意的细节
1.只配置class属性,没有id值 <bean class="com.hyy.basic.Person"></bean> a) 上述这种配置,Spring给我们创建了id值 com.hyy.basic.Person#0 b) 应用场景:如果这个bean只需要使用一次,就可以省略id值 如果这个bean会使用多次,或者被其他bean引用则需要设置id值 2.name属性 作用:用于在Spring的配置文件中,为bean对象定义别名 相同: 1.context.getBean("id|name") ---> object 区别: 1.别名可以定义多个,id只能由一个值 2.XML的id属性的值,命名要求:必须以字母开头,数字,字母,下划线,连字符 name属性的值,命名没有要求 name属性会应用在特殊命名的场景下:/person
5.Spring工厂的底层实现原理(简易版)
Spring工厂是可以调用对象私有的构造方法创建对象
通过反射创建对象
6.思考
问题:未来在开发过程中,是不是所有的对象,都交给Spring来创建呢?
回答:理论上是,但是有特例 实体对象(entity)是由持久层框架来进行创建的。
第三章 Spring5与日志框架的整合
Spring与日志框架的整合,日志就可以在控制台中,输出Spring框架运行中的一些重要信息
好处:便于了解Spring框架的运行过程,利于程序的调试
-
Spring如何整合日志框架
默认 Spring1.2.3 早前都是commons-logging.jar Spring5.x 默认整合的日志框架 logback log4j2 Spring5.x整合log4j 1.引入log4j.jar包 2.引入log4.properties配置文件-
pom
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> -
log4j.properties
#配置根 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
-
第四章 注入(Injection)
1.什么是注入
通过Spring工厂及配置文件,为所创建对象的成员变量赋值
1.1 为什么需要注入
通过编码的方式,为成员变量进行赋值,存在耦合
/*
* 用于测试属性注入
* */
@Test
public void test6(){
//创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
//获取对象
Person person = (Person) context.getBean("person");
person.setId(1);
person.setName("迷雾神");
System.out.println(person);
}
1.2 如何进行注入[开发步骤]
-
类成员变量提供set/get方法
-
配置Spring的配置文件
<bean id="person" class="com.hyy.basic.Person"> <property name="id" value="10"></property> <property name="name" value="迷雾小神"></property> </bean>
1.3 注入好处
解耦合
2.Spring注入的原理分析(简易版)
Spring通过底层调用对象属性对应的set方法,完成成员变量的赋值,称之为set注入
第五章 Set注入详解
针对于不同类型的成员变量,在<property>标签,需要嵌套其他的标签
<property>
<xxx>
</property>
1.JDK内置类型
1.1 Sting+8钟基本类型
<value>迷雾神</value>
1.2 数组类型
<list>
<value>hu@qq.com.cn</value>
<value>yang@qq.com.cn</value>
<value>li@qq.com.cn</value>
</list>
1.3 set集合
<set>
<value>133111</value> //根据属性字段的泛型类型
<value>133222</value>
<value>133444</value>
</set>
<set>
<ref></ref>
<set></set>
</set>
1.4 list集合
<list>
<value>chengdu</value>
<value>beijing</value>
<value>shanghai</value>
</list>
1.5 Map集合
注意:map --- entry -- key有特定的标签
值根据类型选择对应的标签
<map>
<entry>
<key><value>miwushen</value></key>
<value>1111</value>
</entry>
<entry>
<key><value>miwuxiaoshen</value></key>
<value>2222</value>
</entry>
</map>
1.6 Properites
Properites类型 特殊的Map key=String value=String
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
</props>
1.7 复杂的JDK类型(Date)
需要通过自定义转换器,处理
2.用户自定义类型
2.1 第一种方式
-
为成员变量提供set 方法
-
配置文件中进行注入
<bean id="userService" class="com.hyy.basic.UserServiceImpl"> <property name="userDao" > <bean class="com.hyy.basic.UserDaoImpl"></bean> </property> </bean>
2.2 第二种方式
-
第一种赋值方式存在的问题
1.配置文件代码冗余 2.被注入的对象多次创建,浪费内存资源 -
为成员变量提供set 方法
-
配置文件中进行注入
<bean id="userService" class="com.hyy.basic.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.hyy.basic.UserDaoImpl"></bean>
3.Set注入的简化写法
3.1 基于属性简化
JDK类型注入
<property name="name">
<value>miwushen</value>
</property>
<property name="name" value="miwushen"></property>
3.2 基于p命名空间简化
<bean id ="person" class="com.hyy.basic.Person" p:name="迷雾神" p:id="100"></bean>
//自定义类型
<bean id="userService" class="com.hyy.basic.UserServiceImpl" p:userDao-ref="userDao"></bean>
第六章 构造注入
注入:通过Spring的配置文件,为成员变量赋值
Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值
构造注入:Spring调用构造方法,通过配置文件,为成员变量赋值
1.开发步骤
-
提供有参的构造方法
public class Customer { private String name; private int age; public Customer(String name, int age) { this.name = name; this.age = age; } } -
Spring的配置文件
//constructor-arg的个数和顺序要与给定构造方法的参数一致 <bean id="customer" class="com.hyy.basic.constructor.Customer"> <constructor-arg> <value>迷雾神</value> </constructor-arg> <constructor-arg> <value>18</value> </constructor-arg> </bean>
2.构造方法重载
2.1 参数个数不同时
public Customer(String name) {
this.name = name;
}
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
通过控制<constructor-arg>标签的数量进行区分
2.2 参数个数相同时
public Customer(String name) {
this.name = name;
}
public Customer(int age) {
this.age = age;
}
通过在标签引入 type属性 进行类型的区分<constructor-arg type="int">标签的数量进行区分
3.注入的总结
未来的实战,应用set注入还是构造注入?
答案:set注入更多
1.构造注入麻烦(重置)
2.Spring底层框架底层 大量应用了set注入
第七章 反转控制与依赖注入
1.反转控制(IOC Inverse of Control)
控制:对于成员变量赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中反转到Spring工厂和配置文件中。
好处:解耦合
底层实现:工厂设计模式
2.依赖注入(Dependency Injection DI)
注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值
依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个作为本类的成员变量,
最终通过Spring配置文件进行注入(赋值)
第八章 Spring工厂创建复杂对象
1.什么是复杂对象
复杂对象:指的就是不能直接通过new构造方法创建的对象
Connection
SqlSessionFactory
2.Spring工厂创建复杂对象的3种方式
2.1 FactoryBean接口
-
开发步骤
-
实现FactoryBean接口
-
Spring配置文件的配置
# 如果Class中指定的类型 是FactoryBean接口的实现类,那么通过id值获取的是这个,类所创建的复杂类对象 Connection <bean id ="conn" class="com.hyy.factoryBean.ConnectionFactoryBean"/>
-
-
细节
-
如果就想获取FactoryBean类型的对象 加 “&”
context.getBean("&conn") -
isSingleton方法
返回true只会创建一个对象
返回false每一次都会创建一个对象
问题:根据对象的特点,决定返回true(SqlSessionFactory)还是false(Connection) -
依赖注入的体会(DI)
<bean id ="conn" class="com.hyy.factoryBean.ConnectionFactoryBean"> <property name="drive" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/testspring?serverTimezone=UTC"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean>
-
-
FactoryBean的实现原理(简易版)
接口回调 1.为什么Spring要规定FactoryBean接口实现,并且getObject()? 2.context.getBean("conn")获得是复杂对象Connection 而没有获得ConnectionFactoryBean(&) Spring内部运行流程 1.通过conn获得ConnectionFactoryBean类的对象,通过instanof判断出是FactoryBean接口实现类 2.Spring按照规定 getObject() ----> Connection 3.返回Connection -
FactoryBean总结
Spring中用于创建复杂对象的一种方式,也是Spring原生提供。整合其他框架,大量应用FactoryBean
2.2 实例工厂
1.避免Spring框架的侵入
2.整合遗留系统
-
开发步骤
<bean id ="connFactory" class="com.hyy.factoryBean.ConnectionFactory"></bean> <bean id ="conn" factory-bean="connFactory" factory-method="getConnection">
2.3 静态工厂
-
开发步骤
<bean id ="conn" class="com.hyy.factoryBean.StaticConnectionFactory factory-method="getConnection">
3.Spring工厂创建对象的总结
第九章 控制Spring工厂创建对象的次数
1.如何控制简单对象的创建次数
<bean id="account" class="com.hyy.scope.Account" scope="prototype"></bean>
scope="prototype" :每一次都会创建新的对象
scope="singleton" :只会创建一次对象 (默认)
2.如何控制复杂对象的创建次数
通过FactoryBean的isSingleton()方法来控制
3.为什么要控制对象的创建次数
好处:节省不必要的内存浪费
-
什么样的对象只创建一次
1.SqlSessionFactory 2.DAO 3.Service -
什么样的对象每一次都要创建
1.Connection 2.SqlSession | Session 3.Struts Action
第十章 对象的生命周期
1.什么是对象的生命周期
指的一个对象创建,存活,消亡的一个完整过程
2.为什么要学习对象的生命周期
由Spring负责对象的创建,存活,销毁,了解生命周期,有利于我们使用好Spring创建的对象
3.生命周期的三个阶段
-
创建阶段
Spring工厂何时创建对象-
scope="singleton"
Spring工厂创建的同时,创建对象 注意:获取对象的时候,再创建对象 加入 lazy-init="true" <bean id="product" class="com.hyy.life.Product" lazy-init="true" scope="singleton"/> -
scope="prototype"
Spring工厂在获取对象同时,创建对象 context.getBean("")
-
-
初始化阶段
Spring工厂再创建完对象后,调用对象的初始化方法 1.初始化方法提供:通过需求,用代码提供初始化方法,完成初始化操作 2.初始化方法调用:Spring工厂来进行调用-
InitializingBean接口
public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } -
对象中提供普通方法
public void myInit(){ } //配置文件 <bean id="product" class="com.hyy.life.Product" init-method="myInit"/> -
细节分析
1.如果一个对象即实现了InitializingBean接口,又提供了普通的初始化方法
1.先执行InitializingBean 2.在执行普通初始化方法2.注入一定发生在初始化之前
3.什么叫做初始化操作
1.资源的初始化:数据库 IO 网络...
-
-
销毁阶段
Spring销毁对象前,会调用对象的销毁方法,完成销毁操作 1.Spring什么时候销毁所创建的对象 context.close(); 2.销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作 调用:Spring工厂完成调用-
DisposableBean
public void destroy() throws Exception { System.out.println("Product销毁了"); } -
定义一个普通的销毁方法
public void myDestory(){ } //配置文件 <bean id="product" class="com.hyy.life.Product" destory-method="myDestory"/> -
细节分析
-
销毁方法的操作只适用于scope="singleton"的情况
-
什么叫做销毁操作
主要指的是 资源的释放操作 io.close() connection.close()
-
-
4.总结
第十一章 配置文件参数化
把Sping配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中
1.Spring配置文件中存在需要经常修改的字符串?
存在 以数据库连接相关的参数 代表
2.经常变化的字符串,在Spring的配置中,直接修改,不利于项目维护
3.转移到更小的配置文件中(.properties)中
利于项目维护
1.配置文件参数的开发步骤
-
提供一个小的配置文件(.properties)
名字:随便 放置位置:随便 jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/testspring?serverTimezone=UTC jdbc.userName=root jdbc.password=root -
Spring配置文件与小的配置文件整合
<context:property-placeholder location="classpath:db.properties"/> -
Spring配置文件中通过${key}获取小配置文件对应的值
<context:property-placeholder location="classpath:db.properties"/> <bean id ="conn" class="com.hyy.factoryBean.ConnectionFactoryBean"> <property name="drive" value="${jdbc.driverClassName}"></property> <property name="url" value="${jdbc.url}}"></property> <property name="user" value="${jdbc.userName}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
第十二章 自定义类型转换器
1.类型转换器
作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据
2.自定义类型转换器
原因:当Spring内部没有提供特定类型的转换器,而还需要使用的时候,那么就需要自己定义类型转换器
-
类 implements Converter接口
public class MydateConvert implements Converter<String ,Date> { /* * convert 作用:String ---> Date * param :代表配置文件中的日期的字符串 value="2020-04-01" * return:把转换好的Date作为convert方法的返回值后,Spring自动的为birthday属性进行注入 * */ public Date convert(String s) { Date date=null; try { SimpleDateFormat format =new SimpleDateFormat("yyyy-MM-dd"); date = format.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } } -
Spring的配置文件进行配置
-
MydateConvert对象创建出来
<bean id="myDateCOnvert" class="com.hyy.convert.MydateConvert" /> -
类型转换器的注册
目的:告知Spring框架,我们所创建的MydateConvert是一个类型转换器 <!--用于注册类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateCOnvert"></ref> </set> </property> </bean>
-
3.细节
-
MydateConvert 中的日期的格式,通过依赖注入的方式,由配置文件来完成
public class MydateConvert implements Converter<String ,Date> { private String pattern; /* * convert 作用:String ---> Date * param :代表配置文件中的日期的字符串 value="2020-04-01" * return:把转换好的Date作为convert方法的返回值后,Spring自动的为birthday属性进行注入 * */ public Date convert(String s) { Date date=null; try { SimpleDateFormat format =new SimpleDateFormat(pattern); date = format.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } }<bean id="myDateCOnvert" class="com.hyy.convert.MydateConvert" > <property name="pattern" value="yyyy-MM-dd"></property> </bean> -
ConversionServiceFactoryBean定义id属性时 值必须为:conversionService
-
Spring框架内置了日期类型的转换器
日期格式:2020/05/01 (不支持:2020-05-01)
第十三章 后置处理Bean
BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工
注意:BeanPostProcessor作用接口
工厂创建对象过程
BeanPostProcessor实现原理
我们要实现BeanPostProcessor接口中规定的方法
Object postProcessBeforeInitialization(Object bean, String beanName)
作用:Spring创建完对象后,进入注入后,可以运行Before方法进行加工
获得Spring创建好的对象:通过方法的参数
最终通过返回值交给Spring框架
Object postProcessAfterInitialization(Object bean, String beanName)
作用:Spring执行完对象的初始化操作后,可以运行After方法进行加工
获得Spring创建好的对象:通过方法的参数
最终返回值交给Spring框架
实战中:
很少处理初始化操作:没必要区分Before After。只需实现其中一个方法就可以
-
BeanPostProcessor开发步骤
-
类实现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.setName("迷雾小神"); return bean; } } -
Spring配置文件中进行配置
<bean id="myBeanPostProcessor" class="com.hyy.beanpost.MyBeanPostProcessor"/> -
BeanPostProcessor细节
BeanPostProcessor会对Spring工厂中所有创建的对象进行加工
-