文章目录
什么是 IoC
IoC 也叫控制反转
在传统的程序开发中,需要获取对象时,通常由开发者来手动创建实例化对象,但是在 Spring 框架中创建对象的工作不再由开发者完成,而是交给 IoC 容器来创建,我们直接获取即可,整个流程完成反转,因此是控制反转。
IOC的实现
创建实体类:
public class Student {
private int id;
private String name;
}
在 pom.xml 中添加 Spring 依赖
创建配置文件,可以自定义文件名 spring.xml
无参构造方式
在 spring.xml 中配置 bean 标签,IoC 容器通过加载 bean 标签来创建对象。
<bean id="stu" class="com.sensen.springdemo.entity.Student"></bean>
调用 API 获取 IoC 创建的对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student stu = (Student) applicationContext.getBean("stu");
System.out.println(stu);
Student bean = applicationContext.getBean(Student.class);
System.out.println(bean);
两种获取方式,上面的是根据id获取,后面的根据类名,返回结果都是属性值全为初始化值或null的对象.
如果想给对象赋值怎么办呢?
需要给实体类的每个属性加上set和get方法,spring通过对象的set方法和get方法来初始化对象。
之后修改bean配置即可:
<bean id="stu" class="com.southwind.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
再次运行即可得到有值的对象
有参构造方式
先给对象加上有参数的构造方法.
修改bean配置:
<bean id="stu" class="com.sensen.springdemo.entity.Student">
<constructor-arg name="id" value="3"></constructor-arg>
<constructor-arg name="name" value="小明"></constructor-arg>
</bean>
再次运行,也是得到有值的对象.
依赖注入(DI)
如果 IoC 容器管理多个对象,并且对象之间有级联关系,如何实现?
创建 Classes 类
public class Classes {
private int id;
private String name;
}
在 Student 类中添加 Classes 属性
private int id;
private String name;
private Classes classes;
spring.xml 中配置 classes 对象,然后将该对象赋值给 stu 对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="stu" class="com.sensen.springdemo.entity.Student">
<constructor-arg name="id" value="3"></constructor-arg>
<constructor-arg name="name" value="小明"></constructor-arg>
<constructor-arg name="classes" ref="classes"></constructor-arg>
</bean>
<bean id="classes" class="com.sensen.springdemo.entity.Classes">
<property name="id" value="1"></property>
<property name="name" value="Java班"></property>
</bean>
</beans>
再次获取 Student 对象,结果如下图所示。

在 spring.xml 中,通过 ref 属性将其他 bean 赋给当前 bean 对象,这种方式叫做依赖注入(DI),是 Spring 非常重要的机制,DI 是将不同对象进行关联的一种方式,是 IoC 的具体实现方式,通常 DI 和 IoC 是紧密结合在一起的,因此一般说的 IoC 包括 DI。
Spring 中的 bean
bean 是根据 scope 来生成的,scope 常用的两种类型:
singleton,单例,表示通过 Spring 容器获取的该对象是唯一的;
prototype,原型,表示通过 Spring 容器获取的对象都是不同的;
默认是singleton的,如果需要改变配置,只需修改为prototype即可:
<bean id="stu" class="com.sensen.springdemo.entity.Student" scope="prototype">
Spring 的依赖
依赖是 bean 和 bean 之间的一种关联方式,配置依赖关系后,被依赖的 bean 一定先创建,再创建依赖的 bean.
例如如下配置:
<bean id="user" class="com.southwind.entity.User" depends-on="car">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
</bean>
<bean id="car" class="com.southwind.entity.Car">
<property name="id" value="1"></property>
<property name="brand" value="宝马"></property>
</bean>
正常情况下,spring创建bean的顺序是由上到下,但是如果创建了依赖关系,则会先创建被依赖的对象.
例如上面这种配置后,就会先创建 car,再创建 user.
Spring 读取外部资源
创建实体类TestConfig,,并加上setget方法:
public class TestConfig {
private String url;
private String user;
private String root;
@Override
public String toString() {
return "TestConfig{" +
"url='" + url + '\'' +
", user='" + user + '\'' +
", root='" + root + '\'' +
'}';
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
}
在spring.xml的相同目录下,创建jdbc.properties
url = com.testConfig.com
user = root
pwd = root
之后在spring.xml中如下配置:
<!-- 导入外部的资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 创建对象 -->
<bean id="testConfig" class="com.sensen.springdemo.entity.TestConfig">
<property name="url" value="${user}"></property>
<property name="user" value="${pwd}"></property>
<property name="root" value="${url}"></property>
</bean>
之后运行
ApplicationContext applicationContext1 = new ClassPathXmlApplicationContext("spring.xml");
TestConfig testConfig = (TestConfig) applicationContext.getBean("testConfig");
System.out.println(testConfig);
即可看到配置已经生效了.
IoC 通过工厂方法创建对象
IoC 是典型的工厂模式,把IOC看成一个大工厂,这个大工厂里面要生成的对象都是在XML文件中定义的,然后利用java的反射编程,根据xml给出的类名动态的生成相应的对象。
下面我们就来学习如何使用工厂模式来创建 bean。
静态工厂
先创建个Car类:
public class Car {
private int num;
private String brand;
public Car(int num, String brand) {
super();
this.num = num;
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"num=" + num +
", brand='" + brand + '\'' +
'}';
}
}
再来个静态工厂类:
public class StaticCarFactory {
private static Map<Integer, Car> cars;
static{
cars = new HashMap<Integer,Car>();
cars.put(1, new Car(1,"奥迪"));
cars.put(2, new Car(2,"奥拓"));
}
public static Car getCar(int num){
return cars.get(num);
}
}
之后是xml的配置:
<!-- 在 spring.xml 中配置静态工厂-->
<!-- factory-method 指向静态方法;-->
<!-- constructor-arg 的 value 属性为调用静态方法所传的参数。-->
<bean id="car1" class="com.sensen.springdemo.factory.StaticCarFactory" factory-method="getCar">
<constructor-arg value="1"></constructor-arg>
</bean>
最后在test中演示:
//静态工厂
Car car = (Car) applicationContext.getBean("car1");
System.out.println(car);
即可看到奥迪车了。
实例工厂
还是上面的Car类,来个实例工厂:
public class InstanceCarFactory {
private Map<Integer, Car> cars;
public InstanceCarFactory() {
cars = new HashMap<Integer,Car>();
cars.put(1, new Car(1,"奥迪"));
cars.put(2, new Car(2,"奥拓"));
}
public Car getCar(int num){
return cars.get(num);
}
}
xml的配置:
<!-- 配置实例工厂对象 -->
<bean id="carFactory" class="com.sensen.springdemo.factory.InstanceCarFactory"></bean>
<!-- 通过实例工厂对象创建 car 对象 -->
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="2"></constructor-arg>
</bean>
演示:
//实例工厂
Car car2 = (Car) applicationContext.getBean("car2");
System.out.println(car2);
IoC 自动装载(autowire)
Spring 还提供了另外一种更加简便的方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成依赖注入。
自动装载有两种方式:
byName,通过属性名自动装载。
创建实体类,类中成员变量有Car对象:
public class Person {
private int id;
private String name;
private Car car;
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", car=" + car +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
在xml中配置:
<!-- 自动装载-->
<bean id="person" class="com.sensen.springdemo.entity.Person" autowire="byName">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
<bean id="car" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="2"></constructor-arg>
</bean>
test类:
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
可以看到结果:
Person{id=1, name=‘张三’, car=Car{num=2, brand=‘奥拓’}}
byType,通过属性对应的数据类型自动装载。
其他的不变,把xml修改为:
<!-- 自动装载-->
<bean id="person" class="com.sensen.springdemo.entity.Person" autowire="byType">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
<bean id="car" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="2"></constructor-arg>
</bean>
注意的是需要把其他的Car类配置删除,不然spring无法识别你需要装载哪个Car。
Spring IoC 基于注解
新建一个项目,创建及格包:
controller,service,dao,entity。
在entity中新建user类:
package com.sensen.springboot.entity;
/**
* @ClassName User
* @Author linsen
* @Date 2020/11/26 11:12
**/
public class User {
private int id;
private String name;
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
在dao中新建接口和实现类,模拟数据:
public interface UserDAO {
public User getUserById(int id);
}
public class UserDAOImpl implements UserDAO{
private static Map<Integer,User> users;
static{
users = new HashMap<Integer,User>();
users.put(1, new User(1, "张三"));
users.put(2, new User(2, "李四"));
users.put(3, new User(3, "王五"));
}
@Override
public User getUserById(int id) {
// TODO Auto-generated method stub
return users.get(id);
}
}
在service中新建接口和实现类:
public interface UserService {
public User getUserById(int id);
}
public class UserServiceImpl implements UserService{
private UserDAO userDAO;
@Override
public User getUserById(int id) {
// TODO Auto-generated method stub
return userDAO.getUserById(id);
}
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}
最后在controller中新建类:
public class UserController {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUserById(int id){
return userService.getUserById(id);
}
}
之后是spring.xml的配置:
<?xml version="1.0" encoding="UTF-8"?>
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- <context:component-scan base-package="com.sensen.springboot"></context:component-scan>-->
<!-- 配置UserController -->
<bean id="userController" class="com.sensen.springboot.controller.UserController">
<property name="userService" ref="userService"></property>
</bean>
<!-- 配置UserService -->
<bean id="userService" class="com.sensen.springboot.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"></property>
</bean>
<!-- 配置UserDAO -->
<bean id="userDAO" class="com.sensen.springboot.dao.UserDAOImpl"></bean>
</beans>
新建main类:
public class ApplicationTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserController userController =(UserController)applicationContext.getBean("userController");
System.out.println(userController.getUserById(1));
}
}
即可输出结果:
User{id=1, name=‘张三’}
下面把项目改造成用注解实现,首先删除xml中所有配置,加上一句:
<context:component-scan base-package=“com.sensen.springboot”></context:component-scan>
这句话代表项目改用注解配置。
之后在各个类上加注解
@Controller
public class UserController {
@Autowired
private UserService userService;
..........................................
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDAO userDAO;
...........................................
@Repository
public class UserDAOImpl implements UserDAO{
即可完成和上面一样的效果。类中属性的自动装载默认是通过 byType 的方式实现的。
当然也可以用byName的方式去完成:
将service的注解改成
@Service(“myUserService”)
之后再controller中自动装载service类时改成
@Autowired()
@Qualifier(“myUserService”)
private UserService userService;
效果相同,只是换成了byName的方式去装载。
@Qualifier() 中的值必须与 @Service() 中的值一致,才能完成自动装载。
源代码
两种方式配置ioc的代码
https://gitee.com/linsen97/spring-demo
https://gitee.com/linsen97/spring-demo2
简单的实现spring的ioc功能
https://gitee.com/linsen97/spring-ioc-impl
本文详细介绍了Spring框架中的IoC容器概念及其多种实现方式,包括无参构造、有参构造、依赖注入等,同时讲解了如何通过工厂方法创建对象、自动装载以及基于注解的配置。
174万+

被折叠的 条评论
为什么被折叠?



