目录
1、IOC容器
2、IOC接口
3、IOC 操作 Bean 管理
4、基于 XML 方式
5、Bean 作用域和生命周期
6、注解方式
一、IOC 容器
IOC 的概念原理
IOC 是什么?
-
控制反转,把对象的创建和对象之间的调用过程,都交给 Spring 进行管理
-
使用 IOC 的目的:降低耦合性
IOC 底层实现
-
xml 解析
-
工厂模式
-
反射技术
二、IOC 接口
1)IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
2)Spring 提供了IOC 容器实现的两种方式(即两个接口)
-
BeanFactory
-
IOC 容器基本实现
-
加载配置文件时不会创建对象,使用对象时才会创建对象
-
-
ApplicationContext
-
BeanFactory
的子接口,提供更多功能,提供给开发人员使用 -
加载配置文件就创建对象
-
BeanFactory
BeanFactory
接口的子接口和实现类
ConfigurableApplicationContext
:包含一些相关的扩展功能
ApplicationContext
ApplicationContext
接口的实现类
FileSystemXmlApplicationContext
:xml 配置文件在系统盘中的文件全路径名new FileSystemXmlApplicationContext("D:\workspace\NOTE_Spring\src\bean1.xml");
ClassPathXmlApplicationContext
:xml 配置文件在项目 src 下的相对路径名new ClassPathXmlApplicationContext("bean1.xml");
三、IOC 操作 Bean 管理
Bean 管理是什么
Bean 管理指的是两个操作
-
Spring 创建对象
-
Spring 注入属性
public class User{
private String userName;
public void setUserName(String userName){
this.userName = userName;
}
}
Bean 管理实现方式
-
基于 XML 配置文件方式实现
-
基于注解方式实现
四、基于 XML 方式
创建对象
<!--配置User对象--> <bean id="user" class="com.zking.spring.User"></bean>
1)在 Spring 配置文件中,使用bean
标签,标签里添加对应属性,就可以实现对象的创建
2)bean
标签中有很多属性,介绍常用的属性
id
属性:唯一标识
class
属性:类全限定名、类全路径
name
属性:了解即可,早期为Struts
框架服务,现已“退役”,作用跟id
属性一样其他属性:后面再做介绍...
3)创建对象时,默认执行无参构造方法
如果只提供一个有参构造方法,如下
public class User {
private String userName;
public User(String userName) {
this.userName = userName;
}
// ...
}
仍然按照之前获取 User 对象创建方式,即
// 1、加载自定义的Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 2、获取配置的User对象
User user = context.getBean("user", User.class);
则会报错,其中主要报错信息
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in class path resource [bean1.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zking.spring.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.zking.spring.User.<init>()
就是说:初始化 User 对象失败,因为没有找到默认构造,所以抛出了一个NoSuchMethodException
异常,即 User 中没有<init>()
方法
注入属性
DI
:依赖注入,就是注入属性(但需要在创建对象基础上进行)
IOC
和DI
的区别:DI
是IOC
的一种具体实现
两种注入方式:Setter 方式、有参构造方法
-
第一种注入方式:通过 Setter 方式进行注入
public class Book{ private String bname; // Setter 方法注入 public void setBname(String bname){ this.bname = bname; } public static void main(String[] args){ Book book = new Book(); book.setBname("book1"); } }
-
第二种注入方式:通过有参构造方法进行注入
public class Book{ private String bname; // 有参构造注入 public Book(String bname){ this.bname = bname; } public static void main(String[] args){ Book book = new Book("book1"); } }
通过 Setter 方式注入
① 创建类,定义属性和 Setter 方法
public class Book {
private String bname;
private String bauthor;
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
② 在 Spring 配置文件中配置对象创建,配置属性注入
<!-- 2、Setter方法注入属性 -->
<bean id="book" class="com.zking.spring.Book">
<!-- 使用property完成属性注入
name: 类中属性名称
value: 向属性中注入的值
-->
<property name="bname" value="Spring实战 第5版"></property>
<property name="bauthor" value="克雷格·沃斯(Craig Walls)"></property>
</bean>
通过有参构造注入
① 创建类,定义属性,创建属性对应有参构造方法
public class Orders {
private String oname;
private String address;
public Orders(String oname, String address) {
this.oname = oname;
this.address = address;
}
}
② 在 Spring 配置文件中配置对象创建,配置有参构造注入
<!-- 3、有参构造注入属性 -->
<bean id="orders" class="com.zking.spring.Orders">
<constructor-arg name="oname" value="Spring微服务实战"></constructor-arg>
<constructor-arg name="address" value="[美]约翰·卡内尔(John Carnell)"></constructor-arg>
</bean>
或者使用index
属性代替name
属性,索引值大小是几就表示有参构造中的第几个参数(索引从0开始)
<!-- 3、有参构造注入属性 -->
<bean id="orders" class="com.zking.spring.Orders">
<constructor-arg index="0" value="冰墩墩"></constructor-arg>
<constructor-arg index="1" value="Peking"></constructor-arg>
</bean>
注入其他类型属性
字面量
-
null
值:使用<null>
<bean id="book2" class="com.zking.spring.Book">
<property name="bname" value="Spring实战 第5版"></property>
<property name="bauthor">
<null></null>
</property>
</bean>
-
属性值包含特殊符号:有两种方式
-
使用转义字符,如
<>
标识<>
-
<!-- 字面量:property方式注入含有特殊符号的属性 -->
<bean id="book3" class="com.zking.spring.Book">
<property name="bname" value="Spring实战 第5版"></property>
<property name="bauthor" value="<Test>"</property>
</bean>
-
使用
CDATA
结构,如<![CDATA[<Test>]]>
<!-- 字面量:property方式注入含有特殊符号的属性 -->
<bean id="book4" class="com.zking.spring.Book">
<property name="bname" value="Spring实战 第5版"></property>
<property name="bauthor">
<value><![CDATA[<Test>]]></value>
</property>
</bean>
外部 Bean
① 创建两个类:Service
类和Dao
类
public interface UserDao {
void update();
}
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update...");
}
}
② 在Service
中调用Dao
中的方法
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void updateUser(){
System.out.println("service update...");
userDao.update();
}
}
③ 在 Spring 配置文件中进行配置
<!-- 1、配置service和dao创建 -->
<bean id="userService" class="com.zking.spring.service.UserService">
<!-- 2、注入userDao对象
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.zking.spring.dao.UserDaoImpl"></bean>
内部 Bean
① 一对多关系:部门和员工。部门里有多个员工,一个员工属于一个部门。部门是一的一方,员工是多的一方
② 在实体类之间表示一对多关系。在员工类中使用对象类型表示所属部门
public class Dept {
private String dname;
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
}
public class Emp {
private String ename;
private String gender;
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
③ 在 Spring 配置文件中进行配置
<bean id="emp1" class="com.zking.spring.bean.Emp">
<property name="ename" value="Rose"></property>
<property name="gender" value="female"></property>
<property name="dept">
<bean id="dept1" class="com.zking.spring.bean.Dept">
<property name="dname" value="Develop Department"></property>
</bean>
</property>
</bean>
当希望一个bean只被某一个类使用时,我们可以使用内部bean。内部bean此时作为某一个类的属性,只能通过该类实例化。
注入集合属性
-
注入数组类型属性
-
注入 List 集合类型属性
-
注入 Map 集合类型属性
自动装配
自动装配:根据指定装配规则(属性名称或属性类型),Spring 自动将匹配的属性值进行注入
XML 实现自动装配:
bean
标签中有个属性autowire
,里面有两个常用的属性值
byName
根据属性名称注入,要求注入值bean
的id
值和类中属性名称一致
byType
根据属性类型注入,要求注入值bean
的类型在配置文件中只存在一份
1)根据属性名称进行自动注入
<bean id="emp" class="com.zking.spring.autowire.Emp" autowire="byName"></bean> <bean id="dept" class="com.zking.spring.autowire.Dept"></bean>
2)根据属性类型进行自动注入
<bean id="emp" class="com.zking.spring.autowire.Emp" autowire="byType"></bean> <bean id="dept" class="com.zking.spring.autowire.Dept"></bean>
外部属性文件
1、直接配置数据库信息
-
引入
Druid
连接池依赖jar
包 -
配置
Druid
连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
2、引入外部属性文件配置数据库连接池
-
1)创建
properties
格式的外部属性文件,配置数据库连接信息
mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306
mysql.username=root
mysql.password=root
-
2)将外部
properties
属性文件引入到 Spring 配置文件中
<!--引入context名称空间-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--使用Spring表达式配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysql.driverClassName}"></property>
<property name="url" value="${mysql.url}"></property>
<property name="username" value="${mysql.username}"></property>
<property name="password" value="${mysql.password}"></property>
</bean>
</beans>
FactoryBean
Spring 有两种类型 Bean,一种是普通 Bean,另外一种是工厂 Bean(FactoryBean)
普通 Bean:在配置文件中定义的 Bean 类型就是返回类型
工厂 Bean:在配置文件中定义的 Bean 类型可以和返回类型不一致
上述的例子都是普通 Bean 的类型,那么工厂 Bean 该怎么实现呢?
1)创建类,实现 FactoryBean 接口,使其作为一个工厂 Bean
2)实现接口中的方法,在实现方法中定义返回的 Bean 类型
3)在 Spring 配置文件中进行配置
五、Bean 作用域和生命周期
Bean 作用域
在 Spring 里面,可以设置创建 Bean 的实例是单实例还是多实例,默认情况下是单实例
<bean id="book" class="com.zking.spring.collectiontype.Book"></bean>
如何设置单实例多实例?
在 Spring 配置文件中 bean 标签里scope
属性用于设置单实例还是多实例
1)
singleton
,单实例,默认情况下不写也是它2)
prototype
,多实例
singleton
和prototype
的区别
singleton
和prototype
除了单实例和多实例的差别之外,还有以下区别
设置
scope
值是singleton
时,加载 Spring 配置文件时就会创建单实例对象设置
scope
值是prototype
时,加载 Spring 配置文件时不会创建对象,而是在调用getBean
方法时创建多实例对象
scope
的其他值
scope
的属性值除了singleton
和prototype
之外,其实还有一些属性值,如
request
,每个request
创建一个新的 bean
session
,同一session
中的 bean 是一样的
Bean 生命周期
生命周期:从对象创建到对象销毁的过程
Bean 生命周期
通过构造器创建 Bean 实例(无参构造)
为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)
调用 Bean 的初始化方法(需要进行配置初始化方法)
Bean 就可以使用了(对象获取到了)
当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)
Spring 配置文件中的配置
<bean id="orders" class="com.zking.spring.bean.Orders" init-method="initMethod"
destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
Bean 后置处理器
加上 Bean 后置处理器,Bean 生命周期如下
通过构造器创建 Bean 实例(无参构造)
为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)
把 Bean 的实例传递给 Bean 后置处理器的
postProcessBeforeInitialization
方法调用 Bean 的初始化方法(需要进行配置初始化方法)
把 Bean 的实例传递给 Bean 后置处理器的
postProcessAfterInitialization
方法Bean 就可以使用了(对象获取到了)
当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)
六、注解方式
什么是注解
-
注解是一种代码特殊标记,格式:
@注解名称(属性名称=属性值,属性名称=属性值...)
-
注解作用:在类上面,方法上面,属性上面
-
注解目的:简化 XML 配置
创建对象
-
@Component
-
@Service
-
@Controller
-
@Repository
上面四个注解功能是一样的,都可以用来创建 Bean 实例
-
1)引入依赖
spring-aop.xxx.jar
-
2)开启组件扫描
<?xml version="1.0" encoding="UTF-8"?> <!--引入context名称空间--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启组件扫描:多个包用逗号隔开--> <context:component-scan base-package="com.zking.spring.dao,com.zking.spring.service"></context:component-scan> </beans>
-
3)创建类,在类上添加创建对象注解
/** * value可省略,默认值为类名首字母小写 */ @Component(value = "userService") public class UserService { public void add(){ System.out.println("UserService add..."); } }
组件扫描配置
设置扫描
-
use-default-filters
表示现在不使用默认filter
,自己配置filter
-
include-filter
设置扫描哪些内容
<context:component-scan base-package="com.zking.spring" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
设置不扫描
-
配置扫描包下所有内容
-
exclude-filter
设置不扫描哪些内容
<context:component-scan base-package="com.zking.spring"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
属性注入
-
@Autowired
根据属性类型进行自动装配 -
@Qualifier
根据属性名称进行注入,需要和@Autowired
一起使用 -
@Resource
可以根据类型和名称注入 -
@Value
根据普通类型注入
完全注解开发
-
创建配置类,替代 XML 配置文件
//标注当前类为配置类
@Configuration
//开启组件扫描
@ComponentScan({"com.zking.spring"})
//引入外部属性文件
@PropertySource({"classpath:config.properties"})
public class SpringConfig {
}