Spring IOC

控制反转是一种编程的思想,被动的接收对象,把创建对象的的权利交spring框架,这个是spring框架的主要特征。它包括依依赖注入(Dependency Injection 简称DI)和依赖查找(Dependency Lookup)。
注意:依赖注入和依赖查找是 Spring 控制反转的一种实现,控制反转思想的实现也不仅这一种方式。
所以IOC的主要功能就是削减程序的耦合性(因为程序中不用主动的去创建对象了)

  1. <!-- 交给容器即可 -->
  2. <bean id="userService" class="cn.com.lichenghao.service.impl.UserServieImpl"></bean>

IOC底层原理

IOC 底层就是个对象工厂,主要依赖 xml 解析、工厂模式、反射去完成解耦操作。
第一步:创建XML文件文件

  1. <bean id="userService" class="cn.com.lichenghao.service.impl.UserServieImpl"></bean>

第二步:工厂类解析 xml,利用反射生成对象返回

  1. // 伪代码
  2. public Class BeanFactory(){
  3. public static Object getBean(){
  4. // 解析xml,得到 cn.com.lichenghao.service.impl.UserServieImpl
  5. // 利用反射得到对象返回
  6. }
  7. }

见:Spring5.2 源码学习 - IOC容器

IOC容器的两个主要的接口

IOC原理是使用工厂,那么Spring有两个主要的工厂接口:BeanFactory、ApplicationContext.

BeanFactory

Spring内部使用的接口,是IOC容器的基本实现,一般不提供给开发人员使用

ApplicationContext

是BeanFactory的子接口,一般开发使用这个接口,有三个主要的实现类

  1. //读取类路径下配置文件
  2. ApplicationContext ac =
  3. new ClassPathXmlApplicationContext("beans.xml");
  4. UserService userService = (UserService) ac.getBean("userService");
  5. userService.sayHi("Tom");
  6. //读取磁盘上的配置文件
  7. FileSystemXmlApplicationContext fileSystemXmlApplicationContext =
  8. new FileSystemXmlApplicationContext("/User/local/beans.xml");
  9. UserService userService1 = (UserService) fileSystemXmlApplicationContext.getBean("userService");
  10. userService1.sayHi("Tom1");
  11. //扫描带注解的
  12. AnnotationConfigApplicationContext annotationConfigApplicationContext =
  13. new AnnotationConfigApplicationContext("cn.com.lichenghao.service");
  14. UserService userService2 = (UserService) annotationConfigApplicationContext.getBean("userService");

两个接口的区别?

核心容器中的接口 BeanFactory 和 ApplicationContext 的区别 ?( 面试题 )
ApplicationContext 创建对象是使用立即加载的方式,也就是说加载完配置文件,对象就已经创建好了,适用于单例模式。
BeanFactory 创建对象是懒加载的方式,只有在使用 bean 的时候才会创建对象。

IOC bean管理

bean管理的两个操作创建bean对象注入对象的属性 两个操作又有两种方式,xml方式注解的方式

XML方式

创建bean的方式

默认构造函数

没有额外的属性和标签,走默认的构造函数,如果没有默认的构造函数就会报错

  1. <bean id="userService" class="cn.com.lichenghao.service.impl.UserServieImpl"></bean>

如果么有默认的构造函数报错:

  1. Caused by: org.springframework.beans.BeanInstantiationException:
  2. Failed to instantiate [cn.com.lichenghao.service.impl.UserServieImpl]:
  3. No default constructor found; nested exception is java.lang.NoSuchMethodException:
  4. cn.com.lichenghao.service.impl.UserServieImpl.<init>()

工厂模式

  1. public class UserFactory {
  2. public UserServieImpl instance() {
  3. return new UserServieImpl();
  4. }
  5. }
  1. <!-- 工厂 -->
  2. <bean id="userFactory" class="cn.com.lichenghao.factory.UserFactory"></bean>
  3. <bean id="userService1" factory-bean="userFactory" factory-method="instance"></bean>

静态工厂模式

  1. public class StaticUserFactory {
  2. public static UserServieImpl instance() {
  3. return new UserServieImpl();
  4. }
  5. }
  1. <!-- 静态工厂 -->
  2. <bean id="userService2" class="cn.com.lichenghao.factory.StaticUserFactory" factory-method="instance"></bean>

属性依赖注入(DI)

在IOC创建完对象之后,才能进行DI,属性的注入
能注入的三类数据:基本数据类型、String,引用类型,复杂类型/集合类型
注入方式:构造函数,set方法

set方法注入

  1. <bean id="user" class="cn.com.lichenghao.model.User">
  2. <property name="userName" value="张无忌"></property>
  3. </bean>

有参构造注入

  1. <!-- 根据属性的名称 -->
  2. <bean id="user" class="cn.com.lichenghao.model.User">
  3. <constructor-arg name="userName" value="张无忌"></constructor-arg>
  4. </bean>
  5. <!-- 根据属性的位置 -->
  6. <bean id="user" class="cn.com.lichenghao.model.User">
  7. <constructor-arg index="0" value="张无忌"></constructor-arg>
  8. </bean>
  9. <!-- 根据属性的位置和名称 -->
  10. <bean id="pig" class="cn.lichenghao.createBeanInstance.entity.Pig">
  11. <constructor-arg name="id" value="红猪"></constructor-arg>
  12. <constructor-arg index="1" value="222"></constructor-arg>
  13. <constructor-arg index="2" value="222"></constructor-arg>
  14. <constructor-arg index="3" value="222"></constructor-arg>
  15. </bean>
  16. <!-- 根据属性的位置和名称 -->
  17. <bean id="pig" class="cn.lichenghao.createBeanInstance.entity.Pig">
  18. <constructor-arg index="0" value="222"></constructor-arg>
  19. <constructor-arg index="1" value="222"></constructor-arg>
  20. <constructor-arg name="age" value="111"></constructor-arg>
  21. </bean>

其他属性的注入

字面量注入, 注入空值和特殊符号
注入null

  1. <bean id="user" class="cn.com.lichenghao.model.User">
  2. <property name="userName">
  3. <null/>
  4. </property>
  5. </bean>

注入特殊符号

  1. <!-- <![CDATA[带有特殊符号的内容]]> -->
  2. <bean id="user" class="cn.com.lichenghao.model.User">
  3. <property name="userName">
  4. <value><![CDATA[《张无忌》]]></value>
  5. </property>
  6. </bean>

注入外部的bean

  1. <bean id="userRepository" class="cn.com.lichenghao.dao.UserRepository"></bean>
  2. <bean id="user" class="cn.com.lichenghao.service.UserService">
  3. <property name="userDao" ref="userRepository"></property>
  4. </bean>

注入内部bean和级联赋值
有部门类和员工类,部分和员工的关系,一对多
内部bean注入

  1. <bean id="emp" class="cn.com.lichenghao.model.Emp">
  2. <property name="userName" value="张无忌"></property>
  3. <property name="gender" value="man"></property>
  4. <property name="dept">
  5. <bean id="dept" class="cn.com.lichenghao.model.Dept">
  6. <property name="name" value="技术部"></property>
  7. </bean>
  8. </property>
  9. </bean>

级联注入

  1. <bean id="emp" class="cn.com.lichenghao.model.Emp">
  2. <property name="userName" value="张无忌"></property>
  3. <property name="gender" value="man"></property>
  4. <property name="dept" ref="dept"></property>
  5. <property name="dept.name" value="技术部"></property>
  6. </bean>
  7. <bean id="dept" class="cn.com.lichenghao.model.Dept"></bean>

注入复杂类型

  1. public class User {
  2. private String[] strings;
  3. private List<String> list;
  4. private Map<String, String> map;
  5. private Properties properties;
  6. ......
  7. }
  1. <bean id="user" class="cn.com.lichenghao.model.User">
  2. <property name="strings">
  3. <array>
  4. <value>1</value>
  5. <value>2</value>
  6. </array>
  7. </property>
  8. <property name="list">
  9. <list>
  10. <value>1</value>
  11. <value>2</value>
  12. </list>
  13. </property>
  14. <property name="map">
  15. <map>
  16. <entry key="m1" value="m1"></entry>
  17. <entry key="m2">
  18. <value>m2</value>
  19. </entry>
  20. </map>
  21. </property>
  22. <property name="properties">
  23. <props>
  24. <prop key="p1">p1</prop>
  25. <prop key="p2">p2</prop>
  26. </props>
  27. </property>
  28. </bean>

注解的方式

开启注解扫描:

  1. <!-- 多个包逗号,隔开 -->
  2. <context:component-scan base-package="cn.com.lichenghao"></context:component-scan>

扫描过滤

  1. <!-- 只扫描@Controller注解 -->
  2. <context:component-scan base-package="cn.com.lichenghao" use-default-filters="false">
  3. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller">
  4. </context:component-scan>
  5. <!-- 不扫描@Controller注解 -->
  6. <context:component-scan base-package="cn.com.lichenghao" use-default-filters="false">
  7. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller">
  8. </context:component-scan>

使用注解创建对象
@Component @Service @Controller @Repository
使用注解注入属性
@AutoWired @Qualifer @Resource

bean的类型

Spring 有两种类型的 bean:

  1. 普通 bean: 定义什么bean就返回什么bean;
  2. 工厂 bean: 定义的类型和返回的类型可以是不一样的 (实现FactoryBean 接口,自己指定返回什么样类型的bean)。