spring IOC(实现原理)

  金牌会员 | 2025-3-9 15:33:57 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 971|帖子 971|积分 2913

依赖注入

**依赖注入(DI)**是Spring框架核心功能之一,它允许对象间的依赖关系通过配置或代码在运行时动态注入,而不是通过对象内部硬编码。这种方法淘汰了代码间的耦合,增加了灵活性和可重用性。
依赖注入强调的是对象的依赖关系由外部来管理和提供,而不是由对象自身负责创建依赖对象。通过这种方式,可以低沉对象之间的耦合度。即下面的方式也属于依赖注入。
常见的依赖注入方式


  • 构造函数注入:通过类的构造函数将依赖对象转达给该类。在对象实例化时,容器会调用相应的构造函数,并传入依赖对象的实例。
  1. public class ProductService {
  2.     private final ProductRepository productRepository;
  3.     public ProductService(ProductRepository productRepository) {
  4.         this.productRepository = productRepository;
  5.     }
  6.     // 业务方法
  7. }
复制代码


  • Setter 方法注入:利用类的 setter 方法将依赖对象设置到该类中。容器先创建对象实例,然后调用 setter 方法来注入依赖对象。
  1. public class UserService {
  2.     private UserRepository userRepository;
  3.     public void setUserRepository(UserRepository userRepository) {
  4.         this.userRepository = userRepository;
  5.     }
  6.     // 业务方法
  7. }
复制代码
xml依赖注入


  • <property>
  • <list><value></value></list>
  • <map><entry key=' ' value=' '></map>
  • <array><value></value></array>
  • <set><value></value></set>
  • <props><prop key=' '>值</prop></props>
  1. ---------------Student类------------------
  2. public class Student {
  3.     private String name;
  4.     private Adress address;
  5.     private String[] books;
  6.     private List<String> hobbies;
  7.     private Map<String,String> card;
  8.     private Set<String> games;
  9.     private String wife;
  10.     private Properties info;
  11.     //getter,setter,无参and全参构造
  12.     }
  13. ---------------Teacher类------------------
  14. public class Teacher {
  15.     private String name;
  16.     private int age;
  17.     //getter,setter,无参and全参构造
  18.     }
  19. ---------------applicationContext.xml------------------
  20.   <bean id="adress" class="com.luxiya.Adress">
  21.         <property name="address" value="空中花园"/>
  22.     </bean>
  23.    <!-- 需要相应的构造函数-->
  24.     <bean id="teacher" class="com.luxiya.Teacher" c:name="luxiya" c:age="18"/>
  25.     <!-- 需要相应的构造函数-->
  26.     <bean id="teacher1" class="com.luxiya.Teacher">
  27.         <constructor-arg name="name" value="luxiya-constructor"/>
  28.         <constructor-arg name="age" value="188"/>
  29.     </bean>
  30.     <!-- 需要空参构造-->
  31.     <bean id="teacher2" class="com.luxiya.Teacher" p:name="zhihuiguan" p:age="19">
  32.     </bean>
  33.     <bean id="student" class="com.luxiya.Student">
  34.         <property name="name" value="luxiya"/>
  35.         <property name="address" ref="adress"/>
  36.         <property name="books">
  37.             <array>
  38.                 <value>java</value>
  39.                 <value>python</value>
  40.             </array>
  41.         </property>
  42.         <property name="hobbies">
  43.             <list>
  44.                 <value>打篮球</value>
  45.                 <value>打游戏</value>
  46.             </list>
  47.         </property>
  48.         <property name="card">
  49.             <map>
  50.                 <entry key="身份证" value="123456789"/>
  51.                 <entry key="银行卡" value="123456789"/>
  52.             </map>
  53.         </property>
  54.         <property name="games">
  55.             <set>
  56.                 <value>LOL</value>
  57.                 <value>DOTA</value>
  58.             </set>
  59.         </property>
  60.         <property name="wife">
  61.             <null/>
  62.         </property>
  63.         <property name="info">
  64.             <props>
  65.                 <prop key="name">luxiya</prop>
  66.                 <prop key="age">18</prop>
  67.             </props>
  68.         </property>
  69.     </bean>
  70. ---------------测试类------------------
  71. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  72. Student student = (Student) context.getBean("student");
  73. System.out.println(student.toString());
  74. Teacher teacher = (Teacher) context.getBean("teacher");
  75. System.out.println(teacher.toString());
  76. Teacher teacher1 = (Teacher) context.getBean("teacher1");
  77. System.out.println(teacher1.toString());
  78. Teacher teacher2 = (Teacher) context.getBean("teacher2");
  79. 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管理的配置文件方式


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

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

  • scope 属性


  • singleton(单例):这是 scope 属性的默认值。当 scope 设置为 singleton 时,Spring 容器只会创建该 Bean 的一个实例,而且在整个应用程序的生命周期中都使用这个实例。
  • prototype(多例):当 scope 设置为 prototype 时,每次从 Spring 容器中获取该 Bean 时,容器都会创建一个新的实例。

  • init - method 和 destroy - method 属性


  • init - method:当 Bean 被加载到 Spring 容器时,会调用 init - method 属性指定的方法。
  • destroy - method:当 Bean 从 Spring 容器中移除时,会调用 destroy - method 属性指定的方法。
  1.     <!-- 定义一个单例的 UserService Bean -->
  2.     <bean id="userServiceSingleton" class="com.example.service.UserService"
  3.           scope="singleton"
  4.           init-method="init"
  5.           destroy-method="destroy">
  6.     </bean>
  7.     <!-- 定义一个多例的 UserService Bean -->
  8.     <bean id="userServicePrototype" class="com.example.service.UserService"
  9.           scope="prototype">
  10.     </bean>
  11. -----------------测试------------------
  12. public class UserService {
  13.     // 初始化方法
  14.     public void init() {
  15.         System.out.println("UserService 初始化");
  16.     }
  17.     // 销毁方法
  18.     public void destroy() {
  19.         System.out.println("UserService 销毁");
  20.     }
  21. }
复制代码
实例化Bean的三种方式

无参构造器实例化

这是 Spring 中最基本的实例化 Bean 的方式,Spring 容器会调用 Bean 类的无参构造器(假如没有指定其他构造器)或者指定的有参构造器来创建 Bean 对象。
  1. public class UserService {
  2.     public UserService() {
  3.         System.out.println("UserService实例化");
  4.     }
  5. }
  6. -------------application.xml--------------
  7. <bean id="userService" class="com.luxiya.demo.UserService"/>
  8. -------------测试类--------------
  9.         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  10.         UserService userService = (UserService) context.getBean("userService");
复制代码
静态工厂方法实例化

使用一个静态工厂方法来创建 Bean 对象。静态工厂方法是类中的一个静态方法,该方法会返回一个对象实例。Spring 容器会调用这个静态工厂方法来获取 Bean 对象。
  1. public class StaticFactory {
  2.     public static UserService getUserService()
  3.     {
  4.         System.out.println("静态工厂创建");
  5.         return new UserService();
  6.     }
  7. }
  8. -------------application.xml--------------
  9.     <!-- 静态工厂-->
  10.     <bean id="uSstatic" class="com.luxiya.demo.StaticFactory" factory-method="getUserService"/>
  11. -------------测试类--------------
  12.         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  13.         UserService userService = (UserService) context.getBean("uSstatic");
复制代码
实例工厂方法实例化

使用一个实例工厂的方法来创建 Bean 对象。起首需要创建一个工厂类的实例,然后调用该实例的方法来创建 Bean 对象。
  1. -------------application.xml--------------
  2. <!--动态工厂-->
  3. <bean id="dFactory" class="com.luxiya.demo.DFactory"/>
  4. <bean id="useDF" factory-bean="dFactory" factory-method="getUserService"/>
  5. -------------测试类--------------
  6. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  7. 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
  1. @Service
  2. public class TeacherService {
  3.     public TeacherService() {
  4.         System.out.println("TeacherService初始化");
  5.     }
  6. }
  7. -----------application.xml----------
  8. <!--开启注解扫描-->
  9. <context:component-scan base-package="com.luxiya.demo"/>
  10. -----------------测试类--------------
  11. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  12. TeacherService teacherService = (TeacherService) context.getBean("teacherService");
复制代码
纯注解

纯注解的方式是微服务架构开辟的主要方式,所以也黑白常的紧张。纯注解的目标是替换掉全部的配置文件。但是需要编写配置类。
常用的注解总结
@Configuration 声明是配置类
@ComponentScan 扫描详细包结构的。也可以扫描多个包@ComponentScan(value ={“com.qcbyjy.demo4”,“com.qcbyjy.demo3”})
@Import 注解 Spring 的配置文件可以分成多个配置的,编写多个配置类。用于导入其他配置类
@Bean 注解 只能写在方法上,表明使用此方法创建一个对象,对象创建完成保存到 IOC 容器中
  1. ------------demo包下的配置类--------------
  2. @Configuration
  3. @ComponentScan(basePackages = "com.luxiya.demo")
  4. @Import(SpringConfig2.class)
  5. public class SpringCofig {
  6.     @Bean(name="dataSource")
  7.     public DataSource createDataSource(){
  8.         DruidDataSource dataSource = new DruidDataSource();
  9.         dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  10.         dataSource.setUrl("jdbc:mysql:///spring_db");
  11.         dataSource.setUsername("root");
  12.         dataSource.setPassword("root");
  13.         return dataSource;
  14.     }
  15. }
  16. -------------------基础测试--------------
  17.         ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
  18.         Student student = (Student) context.getBean("student");
  19. -------------demo2包下的配置类----------------
  20. @Configuration
  21. @ComponentScan(basePackages = "com.luxiya.demo2")
  22. public class SpringConfig2 {
  23. }
  24. //demo2包下
  25. @Data
  26. @Component
  27. public class Course {
  28.     @Value("java")
  29.     private String name;
  30.     public Course()
  31.     {
  32.         System.out.println("Course初始化");
  33.     }
  34. }
  35. //demo包下
  36. @Component
  37. @Data
  38. public class Student {
  39.     @Value("luxiya")
  40.     private String name;
  41.     public Student() {
  42.         System.out.println("Student实例化");
  43.     }
  44. }
  45. ------------测试@Import注解-------------
  46.         ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
  47.         Course course = (Course) context.getBean("course");
  48.         
  49. ------------测试@Bean注解-------------
  50.                 ApplicationContext context = new AnnotationConfigApplicationContext(SpringCofig.class);
  51. //        Student student = (Student) context.getBean("student");
  52. //        Course course = (Course) context.getBean("course");
  53.         // 从应用上下文中获取 DataSource 对象
  54.         DataSource dataSource = context.getBean("dataSource", DataSource.class);
  55.         // 验证 DataSource 对象是否为 DruidDataSource 类型
  56.         if (dataSource instanceof DruidDataSource) {
  57.             DruidDataSource druidDataSource = (DruidDataSource) dataSource;
  58.             System.out.println("Druid DataSource 配置信息:");
  59.             System.out.println("Driver Class Name: " + druidDataSource.getDriverClassName());
  60.             System.out.println("URL: " + druidDataSource.getUrl());
  61.             System.out.println("Username: " + druidDataSource.getUsername());
  62.             System.out.println("Password: " + druidDataSource.getPassword());
  63.         }
复制代码
别的问题

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

缘故原由:在 Spring 中,当你使用属性注入(如你给出的 p 定名空间方式)时,Spring 容器在创建 Bean 实例的过程中,默认情况下会尝试使用无参构造函数来实例化对象,之后再通过 setter 方法举行属性注入。
Spring 容器在创建 Bean 实例时,主要有以下几个步调:

  • 实例化对象:Spring 需要先创建 Bean 对应的 Java 对象实例,一般情况下,它会优先尝试使用无参构造函数来创建对象。这是由于无参构造函数可以简单直接地创建对象,不需要额外的参数信息。
  • 属性注入:在对象实例创建完成后,Spring 会根据配置(如 p 定名空间指定的属性值)调用对象的 setter 方法将属性值注入到对象中。
所以,当你使用 p 定名空间举行属性注入时,Spring 起首会尝试调用无参构造函数来创建 Teacher 对象,若类中没有无参构造函数,就会抛出 No default constructor found 如许的异常。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表