位置:首页 > Java > Spring IOC控制反转入门与实战教程

Spring IOC控制反转入门与实战教程

时间:2026-08-18  |  作者:实验室老王  |  阅读:0

一、含义

控制反转,说到底就是把主动权交出去。以前是开发者自己创建资源,主动向环境索取;现在反过来,环境已经把所有资源都准备好了,开发者只需要享受资源注入就行。换句话说,你不用再操心“这东西怎么造出来的”,直接拿来用就好。

SpringIOC控制反转实战教程

二、基于 XML 管理 Bean

1、准备

先创建一个 Ma ven 工程,在 pom.xml 里加上必需依赖。


    
        org.springframework
        spring-context
        5.3.1
    

然后在 com.example.pojo 包下,创建三个实体类:Teacher、Clazz、Student。具体代码就不逐行解释了,重点是每个类都要有构造器、Getter/Setter 和 toString 方法,这是 Ja va 实体类的常规操作。

public class Teacher { //Alt+Insert,一键生成构造器、Get、Set、toString 方法...
    private Integer tid;
    private String tname;
    public Teacher() {
    }
    public Teacher(Integer tid, String tname) {
        this.tid = tid;
        this.tname = tname;
    }
    public void setTid(Integer tid) {
        this.tid = tid;
    }
    public void setTname(String tname) {
        this.tname = tname;
    }
    public Integer getTid() {
        return tid;
    }
    public String getTname() {
        return tname;
    }
    @Override
    public String toString() {
        return "Teacher{" +
                "tid=" + tid +
                ", tname='" + tname + ''' +
                '}';
    }
}
public class Clazz {
    private Integer cid;
    private String cname;
    private List students;
    public Clazz() {
    }
    public Clazz(Integer cid, String cname) {
        this.cid = cid;
        this.cname = cname;
    }
    public Integer getCid() {
        return cid;
    }
    public String getCname() {
        return cname;
    }
    public List getStudents() {
        return students;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public void setStudents(List students) {
        this.students = students;
    }
    @Override
    public String toString() {
        return "Clazz{" +
                "cid=" + cid +
                ", cname='" + cname + ''' +
                ", students=" + students +
                '}';
    }
}
public class Student {
    private Integer sid;
    private String sname;
    private Integer age;
    private String gender;
    private Clazz clazz;
    private String[] hobby;
    private Map teacherMap;
    public Student() {
    }
    public Student(Integer sid, String sname, Integer age, String gender) {
        this.sid = sid;
        this.sname = sname;
        this.age = age;
        this.gender = gender;
    }
    public Integer getSid() {
        return sid;
    }
    public String getSname() {
        return sname;
    }
    public Integer getAge() {
        return age;
    }
    public String getGender() {
        return gender;
    }
    public Clazz getClazz() {
        return clazz;
    }
    public String[] getHobby() {
        return hobby;
    }
    public Map getTeacherMap() {
        return teacherMap;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }
    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }
    public void setTeacherMap(Map teacherMap) {
        this.teacherMap = teacherMap;
    }
    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + ''' +
                ", age=" + age +
                ", gender='" + gender + ''' +
                ", clazz=" + clazz +
                ", hobby=" + Arrays.toString(hobby) +
                ", teacherMap=" + teacherMap +
                '}';
    }
}

2、配置 Bean

配置文件的创建很简单:右击 resources 目录,依次选择 New -> XML Configuration File -> Spring Config,然后命名为 applicationContext.xml。接下来看看这个配置文件里到底写了什么。



    
    
        
        
        
        
        
            
        
        
        
            ]]>
        
        
        
        
        
            
            
                
                
            
        
        
        
            
                抽烟
                喝酒
                烫头
            
        
        
        
        
        
            
                
                
            
        
    
    
        
        
        
        
        
        
            
                
            
        
    
    
    
        
    
    
    
        
        
    
    
        
        
        
    
    
        
        
    

要点精炼:基于 XML 配置 Bean,核心是依赖注入,包括 setter 注入和构造器注入。注入的类型也很丰富,字面量、null、特殊符号、类类型、数组、List、Map 都能搞定。

3、获取 Bean

从 IOC 容器中获取 Bean 对象,有三种方式:根据 id、根据类型、或者 id 和类型一起用。

//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//(1)根据 id,从 IOC 容器中获取 bean 对象
Student studentOne = (Student) ioc.getBean("student");
/*
    (2)根据类型,从 IOC 容器中获取 bean 对象
    注意:要求容器中有且仅有一个类型匹配的 bean,否则报错 NoSuchBeanDefinitionException/NoUniqueBeanDefinitionException;
         满足 bean 唯一性的前提下,通过 bean 的类型、bean 所继承的类的类型、bean 所实现的接口的类型均可获取 bean 对象。
*/
Student studentTwo = ioc.getBean(Student.class);
//(3)根据类型 & id,从 IOC 容器中获取 bean 对象
Teacher teacher = ioc.getBean("teacherOne", Teacher.class);

4、Spring 管理数据源

先在 pom.xml 里添加 MySQL 驱动和 Druid 数据源的依赖。



    mysql
    mysql-connector-ja va
    8.0.16



    com.alibaba
    druid
    1.0.31

然后创建 jdbc.properties 属性配置文件,右击 resources,选择 New -> Resource Bundle。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydbuseUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8
jdbc.username=root
jdbc.password=root
jdbc.initialSize=10
jdbc.maxActive=20
jdbc.maxWait=5000

接着在 applicationContext.xml 中配置数据源,并引入外部属性文件。



	
	
	
	
	
	
	
	
	
	


最后,从 IOC 容器中获取数据源,测试连接。

//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据类型,从 IOC 容器中获取 Druid 数据库连接池对象
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
//从数据库连接池,获取数据库连接对象
System.out.println(dataSource.getConnection());

5、自动装配

自动装配是 IOC 容器按照既定规则,自动匹配某个 bean,为指定 bean 依赖的类类型或接口类型属性赋值。通过 bean 标签的 autowire 属性设置规则:no 或 default 表示不装配;byType 表示根据 setter 参数类型匹配 bean,如果匹配到多个会抛 NoUniqueBeanDefinitionException;byName 表示截取 setter 名称作为 bean 的 id 进行匹配。

值得注意的一点是,基于 XML 配置时,设置 autowire 为 byType 或 byName,Spring 会先调用无参构造创建对象,再通过 set 注入为类类型或接口类型的属性赋值。字符串、数字等字面量类型的属性会跳过,需要手动用 property 来赋值。

三、基于注解管理 Bean

1、准备

同样,先创建 Ma ven 工程,添加 spring-context 依赖。


    
        org.springframework
        spring-context
        5.3.1
    

2、标记

在 com.example.controller 包下创建 UserController,用 @Controller 注解标记为控制层组件。

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    //添加用户信息
    public void sa veUser(){
        userService.sa veUser();
    }
}

在 com.example.service 包下创建 UserService 接口,在 com.example.service.impl 包下创建 UserServiceImpl 实现类,用 @Service 标记为业务层组件。

public interface UserService {
    //保存用户信息
    void sa veUser();
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public void sa veUser() {
        userDao.sa veUser();
    }
}

在 com.example.dao 包下创建 UserDao 接口,在 com.example.dao.impl 包下创建 UserDaoImpl 实现类,用 @Repository 标记为持久层组件。

public interface UserDao {
    //保存用户信息
    void sa veUser();
}
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void sa veUser() {
        System.out.println("保存成功!");
    }
}

另外,@Component 是通用注解,@Controller、@Service、@Repository 都是它的派生注解,功能基本一致,只是语义上区分了代码层次。不过,注解只是做标记,要让 Spring 知道这些标记,还需要配置组件扫描。

3、扫描

创建 Spring 配置文件 applicationContext.xml,配置组件扫描。



    
    
        
        
        
        
        
        
    

通过“注解 + 扫描”配置的 bean,id 默认是类名的小驼峰形式(如 userController),也可以通过注解的 value 属性自定义 id(如 @Controller("controller"))。这时候,Spring 会通过反射调用默认无参构造来创建对象。

4、获取 Bean

同样有三种获取方式:根据 id、根据类型、id 和类型一起用。

//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//(1)根据 id,从 IOC 容器中获取 bean 对象
UserController userController = (UserController) ioc.getBean("userController");
//(2)根据类型,从 IOC 容器中获取 bean 对象
UserService userService = ioc.getBean(UserService.class);
//(3)根据类型 & id,从 IOC 容器中获取 bean 对象
UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);

5、自动装配

在成员变量上添加 @Autowired 注解,就能完成自动装配。Spring 默认根据成员变量的类型,在 IOC 容器中匹配 bean 并赋值。如果匹配到多个类型相同的 bean,就把成员变量的变量名作为 bean 的 id 去匹配;如果还是匹配不上,就会抛 NoUniqueBeanDefinitionException。这时候可以用 @Qualifier 注解指定 bean 的 id。如果没有任何类型匹配的 bean,则抛 NoSuchBeanDefinitionException,因为 @Autowired 的 required 属性默认是 true,要求必须完成装配;如果设置为 false,则能装配就装配,不能装配就使用默认值。

值得注意的是,基于注解配置时,在成员变量上加 @Autowired,Spring 会先调用默认无参构造创建对象,然后利用反射直接给成员变量赋值,这既不是 setter 注入,也不是构造器注入。当然,@Autowired 也可以放在 set 方法或构造器上,这里就不展开讨论了。

四、其他知识

1、Bean 的作用域

作用域规定的是 IOC 容器何时创建对象、创建多少个对象,以及对象的生效范围。基于 XML 时,通过 bean 标签的 scope 属性设置:singleton 是单例(默认),prototype 是多例(每次获取都是新对象)。基于注解时,用 @Scope 注解设置,值可以是 ConfigurableBeanFactory.SCOPE_SINGLETON(单例,默认)或 ConfigurableBeanFactory.SCOPE_PROTOTYPE(多例)。

2、Bean 的生命周期

对于单例 bean,获取 IOC 容器时就会完成创建、依赖注入、执行后置处理器的 postProcessBeforeInitialization()、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 这一整套流程;IOC 容器关闭时,执行销毁方法,之后等待 JVM 的 GC 回收。

对于多例 bean,则是在获取 bean 时执行上述流程,但不会执行销毁方法,同样等待 GC 回收。

基于 XML 时,通过 bean 标签的 init-method 和 destroy-method 属性设置初始化和销毁方法;基于注解时,用 @PostConstruct 和 @PreDestroy 注解来设置。

3、Bean 的后置处理器

后置处理器用于在 Bean 生命周期初始化前后添加额外操作。需要创建一个类实现 BeanPostProcessor 接口,重写 postProcessBeforeInitialization() 和 postProcessAfterInitialization() 方法。

public class MyBeanPostProcessor implements BeanPostProcessor { //Ctrl+O,选择需要实现的方法
    //在 bean 生命周期初始化之前执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    //在 bean 生命周期初始化之后执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

然后在 applicationContext.xml 中配置这个后置处理器。这里用 XML 方式配置,也可以用注解。

一个关键点:Bean 后置处理器不是针对某一个 bean,而是对 IOC 容器中所有 bean 都生效。

4、FactoryBean

FactoryBean 是 Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑。先创建一个实体类 User。

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    public User() {
    }
    public User(Integer id, String username, String password, Integer age) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
    }
    public Integer getId() {
        return id;
    }
    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
    public Integer getAge() {
        return age;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", password='" + password + ''' +
                ", age=" + age +
                '}';
    }
}

然后创建 UserFactoryBean 实现 FactoryBean 接口,泛型指定为最终要交给 IOC 容器管理的对象类型,并重写 getObject()、getObjectType()、isSingleton() 方法。

public class UserFactoryBean implements FactoryBean { //指定泛型为最终交给 IOC 容器管理的对象类型
    @Override
    public User getObject() throws Exception {
        //在这里手写复杂创建逻辑!
        return new User(); //返回最终交给 IOC 容器管理的对象
    }
    @Override
    public Class getObjectType() {
        return User.class; //返回最终交给 IOC 容器管理的对象的类型
    }
    @Override
    public boolean isSingleton() {
        return true; //设置最终交给 IOC 容器管理的对象是否单例,true表单例(默认)、false表多例
    }
}

在 applicationContext.xml 中配置 UserFactoryBean,同样可以用 XML 或注解方式。

获取 Bean 时,通过 id 获取的是 User 对象,而加 & 前缀获取的是工厂本身 UserFactoryBean 对象。

//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据 id,从 IOC 容器中获取 bean 对象
User user = (User) ioc.getBean("userBean");
//加 & 符号,从 IOC 容器中获取工厂本身 UserFactoryBean 对象
UserFactoryBean userFactoryBean = (UserFactoryBean) ioc.getBean("&userBean");

最后补充一点,BeanFactory 是 IOC 容器最顶层的根接口,负责创建、获取、管理所有 Bean 对象;而 FactoryBean 是 Spring 提供的工厂 Bean 接口,专门用于封装复杂对象的创建逻辑。Spring 会先实例化 FactoryBean 工厂,再调用 getObject() 获取目标对象放入容器,工厂本身也可以通过 id 加 & 前缀从 IOC 容器中获取。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多