Spring之ioc控制反转

本文详细介绍了Spring的IOC思想,包括控制反转的概念、配置bean、单例与多例模式、懒加载,以及DI依赖注入的实践,通过set注入、构造方法注入和自动注入等方式实现组件间的解耦。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring的IOC思想

1.IOC:控制反转

IOC 是指在程序开发中,实例的创建不在由调用者管理,而是由Spring容器创建。
容器会控制对象之间的关系,而不是由程序代码直接控制。
因此控制权由程序代码转移到了Spring容器中。
控制权发生了反转,这就是Spring的IOC思想。

准备工作:修改pom文件

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.15.RELEASE</version>
    </dependency>

刷新maven即可添加。

2.使用bean标签:

先创建一个preson类

package com.lr.spring.po;

public class Person {
    //属性
    private Integer id;
    private String name;
    private double sal;
    //无参构造方法
    public Person (){
        System.out.println("创建person对象");
    }
}

配置文件

<?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">
<!--
beans是spring中的根标签,在bean标签中配置自己需要的对象
1.bean在spring中相当于java中的对象,需要把该对象对应的类配置到容器中
2.id="":标识,相当于对象名,唯一
3.class="包名.类名(即全限定名)"  包名.类名通过反射创建该类的对象,不能写接口名
  -->
    <bean id="person1" class="com.lr.spring.po.Person"></bean>
<!--  把Person类纳入到spring管理中,当程序执行(默认),在spring容器,创建一个Preson类的对象preson1(反射)  -->
</beans>

 测试类

package com.lr.spring;

import com.lr.spring.po.Person;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppTest{
    @Test
    public void test01(){
        BeanFactory factory = new ClassPathXmlApplicationContext("application.xml");
        Person person = (Person)factory.getBean("person1");
        System.out.println(person);
        //单例模式:对象person是唯一的
        Person person2 = (Person)factory.getBean("person1");
        System.out.println(person2);
    }
}

测试结果

3.:单例模式与多例模式

单例模式:singleton

默认值,单例(不管任何人,多少人过来访问,拿到的都是同一个对象)

    <bean id="person1" class="com.lr.spring.po.Person" scope="singleton"></bean>

 多例模式:prototype

多例,懒加载(延迟加载),读取配置文件的时候不创建对象,只有调用getBean()方法的时候,才创建对象

    <bean id="person1" class="com.lr.spring.po.Person" scope="prototype"></bean>

加入多例模式后,测试结果发生的变化,两个对象地址不一样了

 

4.懒加载

beans是spring中的根标签,在bean标签中配置自己需要的对象
1.bean在spring中相当于java中的对象,需要把该对象对应的类配置到容器中
2.id="":标识,相当于对象名,唯一
3.class="包名.类名(即全限定名)"  包名.类名通过反射创建该类的对象,不能写接口名
  -->
    <bean id="person1" class="com.lr.spring.po.Person" scope="prototype" lazy-init="default"></bean>
<!--  把Person类纳入到spring管理中,当程序执行(默认),在spring容器,创建一个Preson类的对象preson1(反射)  -->
<!--scope="prototype"/"singleton":控制spring容器创建的对象是单例还是多例
singleton:默认值,单例(不管任何人,多少人过来访问,拿到的都是同一个对象)
prototype:多例,懒加载(延迟加载),读取配置文件的时候不创建对象,只有调用getBean()方法的时候,才创建对象
lazy-init="true"/"default"/"false":主要是针对单例模式的
true:懒加载(延迟加载),只有使用的时候才真正创建该类对象
default/false:不懒加载,读取配置文件时就立刻创建对象,default是默认值
-->

 ioc实例

1.创建User类

package com.lr.spring.po;

import java.io.Serializable;

public class User implements Serializable {
    public Integer id;
    private String name;
    private String password;

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

2.写入配置文件,用bean进行管理

<?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="user1" class="com.lr.spring.po.User"></bean>
    <bean id="user2" class="com.lr.spring.po.User"></bean>
    <bean id="user3" class="com.lr.spring.po.User"></bean>
</beans>

3.测试程序

测试ApplicationContext()中的几个方法

package com.lr.spring;

import com.lr.spring.po.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppTest{
    @Test
    public void test01(){
        //ApplicationContext在实际开发中取代了BeanFactory,功能更加枪法,而且有很多其他功能
        ApplicationContext factory = new ClassPathXmlApplicationContext("application.xml");
        User user1=(User)factory.getBean("user1");
        System.out.println(user1);
        //getBeanDefinitionCount()可以查配置文件里有多少个对象
        int count = factory.getBeanDefinitionCount();
        System.out.println("对象的个数:"+count);
        //getBeanDefinitionNames():获取对象的名字
        String[] names =factory.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}
//通过程序在初始化的时候,导入Bean配置文件,然后得到Bean实例。
        ApplicationContext factory2 =new FileSystemXmlApplicationContext("D:\\code\\spring02\\src\\main\\resources\\application.xml");
        User user1 = (User) factory2.getBean("user1");
        System.out.println(user1);

添加其他类

添加date类

1.配置文件

注意,这里需要绝对路径,date类在其他包里

<?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="user1" class="com.lr.spring.po.User"></bean>
    <bean id="user2" class="com.lr.spring.po.User"></bean>
    <bean id="user3" class="com.lr.spring.po.User"></bean>
    <bean id="date1" class="java.util.Date"></bean>
</beans>

2.测试程序 

ApplicationContext factory = new ClassPathXmlApplicationContext("application.xml"); 
Date date1=(Date) factory.getBean("date1");
      System.out.println(date1);

 

在User加入多参构造方法,与tostring方法,测试默认调用无参构造方法 

 

工厂模式

如果想调用多参构造方法呢?使用工厂模式:写一个UserFactory类,提供方法

1.静态方法创建对象

package com.lr.spring.factory;

import com.lr.spring.po.User;

public class UserFactory {
    //静态方法创建对象(3参构造方法)
    public static User getUserStaticInstance(){
        System.out.println("通过spring调用工厂中的静态方法创建3参User对象:");
        return new User(1111,"龙傲天","1380341");
    }
}

2.配置文件

!--  通过spring调用工厂中的静态方法创建3参User对象  -->
<!--   静态方法 -->
    <bean id="user4" class="com.lr.spring.factory.UserFactory" factory-method="getUserStaticInstance"></bean>

3测试文件 

    @Test
    public void test02(){
        ApplicationContext app=new ClassPathXmlApplicationContext("application.xml");
        User user=(User) app.getBean("user4");
        System.out.println(user);
    }

1.实例方法创建对象 

//实例方法创建对象
    public User getUserInstance(){
        System.out.println("通过spring调用工厂中的静态方法创建3参User对象");
        return new User(2222,"唐三","123456");
    }

 2.配置文件

<!--  使用实例方法  -->
    <bean id="factory" class="com.lr.spring.factory.UserFactory"></bean>
    <bean id="user5" factory-bean="factory" factory-method="getUserInstance"></bean>

 3测试程序

   public void test02(){
        ApplicationContext app=new ClassPathXmlApplicationContext("application.xml");
//        User user=(User) app.getBean("user4");
//        System.out.println(user);
        User user=(User) app.getBean("user5");
        System.out.println(user);
    }

 

4. 补充bean标签属性

init属性(初始化)       destroy属性(销毁)

    <bean id="user1" class="com.lr.spring.po.User" init-method="init" destroy-method="destory"></bean>

测试初始化

 测试销毁方法

ps:需要用到实现类,因为ApplicationContext没有close()方法

 public void test03(){
        ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("application.xml");
//        User user=(User) app.getBean("user4");
//        System.out.println(user);
        User user=(User) app.getBean("user5");
        System.out.println(user);
        app.close();//关闭Spring容器
    }
}

 

 

使用构造方法创建bean对象

两种方法

通过name属性

通过下标

<?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对象-->
    <bean id="user1" class="com.lr.spring.po.User">
        <constructor-arg name="id" value="1003"></constructor-arg>
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="password" value="123456"></constructor-arg>
    </bean>
    <bean id="user2" class="com.lr.spring.po.User">
        <constructor-arg name="id" index="0" value="1004"></constructor-arg>
        <constructor-arg name="name" index="1" value="李四"></constructor-arg>
        <constructor-arg name="password" index="2" value="654321"></constructor-arg>
    </bean>

</beans>

测试程序

 @Test
    public void test03(){
        ApplicationContext factory = new ClassPathXmlApplicationContext("application2.xml");
        User user01=(User) factory.getBean("user1");
        System.out.println(user01);
        User user02=(User) factory.getBean("user2");
        System.out.println(user02);
    }

测试结果



两种方法都能创建对象

DI:依赖注入

DI—Dependency Injection,即依赖注入

是组件之间依赖关系由容器在运行期决定

我们只需要通过 简单的配置,无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,不需要关心具体的资源来自何处,由谁实现?

 

加入数据访问层dao与业务层service

面向接口编程:减少因为Dao层的改变,对业务层代码的修改.

1.业务逻辑

以前我们常用的业务逻辑

//UserService 
package com.lr.spring.service;

public interface UserService {
    void addUser();
}



//UserDao 
package com.lr.spring.dao;

public interface UserDao {
    void addUser();
}



//UserServiceImpl 
import com.lr.spring.dao.UserDaoImpl;

public class UserServiceImpl implements UserService {
    private UserDaoImpl userDao = new UserDaoImpl();
    @Override
    public void addUser() {
        System.out.println("调用UserDao,插入User对象");
        userDao.addUser();
    }
}



//UserDaoImpl 

package com.lr.spring.dao;

public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("Dao中输入User对象");
    }
}

2.人为实现 DI注入

spring可以做到更完美的解耦合,令dao层的改动完全不影响service层,我们需要修改serviceinpl

这个方法就是DI注入

人为实现DI注入代码,有三种方法

第一种:set注入

package com.lr.spring.service;

import com.lr.spring.dao.UserDao;
import com.lr.spring.dao.UserDaoImpl;

public class UserServiceImpl implements UserService {
//    private UserDaoImpl userDao = new UserDaoImpl();
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        System.out.println("调用UserDao,插入User对象");
        userDao.addUser();
    }
//通过set注入
    public static void main(String[] args) {
        UserServiceImpl userService=new UserServiceImpl();
        userService.setUserDao(new UserDaoImpl());
    }
}

Spring实现DI注入

第一种set注入

set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在
Spring 的依赖注入中大量使用。

1.配置文件

<?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 name="userDao" class="com.lr.spring.dao.UserDaoImpl"></bean>
      <bean name="suerService" class="com.lr.spring.service.UserServiceImp
      <!--    property标签中的name参数跟set方法名字一一对应,跟属性名无关      -->
          <property name="userDao" ref="userDao"></property>
      </bean>

</beans>

 ps:property标签中的name参数跟set方法名字一一对应,跟属性名无关 

2.测试程序

@Test
    public void test04(){
        ApplicationContext factory = new ClassPathXmlApplicationContext("application3.xml");
        UserService userService=(UserService) factory.getBean("userService");
        userService.addUser();
    }

 

第二种:通过构造方法注入 

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。使用构造器设置依赖关系。
<!--  构造方法注入  -->
<!-- constructor-arg标签中的name参数跟构造方法中的参数名字名字一一对应,跟属性名无关 -->
    <bean name="userService" class="com.lr.spring.service.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

 

结果一模一样

ps:constructor-arg标签中的name参数跟构造方法中的参数名字名字一一对应,跟属性名无关

扩展 

新增两个dao实现类,我们使用set注入的方式

package com.lr.spring.dao;

public class UserMySQLDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("MySQLDa");
    }
}




package com.lr.spring.dao;

public class UserOracleDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("OracleDao");
    }
}

配置文件 

<bean name="userDao" class="com.lr.spring.dao.UserDaoImpl"></bean>
      <bean name="userMySQLDao" class="com.lr.spring.dao.UserMySQLDaoImpl"></bean>
      <bean name="userOracleDao" class="com.lr.spring.dao.UserOracleDaoImpl"></bean>

      <bean name="userService" class="com.lr.spring.service.UserServiceImpl">
        <property name="userDao" ref="userMySQLDao"></property>
      </bean>

 

如果我们的数据库换成MySQL只需修改配置文件property标签ref属性值为userMySQLDao即可。

第三种:自动注入 

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签
设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据
自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入,跟set方法中的方法名对应
byType: 根据类型自动注入,跟属性的类型对应(只有一个的时候使用)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值