当前位置: 首页 > article >正文

spring IOC(实现原理)

文章目录

  • 依赖注入
  • 控制反转
    • 相关
    • Spring 框架的 Bean管理的配置文件方式
    • 实例化Bean的三种方式
      • 无参构造器实例化
      • 静态工厂方法实例化
      • 实例工厂方法实例化
      • 静态和动态对比
    • 注解
      • 常用注解
      • 纯注解
  • 其它问题
    • 为什么p 命名空间方式需要无参构造

依赖注入

**依赖注入(DI)**是Spring框架核心功能之一,它允许对象间的依赖关系通过配置或代码在运行时动态注入,而不是通过对象内部硬编码。这种方法减少了代码间的耦合,增加了灵活性和可重用性。

依赖注入强调的是对象的依赖关系由外部来管理和提供,而不是由对象自身负责创建依赖对象。通过这种方式,可以降低对象之间的耦合度。即下面的方式也属于依赖注入。

常见的依赖注入方式

  • 构造函数注入:通过类的构造函数将依赖对象传递给该类。在对象实例化时,容器会调用相应的构造函数,并传入依赖对象的实例。
public class ProductService {
    private final ProductRepository productRepository;

    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    // 业务方法
}
  • Setter 方法注入:利用类的 setter 方法将依赖对象设置到该类中。容器先创建对象实例,然后调用 setter 方法来注入依赖对象。
public class UserService {
    private UserRepository userRepository;

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 业务方法
}

xml依赖注入

  • <property>
  • <list><value></value></list>
  • <map><entry key=' ' value=' '></map>
  • <array><value></value></array>
  • <set><value></value></set>
  • <props><prop key=' '>值</prop></props>
---------------Student类------------------
public class Student {
    private String name;
    private Adress address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
    //getter,setter,无参and全参构造
    }
---------------Teacher类------------------
public class Teacher {
    private String name;
    private int age;
    //getter,setter,无参and全参构造
    }
---------------applicationContext.xml------------------
  <bean id="adress" class="com.luxiya.Adress">
        <property name="address" value="空中花园"/>
    </bean>
   <!-- 需要相应的构造函数-->
    <bean id="teacher" class="com.luxiya.Teacher" c:name="luxiya" c:age="18"/>
    <!-- 需要相应的构造函数-->
    <bean id="teacher1" class="com.luxiya.Teacher">
        <constructor-arg name="name" value="luxiya-constructor"/>
        <constructor-arg name="age" value="188"/>
    </bean>
    <!-- 需要空参构造-->
    <bean id="teacher2" class="com.luxiya.Teacher" p:name="zhihuiguan" p:age="19">
    </bean>

    <bean id="student" class="com.luxiya.Student">
        <property name="name" value="luxiya"/>
        <property name="address" ref="adress"/>
        <property name="books">
            <array>
                <value>java</value>
                <value>python</value>
            </array>
        </property>
        <property name="hobbies">
            <list>
                <value>打篮球</value>
                <value>打游戏</value>
            </list>
        </property>
        <property name="card">
            <map>
                <entry key="身份证" value="123456789"/>
                <entry key="银行卡" value="123456789"/>
            </map>
        </property>
        <property name="games">
            <set>
                <value>LOL</value>
                <value>DOTA</value>
            </set>
        </property>
        <property name="wife">
            <null/>
        </property>
        <property name="info">
            <props>
                <prop key="name">luxiya</prop>
                <prop key="age">18</prop>
            </props>
        </property>
    </bean>
---------------测试类------------------
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
Teacher teacher = (Teacher) context.getBean("teacher");
System.out.println(teacher.toString());
Teacher teacher1 = (Teacher) context.getBean("teacher1");
System.out.println(teacher1.toString());
Teacher teacher2 = (Teacher) context.getBean("teacher2");
System.out.println(teacher2.toString());

控制反转

​ 在传统的程序设计中,当一个对象(A)需要使用另一个对象(B)的功能时,通常会在对象 A 内部主动创建对象 B 的实例,即对象 A 对对象 B 的创建和生命周期管理具有控制权。而在控制反转的思想下,对象 A 所依赖的对象 B 的创建、实例化以及依赖关系的建立,不再由对象 A 自身负责,而是由一个外部的容器(比如 Spring 容器)来负责管理。这样一来,对象 A 获得依赖对象 B 的过程被反转了,控制权从对象 A 转移到了外部容器,这就是 “控制反转” 名称的由来。

​ IoC 容器负责创建对象、管理对象的生命周期,并在适当的时候将对象注入到需要它们的其他对象中。应用程序中的各个组件(对象)只需要关注自身的业务逻辑,而不需要关心依赖对象的创建和获取过程。容器会根据配置信息(如 XML 配置文件或注解)来确定对象之间的依赖关系,并自动完成对象的实例化和注入。

  • XML 配置:在 Spring 早期,XML 是配置 Bean 的主要方式。通过编写 XML 文件,**将 Bean 的定义信息(如 Bean 的类名、依赖关系、属性值等)与 Bean 的实现类分离开来。**这样做的好处是可以在不修改实现类代码的情况下,修改 Bean 的配置信息,便于对系统进行维护和扩展。但缺点是 XML 文件可能会变得冗长复杂,增加了配置的难度和维护成本。
  • 注解配置:随着 Spring 框架的发展,注解配置逐渐流行起来。使用注解(如 @Component@Service@Repository@Autowired 等)可以直接在实现类中定义 Bean 的信息和依赖关系,将 Bean 的定义和实现合二为一。这种方式减少了大量的 XML 配置,使得代码更加简洁,开发效率更高,实现了所谓的 “零配置”(或者说减少了显式配置)。

相关

ApplicationContext 接口,工厂的接口,使用该接口可以获取到具体的 Bean 对象。该接口下有两个具体的实现类。

ClassPathXmlApplicationContext,加载类路径下的 Spring 配置文件。(注:ClassPathXmlApplicationContext在读取application.xml文件时,就会自动创建bean实例)

FileSystemXmlApplicationContext,加载本地磁盘下的 Spring 配置文件。

Spring 框架的 Bean管理的配置文件方式

  1. id 属性

id 属性为每个 Bean 提供了一个唯一的标识符。

  1. class 属性

class 属性指定了 Bean 对象的全限定类名。在这个例子中,com.example.service.UserService 就是 UserService 类的完整路径,Spring 容器会根据这个路径来创建相应的 Bean 实例。

  1. scope 属性
  • singleton(单例):这是 scope 属性的默认值。当 scope 设置为 singleton 时,Spring 容器只会创建该 Bean 的一个实例,并且在整个应用程序的生命周期中都使用这个实例。
  • prototype(多例):当 scope 设置为 prototype 时,每次从 Spring 容器中获取该 Bean 时,容器都会创建一个新的实例。
  1. init - methoddestroy - method 属性
  • init - method:当 Bean 被加载到 Spring 容器时,会调用 init - method 属性指定的方法。
  • destroy - method:当 Bean 从 Spring 容器中移除时,会调用 destroy - method 属性指定的方法。
    <!-- 定义一个单例的 UserService Bean -->
    <bean id="userServiceSingleton" class="com.example.service.UserService"
          scope="singleton"
          init-method="init"
          destroy-method="destroy">
    </bean>

    <!-- 定义一个多例的 UserService Bean -->
    <bean id="userServicePrototype" class="com.example.service.UserService"
          scope="prototype">
    </bean>
-----------------测试------------------
public class UserService {

    // 初始化方法
    public void init() {
        System.out.println("UserService 初始化");
    }

    // 销毁方法
    public void destroy() {
        System.out.println("UserService 销毁");
    }
}

实例化Bean的三种方式

无参构造器实例化

这是 Spring 中最基本的实例化 Bean 的方式,Spring 容器会调用 Bean 类的无参构造器(如果没有指定其他构造器)或者指定的有参构造器来创建 Bean 对象。

public class UserService {
    public UserService() {
        System.out.println("UserService实例化");
    }
}
-------------application.xml--------------
<bean id="userService" class="com.luxiya.demo.UserService"/>
-------------测试类--------------
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");

静态工厂方法实例化

使用一个静态工厂方法来创建 Bean 对象。静态工厂方法是类中的一个静态方法,该方法会返回一个对象实例。Spring 容器会调用这个静态工厂方法来获取 Bean 对象。

public class StaticFactory {
    public static UserService getUserService()
    {
        System.out.println("静态工厂创建");
        return new UserService();
    }
}
-------------application.xml--------------
    <!-- 静态工厂-->
    <bean id="uSstatic" class="com.luxiya.demo.StaticFactory" factory-method="getUserService"/>
-------------测试类--------------
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("uSstatic");

实例工厂方法实例化

使用一个实例工厂的方法来创建 Bean 对象。首先需要创建一个工厂类的实例,然后调用该实例的方法来创建 Bean 对象。

-------------application.xml--------------
<!--动态工厂-->
<bean id="dFactory" class="com.luxiya.demo.DFactory"/>
<bean id="useDF" factory-bean="dFactory" factory-method="getUserService"/>
-------------测试类--------------
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("useDF");

静态和动态对比

  • 静态工厂:静态工厂方法相对固定,因为它是类的静态方法,不能根据不同的实例状态进行不同的处理。通常适用于创建一些通用的、不依赖于实例状态的对象。不依赖于工厂类的实例。
  • 动态工厂:动态工厂方法可以根据工厂类实例的不同状态来创建不同的 Bean 对象,具有更高的灵活性。例如,工厂类可以有不同的属性值,根据这些属性值在实例工厂方法中创建不同类型或配置的 Bean 对象。依赖于工厂类的实例。

注解

常用注解

@Component 普通的类

@Controller 表现层@Service 业务层

@Repository 持久层

依赖注入常用的注解

@Value 用于注入普通类型(String,int,double 等类型)

@Autowired 默认按类型进行自动装配(引用类型)

@Qualifier 和@Autowired 一起使用,强制使用名称注入

@Resource Java 提供的注解,也被支持。使用 name 属性,按名称注入

对象生命周期(作用范围)注解

@Scope 生命周期注解,取值 singleton(默认值,单实例)和 prototype(多

例)

初始化方法和销毁方法注解(了解)

@PostConstruct 相当于 init-method

@PreDestroy 相当于 destroy-method

@Service
public class TeacherService {
    public TeacherService() {
        System.out.println("TeacherService初始化");
    }
}

-----------application.xml----------
<!--开启注解扫描-->
<context:component-scan base-package="com.luxiya.demo"/>
-----------------测试类--------------
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
TeacherService teacherService = (TeacherService) context.getBean("teacherService");

纯注解

纯注解的方式是微服务架构开发的主要方式,所以也是非常的重要。纯注解的目的是替换掉所有的配置文件。但是需要编写配置类。

常用的注解总结

@Configuration 声明是配置类

@ComponentScan 扫描具体包结构的。也可以扫描多个包@ComponentScan(value ={“com.qcbyjy.demo4”,“com.qcbyjy.demo3”})

@Import 注解 Spring 的配置文件可以分成多个配置的,编写多个配置类。用于导入其他配置类

@Bean 注解 只能写在方法上,表明使用此方法创建一个对象,对象创建完成保存到 IOC 容器中

------------demo包下的配置类--------------
@Configuration
@ComponentScan(basePackages = "com.luxiya.demo")
@Import(SpringConfig2.class)
public class SpringCofig {
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///spring_db");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
}
-------------------基础测试--------------
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
        Student student = (Student) context.getBean("student");
-------------demo2包下的配置类----------------
@Configuration
@ComponentScan(basePackages = "com.luxiya.demo2")
public class SpringConfig2 {
}
//demo2包下
@Data
@Component
public class Course {
    @Value("java")
    private String name;

    public Course()
    {
        System.out.println("Course初始化");
    }
}
//demo包下
@Component
@Data
public class Student {
    @Value("luxiya")
    private String name;

    public Student() {
        System.out.println("Student实例化");
    }
}
------------测试@Import注解-------------
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
        Course course = (Course) context.getBean("course");
        
------------测试@Bean注解-------------

                ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
//        Student student = (Student) context.getBean("student");
//        Course course = (Course) context.getBean("course");
        // 从应用上下文中获取 DataSource 对象
        DataSource dataSource = context.getBean("dataSource", DataSource.class);

        // 验证 DataSource 对象是否为 DruidDataSource 类型
        if (dataSource instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            System.out.println("Druid DataSource 配置信息:");
            System.out.println("Driver Class Name: " + druidDataSource.getDriverClassName());
            System.out.println("URL: " + druidDataSource.getUrl());
            System.out.println("Username: " + druidDataSource.getUsername());
            System.out.println("Password: " + druidDataSource.getPassword());
        }

其它问题

为什么p 命名空间方式需要无参构造

原因:在 Spring 中,当你使用属性注入(如你给出的 p 命名空间方式)时,Spring 容器在创建 Bean 实例的过程中,默认情况下会尝试使用无参构造函数来实例化对象,之后再通过 setter 方法进行属性注入。

Spring 容器在创建 Bean 实例时,主要有以下几个步骤:

  1. 实例化对象:Spring 需要先创建 Bean 对应的 Java 对象实例,一般情况下,它会优先尝试使用无参构造函数来创建对象。这是因为无参构造函数可以简单直接地创建对象,不需要额外的参数信息。
  2. 属性注入:在对象实例创建完成后,Spring 会根据配置(如 p 命名空间指定的属性值)调用对象的 setter 方法将属性值注入到对象中。

所以,当你使用 p 命名空间进行属性注入时,Spring 首先会尝试调用无参构造函数来创建 Teacher 对象,若类中没有无参构造函数,就会抛出 No default constructor found 这样的异常。


http://www.kler.cn/a/577782.html

相关文章:

  • 基于自定义Tomcat实现资源访问的完整指南
  • 探索React:构建现代前端应用的强大框架
  • 1-1 驱动开发HelloWorld
  • 代码托管平台对比分析:Gitee与GitLab
  • SpringBoot-模拟SSE对话交互
  • 如何在PHP中实现有效的日志记录
  • git在cmd的操作
  • 使用DeepSeek + Python开发AI思维导图应用,非常强!
  • winform基于antdui中table控件的使用
  • 蓝桥杯备考:bfs之马的遍历
  • Android AudioFlinger(一)——初识AndroidAudio Flinger
  • Vite 6 升级指南:CJS 和 ESM 的爱恨情仇
  • 单例设计模式---懒汉式--线程安全和不安全、枚举类
  • 用AI学编程4——swift学习1
  • Mac软件卸载后启动台图标还在
  • 基于深度学习的恶意软件检测系统:设计与实现
  • springmvc_view介绍
  • 4、STL的deque使用方法
  • SpringBoot知识点及其源码解析(3)
  • 华为eNSP:实验 OSPF单区域