一、Spring简介
1.Spring是什么?
Spring是一个开源的容器框架,其中有两个重要的特性分别为
- 控制反转(IoC)
- 面向切面(AOP)
2.为什么使用Spring
- 可以降低组件之间的耦合度,实现各层次间的解耦
Controller-->Service-->Dao - 让代码的结构变得更好
面向接口编程
高低原则:高内聚,低耦合
开闭原则:对扩展开放、对修改关闭 - 提供了许多支持
提供了辅助类,如:JdbcTemplate、HibernateTemplate、StringUtils、CollectionUtils、StreamUtils等
提供了各种服务,如事务管理服务、消息服务等
提供了单例模式
提供了AOP技术 - 对主流框架提供了集成支持
集成MyBatis、Hibernate、JPA、Struts等
3.Spring体系结构
Spring体系结构图二、核心概念
1.IoC
- 控制反转(Inversion of Control)
public class UserServiceImpl{
//User由Service创建并维护
private UserDao userDao = new UserDaoImpl();
public void regist(User user){
userDao.save(user);
}
}
是指本身不负责依赖对象的创建及维护,把依赖对象的创建和维护交给外部容器来做,这样控制权就发生了转移,控制权转移就是控制反转。
外部容器/IoC容器:存放对象(bean)的容器。
2.DI
- 依赖注入(dependency injection)
public class UserServiceImpl{
//User由Service创建并维护
private UserDao userDao;
//让容器将创建好的对象注入到Service中
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void regist(User user){
userDao.save(user);
}
}
是指在运行期,由外部容器动态的将对象注入到组件中
三、第一个Spring程序
1.添加jar包
添加spring-core、spring-beans、spring-context、spring-expresion
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
</dependencies>
2.核心配置文件
用来进行bean的配置,文件名字自定义,一般默认为applicationContext.xml
创建Spring配置文件
2.1 在applicationContext.xml中配置bean
<!--定义一个bean-->
<bean id="helloSpring" class="ioc01.HelloSpring"></bean>
2.2 在类中加载配置文件并获取bean然后调用方法
public class Test {
public static void main(String[] args) {
//传统的调用方式
// new HelloSpring().show();
//使用Spring方式调用
//1.获取IoC容器,读取配置文件初始化Spring上下文
ApplicationContext ac = new ClassPathXmlApplicationContext("/ioc01/applicationContext.xml");
//2.根据id从容器中获取bean
HelloSpring helloSpring = (HelloSpring) ac.getBean("helloSpring");
//3.调用方法
helloSpring.show();
}
}
运行效果图
问题:
调用方法虽然成功,但是并没有给name属性进行赋值
解决:
使用DI进行注入
<!--定义一个bean-->
<bean id="helloSpring" class="ioc01.HelloSpring">
<property name="name" value="Tom"/>
</bean>
运行效果图
注意:
- 使用DI时一定要为私有属性生成set方法
-
property
标签中的name名一定要与生成的set方法的名字相同(首字母要小写)
四、IoC容器的类型
- applicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
使用方法:
//ApplicationContext
//类路径下加载
ApplicationContext ac = new ClassPathXmlApplicationContext("ioc03/applicationContext.xml");
//文件系统加载
ApplicationContext ac1 = new FileSystemXmlApplicationContext("D:/applicationContext.xml");
//获取bean
SpringBean springBean = (SpringBean) ac.getBean("springBean");
//输出
System.out.println(springBean);
- BeanFactory
使用方法:
//BeanFactory
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("ioc03/applicationContext.xml"));
BeanFactory bf1 = new XmlBeanFactory(new FileSystemResource("D:/applicationContext.xml"));
SpringBean springBean1 = (SpringBean) bf.getBean("springBean");
System.out.println(springBean1);
五、数据的装配
1.什么是数据的装配
为bean中的属性注入值,被称为数据装配,可以装配不同类型的值
2.简单类型(共19种)--->使用value进行装配
八种基本数据类型及包装类
byte short int long float double char boolean
Byte Short Integer Long Float Double Character Boolean
String Class Resource
3.其他bean的引用--->使用ref进行装配
<bean id="otherBean" class="ioc05.OtherBean">
<property name="name" value="alice"/>
</bean>
<bean id="springBean" class="ioc05.SpringBean">
<property name="otherBean" ref="otherBean"/>
</bean>
3.1 集合类型
3.1.1 数组
<!--为数组装配-->
<property name="arrs">
<array>
<value>1</value>
<value>2</value>
<value>5</value>
</array>
</property>
3.1.2 List
<!--为list集合装配-->
<property name="list">
<list>
<!--引用外部定义的bean-->
<ref bean="otherBean"/>
<ref bean="otherBean"/>
<!--现场装配bean-->
<bean class="ioc05.OtherBean">
<property name="name" value="jack"/>
</bean>
<bean class="ioc05.OtherBean">
<property name="name" value="mary"/>
</bean>
</list>
3.1.3 Set
<property name="set">
<set>
<!--引用外部定义的bean-->
<ref bean="otherBean"/>
<ref bean="otherBean"/>
<!--现场装配bean-->
<bean class="ioc05.OtherBean">
<property name="name" value="jack"/>
</bean>
<bean class="ioc05.OtherBean">
<property name="name" value="mary"/>
</bean>
</set>
</property>
3.1.4 Map
<property name="map">
<map>
<entry key-ref="otherBean" value="java.lang.String"/>
</map>
</property>
3.1.5 properties
<property name="properties">
<props>
<prop key="username">zhangsan</prop>
<prop key="password">123456</prop>
</props>
</property>
3.1.6 null类型
<property name="name">
<null/>
</property>
六、bean的生命周期
1.生命周期各阶段
代码块-->构造方法-->set方法-->就绪-->使用-->从容器中销毁
- 问题:在测试类中获取bean的时候希望把name可以转换成大写。
- 解决:在set方法中对name进行大写转换。
public void setName(String name) {
this.name = name.toUpperCase();
}
2.初始化方法/销毁方法
2.1 初始化方法学习
- 问题:在测试类中调用getName方法时希望得到
name_sex
的格式 - 解决:需要在数据装配完成和就绪状态之间添加一个方法。所以在SpringBean类中创建方法,将当前的名字进行转换并追加性别,然后在Spring配置文件中添加init-method属性并指定方法名。
代码块-->构造方法-->set方法-->初始化方法-->就绪-->使用-->从容器中销毁
//初始化方法
public void init(){
this.name = this.name.toUpperCase()+"_"+this.sex.toUpperCase();
}
<bean id="springBean" class="ioc07.SpringBean" init-method="init">
<property name="name" value="alice"/>
<property name="sex" value="female"/>
</bean>
2.2 销毁方法的学习
代码块-->构造方法-->set方法-->就绪-->使用-->销毁方法-->从容器中销毁
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("ioc07/applicationContext.xml");
SpringBean springBean = (SpringBean) ac.getBean("springBean");
System.out.println(springBean.getName());
// 销毁容器
// ac.destroy();
// 代码执行完后,销毁容器
ac.registerShutdownHook();
七、实例化bean的方式
可以通过多种方式创建对象:
- 构造方法,无参、有参
- 静态工厂,无参、有参(静态方法)
- 实例工厂,无参、有参(非静态方法)
1.构造方法
<!--通过无参构造方法实例化bean-->
<bean id="springBean" class="ioc09.SpringBean">
<property name="uname" value="tom"/>
<property name="pwd" value="123"/>
<property name="age" value="20"/>
</bean>
<!--调用带参的构造方法-->
<bean id="springBean2" class="ioc09.SpringBean">
<constructor-arg name="uname" value="alice"/>
<constructor-arg name="pwd" value="123"/>
<constructor-arg name="age" value="18"/>
</bean>
2.静态工厂
<!--通过静态工厂实例化bean-->
<!--
使用factory-method属性可以不调用SpringBeanFactory的构造方法,
而是去调用SpringBeanFactory中的getSpringBean方法
-->
<!--无参-->
<bean id="springBean" class="ioc10.SpringBeanFactory" factory-method="getSpringBean">
<property name="name" value="tom"/>
</bean>
<!--带参-->
<bean id="springBean2" class="ioc10.SpringBeanFactory" factory-method="getSpringBean">
<constructor-arg name="name" value="alice"/>
</bean>
3.实例工厂
<!--实例工厂-->
<bean id="springBeanFactory" class="ioc12.SpringBeanFactory"></bean>
<!--无参-->
<bean id="springBean" factory-bean="springBeanFactory" factory-method="getSpringBean">
<property name="name" value="tom"/>
</bean>
<!--带参-->
<bean id="springBean2" factory-bean="springBeanFactory" factory-method="getSpringBean">
<constructor-arg name="name" value="alice"/>
</bean>
注意:实例化带参工厂的时候,需要使用setXxx("")
的方式进行赋值
八、实例化bean的时机
1.ApplicationContext
在加载Spring配置文件时对bean初始化
优点:调用getBean方法时已经初始化完成,可以直接使用比较方便
缺点:因为没有对这个bean进行操作就对bean进行了实例化,所以造成了内存的浪费。
注意:也可以在bean标签中使用laze-init=true
设置为调用方法时才初始化。
2.BeanFactory
在调用getBean方法时对bean进行初始化
优点:在调用getBean方法时才进行实例化,可以节约内存。
缺点:在调用getBean方法时才去实例化,比较费时,不方便。
九、bean的作用域
在IoC容器中同一个类默认只有一个bean,也就是单例的
问题:多线程访问时,单例模式下是不安全的。
解决1:可以在bean标签中将scope
设置为prototype
<bean id="springBean" class="ioc15.SpringBean" scope="prototype">
<property name="name" value="alice"/>
</bean>
解决2:可以将属性设置为局部变量
public String getName(String name) {
return name;
}
- 注意:在单例模式下,Spring配置文件加载完成就会对bean进行实例化,但是在非单例模式下,只有调用bean的时候才会进行实例化。
十、继承配置
-
简介
用来简化代码,减少配置 -
用法
- 用法1:不同的子bean类继承自父bean
<!--用法1-->
<bean id="parent" abstract="true">
<property name="pwd" value="123"/>
</bean>
<bean id="springBean" class="ioc16.SpringBean" parent="parent">
<property name="uname" value="admin"/>
<property name="age" value="20"/>
</bean>
<bean id="otherBean" class="ioc16.OtherBean" parent="parent">
<property name="uname" value="alice"/>
<property name="sex" value="female"/>
</bean>
- 用法2:相同的子bean类继承自父bean
<!--用法2-->
<bean id="parent2" class="ioc16.SpringBean" abstract="true">
<property name="pwd" value="000"/>
</bean>
<bean id="bean1" parent="parent2">
<property name="uname" value="zhangsan"/>
<property name="age" value="20"/>
</bean>
<bean id="bean2" parent="parent2">
<property name="uname" value="lisi"/>
<property name="age" value="21"/>
</bean>
十一、自动装配
1.简介
IoC容器可以根据bean的名称、类型、或构造方法自动进行注入,称为自动装配。
只适用于其他bean的引用
2.配置
通过bean标签中的autowire属性进行注入,默认情况下为default
- 1.default:不进行自动装配,和no是同样的作用
- 2.byName:根据属性名进行自动装配(查找与属性名相同的bean)
- 3.byType(推荐):根据属性的类型进行自动装配(查找与属性类型相同的bean)
- 4.constructor:根据构造方法自动装配(先按byName方式进行装配,如果没有查找到对应的属性名则按照byType方式进行装配)
注意:此时可以不要setXxx('')
方法
<!--自动注入-->
<bean id="otherBean" class="ioc17.OtherBean">
<property name="name" value="alice"/>
</bean>
<bean id="springBean" class="ioc17.SpringBean" autowire="byName">
<!--<property name="otherBean" ref="otherBean"/>-->
</bean>
十二、在bean中获取IoC容器
定义一个IoC容器的工具类
步骤:
- 1.获取IoC容器
- 2.在Spring配置文件中配置ApplicationContextHolder
- 3.在ApplicationContextHolder中创建获取bean的方法
- 4.在工具bean中通过ApplicationContextHolder获取bean并获取属性
- 5.在测试类中调用工具bean
十三、FactoryBean
1.简介
Spring中有两种类型的bean
- 普通bean,返回的是bean本身的对象。
- 工厂bean,即FactoryBean
应用场景:如果普通的bean的配置比较复杂,在配置文件中定义时步骤比较多,此时可以使用FactoryBean
2.定义FactoryBean
步骤:
- 1.定义一个类,实现FactoryBean接口
- 2.将该bean添加到IoC容器中
- 3.获取到FactoryBean的对象
注意:该Bean返回的并不是FactoryBean类型的对象,而是getObject方法的返回值类型的对象 - 实现
/**
* 该FactoryBean是用来生成PreparedStatement的
*/
public class PreparedStatementFactoryBean implements FactoryBean<PreparedStatement>{
//生成实例的过程
@Override
public PreparedStatement getObject() throws Exception {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/project", "root", "root");
//获取发送器
PreparedStatement ps = conn.prepareStatement("select * from t_user");
return ps;
}
//生成实例的类型
@Override
public Class<?> getObjectType() {
return PreparedStatement.class;
}
//生成的实例是否为单例
@Override
public boolean isSingleton() {
return true;
}
}
十四、Resource类
1.简介
本质就是java.io.File的封装
根据资源位置的不同,提供了不同的实现类,用来快速获取文件资源
- FileSystemResource
- ClassPathResource
- UrlResource
- InputResource
2.基本用法
ApplicationContext ac = new ClassPathXmlApplicationContext("ioc21/applicationContext.xml");
Resource resource = new FileSystemResource("D:/a.txt");
//Resource resource = new ClassPathResource("ioc21/a.txt");
System.out.println(resource.getFilename());
System.out.println(resource.contentLength());
System.out.println(resource.exists());
//获取输入流
InputStream inputStream = resource.getInputStream();
//使用工具类进行文件的复制
StreamUtils.copy(inputStream,new FileOutputStream("D:/b.txt"));
装配Resource
<bean id="springBean" class="ioc21.SpringBean">
<property name="resource" value="ioc21/a.txt"/>
</bean>
十五、后(置)处理器
1.两种后处理器
- Bean后处理器,实现BeanPostProcessor接口。
- BeanFactory后处理器,实现BeanFactoryPostProcessor接口,也称之为容器后处理器。
2.BeanPostProcessor
2.1 简介
Bean后处理器用来对bean的功能进行增强和扩展,对IoC容器中的所有bean都有效。
时机: 执行初始化方法之前和之后
bean的生命周期:
代码块-->构造方法-->set方法-->初始化之前-->初始化方法-->初始化之后-->就绪-->使用-->从容器中销毁
2.2 实现步骤
- 定义一个类,实现BeanPostProcessor接口
- 将该后处理器添加到IoC容器中
3.BeanFactoryPostProcessor
3.1 简介
容器后处理器在bean创建之前,修改bean的定义属性
bean的生命周期:
BeanFactoryPostProcessor-->代码块-->构造方法-->set方法-->初始化之前-->初始化方法-->初始化之后-->就绪-->使用-->从容器中销毁
3.2 实现步骤
- 定义一个类,实现BeanFactoryPostProcessor
- 将容器后处理器添加到IoC容器中
- 定义属性编辑器PropertyEditor(转换器),实现PropertyEditor接口或继承PropertyEditorSupport父类
- 在容器后处理器中注册属性编辑器
网友评论