Spring IOC容器
IOC底层原理
什么是IoC
控制反转(Inversion of Control 缩写为IoC), 把对象的创建和对象之间的调用过程都交给Spring管理, 降低耦合度
IoC的底层原理
- xml解析
- 工厂模式
- 反射(类的字节码)
传统的项目中, 在Service中调用Dao, 如下
class UserService {
execute() {
UserDao dao = new UserDao();
dao.add();
}
}
class UserDao{
add() {
....
}
}
这样直接实例化UserDao并调用, 如果Dao中的方法修改了, Service中的代码也需要修改, 耦合度太高了
通过使用工厂模式来解耦合, 如下
class UserDao {
add() { ... }
}
class UserFactory{
public static UserDao getDao() {
return new UserDao();
}
}
class UserService{
execute() {
UserDao dao = UserFactory.getDao();
dao.add();
}
}
虽然将Service与Dao之间利用一个工厂降低耦合度, 但是工厂与Service和Dao之间还有耦合, 还可以进一步降低耦合度, 如下
第一步在xml配置文件中配置创建的对象
<bean id="dao" class="com.demo.UserDao"/>
第二步, 有service类和dao类, 创建工厂类
class UserFactory {
public static UserDao getDao() {
// xml解析
String classValue = class属性值;
// 通过反射创建对象
Class clazz = Class.forName(classValue);
return (UserDao)clazz.newInstance();
}
}
IOC接口(BeanFactory)
IoC思想基于IoC容器完成, IoC容器底层就是对象工厂, Spring对IoC的容器提供了两种实现方式
- BeanFactory
- IOC容器基本实现, 是Spring内部的使用接口, 不提供开发人员进行使用
- 加载配置文件时不会创建对象, 在获取对象或使用对象时才会创建对象
- ApplicationContext
- BeanFactory接口的子接口, 提供了更多更强大的功能, 一般由开发人员进行使用
- 加载配置文件时就会创建对象
ApplicationContext有提供两个实现类
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext

Bean管理
Bean管理指的是两个操作
- Spring创建对象
- Spring注入属性
并且IoC操作Bean管理有两种方式
- 基于xml方式
- 基于注解的方式
IOC操作Bean管理(基于xml)
- 创建对象
在创建对象的时候, Spring默认执行无参构造方法完成对象创建<bean id="user" class="com.demo.spring5.User" /> - 注入属性
方式一: 使用setter方法注入
方式二: 使用有参构造方法注入
public class Orders {
private String oName;
private String address;
public Orders(String oName, String address) {
this.oName = oName;
this.address = address;
}
}
<bean id="orders" class="com.demo.spring5.Orders">
<constructor-arg name="oName" value="abc" />
<constructor-arg name="address" value="China" />
</bean>
方式三: 使用xml配置文件注入
<bean id="book" class"com.demo.Book">
<property name="bname" value="三国" />
</bean>
bean标签属性
- id: 创建对象的唯一标识符(不能有特殊符号)
- class: 创建对象的类的全路径
- name: 唯一标识符(可以有特殊符号)
DI
依赖注入, 就是注入属性.
面试题: DI与IOC的关系
DI是IOC中的一种具体实现, 表示依赖注入或注入属性, 但依赖注入需要在创建对象的基础之上进行完成
xml注入其他类型属性
- null值
<property name="address"> <null /> </property> - 包含特殊符号
- 转义
- 将特殊符号内容写到CDATA
<property name="address"> <value><![CDATA[<<南京>>]]></value> </property>
注入属性-外部Bean
创建两个类Service和Dao类, 在Service类中调用Dao里面的方法
public interface UserDao {
void update();
}
public class UserDaoImpl implements UserDao {
@Override
public void update() {
System.out.println("dao update");
}
}
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add");
}
}
在spring配置文件中进行配置
<bean id="userService" class="com.demo.spring5.service.UserService">
<!--
注入userDao对象
name属性: 类里面属性名称
ref属性: 创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"/>
</bean>
<bean id="userDaoImpl" class="com.demo.spring5.dao.UserDaoImpl"></bean>
注入属性-内部Bean和级联赋值
- 在实体类中表示一对多关系(部门和员工)
内部Bean写法
<bean id="emp" class="com.demo.spring5.bean.Emp">
<property name="ename" value="tom"/>
<property name="gender" value="女"/>
<property name="dept">
<bean id="dept" class="com.demo.spring5.bean.Dept">
<property name="dname" value="安保部"/>
</bean>
</property>
</bean>
级联赋值
写法一
<bean id="emp" class="com.demo.spring5.bean.Emp">
<property name="ename" value="tom"/>
<property name="gender" value="女"/>
<property name="dept" ref="dept"/>
</bean>
<bean id="dept" class="com.demo.spring5.bean.Dept">
<property name="dname" value="财务部" />
</bean>
写法二
<bean id="emp" class="com.demo.spring5.bean.Emp">
<property name="ename" value="tom"/>
<property name="gender" value="女"/>
<!--需要生成getter方法-->
<property name="dept.dname" value="研发部"/>
</bean>
xml注入集合属性
public class Stu {
// 数组
private String[] courses;
// 集合
private List<String> list;
// Map
private Map<String, String> map;
// Set
private Set<String> set;
}
- 注入数组
- 注入List
- 注入Map
- 注入Set
<bean id="stu" class="com.demo.spring5.collection.Stu">
<property name="courses">
<array>
<value>java</value>
<value>c++</value>
<value>python</value>
</array>
</property>
<property name="list">
<array>
<value>张三</value>
<value>李四</value>
</array>
</property>
<property name="map">
<map>
<entry key="Java" value="java"/>
<entry key="C++" value="c++"/>
</map>
</property>
<property name="set">
<set>
<value>MYSQL</value>
<value>REDIS</value>
</set>
</property>
</bean>
注入对象集合
public class Stu {
private List<Course> courseList;
}
public class Course {
private String cname;
}
<bean id="stu" class="com.demo.spring5.collection.Stu">
<property name="courseList">
<list>
<ref bean="course1"/>
<ref bean="course2"/>
</list>
</property>
</bean>
<!--创建多个course对象-->
<bean id="course1" class="com.demo.spring5.collection.Course">
<property name="cname" value="Spring"/>
</bean>
<bean id="course2" class="com.demo.spring5.collection.Course">
<property name="cname" value="SpringMVC"/>
</bean>
提取list集合
使用util命名空间
<util id="bookList">
<value>aaa</value>
<value>bbb</value>
</util>
<bean id="book" class="com.demo.spring5.collection.Book">
<property name="list" ref="bookList"/>
</bean>
Bean工厂
在spring中有两种类型的bean, 一种是普通bean, 另一种是工厂bean
普通bean在配置文件中定义bean类型就是返回类型
工厂bean在配置文件中定义bean类型可以和返回类型不一样
实现一个工厂bean, 如下
定义一个MyBean类, 返回Course类型
public class MyBean implements FactoryBean<Course> {
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
}
<bean id="myBean" class="com.demo.spring5.collection.MyBean"/>
- 创建类, 让这个类作为工厂bean, 实现接口FactoryBean
- 实现接口里的方法, 在实现的方法中定义返回的bean类型
Bean的作用域
在spring中可以设置bean是单实例还是多实例. 但spring中,在默认情况下, bean是单实例对象
设置Bean多实例
在spring配置文件中bean标签里有属性(scope)用于设置bean为单实例还是多实例
scope属性值
- singleton(默认), 表示单实例对象
- prototype, 表示多实例对象
singleton与prototype区别
singleton为单实例, prototype为多实例;
设置scope的值为singleton时, 加载spring配置文件时就会创建单实例对象;
而设置scope的值为prototype时, 在调用getBean方法时才会创建多实例对象
Bean的生命周期
- 通过构造器创建bean实例(默认无参构造)
- 为bean的属性设置值和对其他bean的引用(调用setter方法)
- 调用bean的初始化方法(需要配置初始化的方法)
- bean能够被使用了(可以获取到)
- 当容器关闭时, 调用bean的销毁方法(需要配置销毁的方法)
Bean的xml自动装配
自动装配
根据指定装配规则(属性名称或者属性类型), spring自动将匹配的属性值进行注入
举例
<!--
byName: 根据属性名称注入, 注入值bean的id和类属性名称一样
byType: 根据属性类型注入
-->
<bean id="emp" class="com.demo.spring5.collection.Emp" autowire="byName">
</bean>
<bean id="dept" class="com.demo.spring5.collection.Dept"></bean>
Bean外部属性文件
配置数据库信息
配置德鲁伊(Druid)连接池, 引入jar包, 下载地址
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/userDB"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
引入外部属性文件配置数据库连接池
新建一个properties类型的文件
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDB
prop.username=root
prop.password=root
使用context名称空间引入peoperties文件到spring的配置文件中
<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:druid.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.username}"/>
<property name="password" value="${prop.password}"/>
</bean>
</bean>
IOC操作Bean管理(基于注解)
什么是注解
注解时代码特殊标记,格式为
- @注解名称(属性名称=属性值, 属性名称=属性值,....)
使用注解, 可以作用在类上面, 方法上和属性上, 大大简化了xml配置
spring针对Bean提供的注解
- Component
- Service
- Controller
- Repository
这四个注解的功能是一样的, 都可以用来创建bean实例
基于注解方式实现对象创建
使用spring提供的注解方式, 需要引入spring aop的jar包, 在使用前, 还需要开启组件扫描, 告诉spring, bean都放在哪个包下
开启组件扫描需要使用context名称空间
<context:component-scan base-package="com.demo.spring5.dao, com.demo.spring5.service"/>
若要添加多个包有两种写法:
- 使用逗号隔开, 如
com.demo.spring5.dao, com.demo.spring5.service - 写包的上层目录, 如service和dao都在spring5包下, 则可写为
com.demo.spring5
现在就可以使用注解来创建bean了
如创建UserService的bean
@Component(value = "userService")
public class UserService {
public void add() {
System.out.println("service add ");
}
}
value中的属性只可以省略不写, 默认值就是类的名称(首字母小写), 如, UserService value的默认值为userService, UserDao的默认值为userDao
测试
public class Test {
@org.junit.Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
关于扫描的过滤器设置
若要只扫描某一个注解的Bean, 如Controller, 可以使用过滤器
<context:component-scan base-package="com.demo.spring5" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
若要不扫描某个注解, 如Service, 如下
<context:component-scan base-package="com.demo.spring5">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
注入属性
spring提供了以下注解来注入属性
- Autowired: 根据属性类型进行自动装配
public interface UserDao {
}
@Repository
public class UserDaoImpl implements UserDao {
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add ");
}
}
- Qualifier: 根据属性名称进行注入, 需要与Autowired一起使用
public interface UserDao {
}
@Repository(value="UserDaoImpl")
public class UserDaoImpl implements UserDao {
}
@Service
public class UserService {
@Autowired
@Qualifier(value = "UserDaoImpl")
private UserDao userDao;
public void add() {
System.out.println("service add ");
}
}
- Resource: 可以根据类型注入, 也可以根据名称注入
// 根据类型注入
@Resource
private UserDao userDao;
// 根据名称注入
@Resource(value = "UserDaoImpl")
private UserDao userDao;
- Value: 注入普通类型属性
@Service
public class UserService {
@Value(value = "abc")
private String name;
public void add() {
System.out.println("service add " + name);
}
}
使用配置类代替xml配置文件
@Configuration
@ComponentScan(basePackages = {"com.demo.spring5"})
public class SpringConfig {
}
@org.junit.Test
public void test2() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}

浙公网安备 33010602011771号