IOC概念和原理

什么是IOC

  1. 控制反转,把对象的创建和对象之间的调用,交给spring进行管理
  2. 使用IOC的目的:为了降低耦合
  3. 做入门案例就是IOC实现

    IOC底层原理

  4. xml解析,工厂模式,反射

通过图片了解IOC底层原理
图1.png
弊端:耦合度太高了
图2.png
目的:耦合度降低最低限度

IOC过程

图3.png
图4.png
进一步降低耦合度

IOC(接口)

  • IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
  • spring提供了IOC容器实现的两种方式:(两个接口)
    • BeanFactory
      • IOC容器基本实现,是spring内部的使用接口,不提供开发人员使用
      • 加载配置文件的时候不会创建对象,在获取对象或者使用对象的时候才去创建对象
    • ApplicationContext
      • BeanFactory接口的子接口,提供了更多更强大的功能,一般由开发人员使用
      • 在配置文件的时候就会把在配置文件中的对象进行创建,将耗时耗资源的事都交给服务器完成
  • ApplicationContext接口有实现类

图5.png
FileSystemXmlApplicationContext
调用的事磁盘路径
ClassPathXmlApplicationContext
调用的是路径

IOC操作Bean管理

什么是Bean管理

  • Bean管理指的是两个操作

    • 由spring创建对象
    • 由spring注入属性

      Bean管理操作有两种方式

      基于xml配置文件方式实现
      基于xml方式创建对象
      6.png
  • 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

  • 在bean标签有很多属性,介绍常用的属性
    • id:唯一标识
    • class:类的全路径(包和类的路径)
    • name:用法和id一样但是它可以添加特殊字符,一般不用
  • 创建对象时候,默认也是执行无参数构造方法完成对象创建

    基于xml方式注入属性

    DI:依赖注入,就是属性. DI是IOC的一种具体实现,它表示依赖注入就是注入属性
    第一种注入方式:使用set方法注入

  • 创建类,定义属性和对应的set方法

    1. public class User{
    2. private Integer age;
    3. private String name;
    4. public void setAge(Integer age){
    5. this.age = age;
    6. }
    7. public void setName(String name){
    8. this.name = name;
    9. }
    10. }
  • 在sprig配置文件配置对象创建配置属性注入

    1. <!--配置User对象创建-->
    2. <bean id="user" class="com.ranin.User">
    3. <!--set方法注入属性 使用property完成属性注入
    4. name:类里面属性的名称
    5. value:向属性注入的值
    6. -->
    7. <property name="age" value="18"/>
    8. </bean>

    第二种注入方式:使用有参构造注入

  • 创建类,定义属性,创建属性对应有参构造

    1. public class User{
    2. //属性
    3. private String name;
    4. private Integer age;
    5. //有参构造
    6. public User(String name,Integer age){
    7. this.name;
    8. this.age;
    9. }
    10. }
    11. <!--配置order对象创建-->
    12. <bean id="order" class="com.ranin.Order">
    13. <!--有参数构造注入属性-->
    14. <constructor-arg name="age" value="18"/>
    15. </bean>

    p名称空间注入

  • 使用P名称空间注入,可以简化基于xml配置方式 ```xml 第一步添加命名空间在配置文件中 xmlns:p=”http://www.springframework.org/schema/p“ 第二步进行属性注入,在bean标签内 类中要有set方法

  1. <a name="d0Z0E"></a>
  2. #### IOC操作Bean管理(xml注入其他类型属性)
  3. 字面量(例如int i = 5;赋上值)
  4. - null值
  5. ```xml
  6. <bean id="book" class="com.ranin.Book">
  7. <!--设置一个空值-->
  8. <property name="age">
  9. <null></null>
  10. </property>
  11. </bean>
  • 属性值包含特殊符号

    1. <!--属性设置特殊字符
    2. 1.把<>进行转义&lt;&gt;
    3. 2.把特殊符号的内容写到CDATA中
    4. -->
    5. <bean id="book" class="com.ranin.Book">
    6. <property name="name">
    7. <value>
    8. <![CDATA[<<南京>>]]>
    9. </value>
    10. </property>
    11. </bean>

    注入属性—外部bean

  • 创建两个类service类和dao类

  • 在service调用dao的方法
  • 在spring配置文件中进行配置
    1. <!--service和dao对象创建-->
    2. <bean id="userService" class="com.ranin.sevice.UserSevice">
    3. <!--注入userDao对象
    4. name属性值:类里面的属性名称
    5. ref属性:创建userDao对象bean标签id值
    6. -->
    7. <property name="userDao" ref="userDao"></property>
    8. </bean>
    9. <bean id="userDao" class="com.ranin.dao.impl.UserDaoImpl"></bean>
    注入属性-内部bean和级联赋值
    1.对多的关系:例如部门和员工
    2.在实体类之间表示一对多关系,员工表示所属部门,是用对象类型属性进行表示
    3.在spring配置文件中进行配置
    1. <!--内部bean-->
    2. <bean id="emp" class="com.ranin.bean.Emp">
    3. <!--先设置两个普通的属性-->
    4. <property name="ename" value="lucy"/>
    5. <property name="gender" value="女"/>
    6. <!--设置对象类型属性-->
    7. <property name="dept">
    8. <bean id="dept" class="com.ranin.bean.Dept">
    9. <property name="dname" value="kaifa"/>
    10. </bean>
    11. </property>
    12. </bean>
    注入属性-级联赋值
    第一种写法
    1. <!--级联赋值-->
    2. <bean id="emp" class="com.ranin.bean.Emp">
    3. <!--设置两个普通属性-->
    4. <property name="ename" value="lucy"/>
    5. <property name="gender" value="女"/>
    6. <!--级联赋值-->
    7. <property name="dept" ref="dept"/>
    8. </bean>
    9. <bean id="dept" class="com.ranin.bean.Dept">
    10. <property name="dname" value="财务"/>
    11. </bean>
    第二种写法 ```xml
  1. <a name="jCHmc"></a>
  2. #### IOC操作Bean管理(Xml注入集合属性)
  3. 注入数组类型属性<br />注入List集合类型属性<br />注入Map集合类型属性
  4. ```java
  5. public class Stu {
  6. //数组类型属性
  7. private String[] courses;
  8. //list集合属性
  9. private List<String> list;
  10. //map
  11. private Map<String,String> map;
  12. //set集合类型属性
  13. private Set<String> sets;
  14. public void setSets(Set<String> sets) {
  15. this.sets = sets;
  16. }
  17. public void setList(List<String> list) {
  18. this.list = list;
  19. }
  20. public void setMap(Map<String, String> map) {
  21. this.map = map;
  22. }
  23. public void setCourses(String[] courses) {
  24. this.courses = courses;
  25. }
  26. }

xml中

  1. <!--集合类型的属性注入-->
  2. <bean id="stu" class="com.ranin.collectiontype.Stu">
  3. <!--数组类型注入-->
  4. <property name="courses">
  5. <array>
  6. <value>"java"</value>
  7. <value>"python"</value>
  8. </array>
  9. </property>
  10. <!--list类型属性注入-->
  11. <property name="list">
  12. <list>
  13. <value>"list1"</value>
  14. <value>"list2"</value>
  15. </list>
  16. </property>
  17. <!--map-->
  18. <property name="map">
  19. <map>
  20. <entry key="java" value="222"/>
  21. <entry key="python" value="333"/>
  22. </map>
  23. </property>
  24. <!--set-->
  25. <property name="sets">
  26. <set>
  27. <value>"set1"</value>
  28. <value>"set2"</value>
  29. </set>
  30. </property>
  31. </bean>

在集合里面设置对象类型值

  1. <!--注入list集合类型,值时对象-->
  2. <property name="courseList">
  3. <list>
  4. <ref bean="course1"/>
  5. <ref bean="course2"/>
  6. </list>
  7. </property>
  8. </bean>
  9. <!--创建多个course对象-->
  10. <bean id="course1" class="com.ranin.collectiontype.Course">
  11. <property name="cname" value="spring5"/>
  12. </bean>
  13. <bean id="course2" class="com.ranin.collectiontype.Course">
  14. <property name="cname" value="course2"/>
  15. </bean>

把集合注入部分提取出来
在spring配置文件中引入名称空间util

  1. xmlns:util="http://www.springframework.org/schema/util"
  2. xsi:schemaLocation="http://www.springframework.org/schema/beans
  3. http://www.springframework.org/schema/beans/spring-beans.xsd
  4. http://www.springframework.org/schema/util
  5. http://www.springframework.org/schema/util/spring-util.xsd">

使用util标签完成list集合注入提取

  1. <!--提取list集合类型属性注入-->
  2. <util:list id="bookList">
  3. <value>"book1"</value>
  4. <value>"book2"</value>
  5. <value>"book3"</value>
  6. </util:list>
  7. <!--提取list集合类型属性注入使用-->
  8. <bean id="book" class="com.ranin.collectiontype.Book">
  9. <property name="list" ref="bookList"/>
  10. </bean>

IOC操作Bean管理(FactoryBean)

1.Spring有两种类型bean,一种普通bean另外一种工厂bean(FactoryBean)
2.普通bean:在配置文件中定义bean类型就是返回类型
3.工厂bean:在配置文件定义bean类型可以和返回类型不一样
第一步创建类,让这个类作为工厂bean,实现接口FactoryBean

  1. public class MyBean implements FactoryBean<Course> {
  2. public boolean isSingleton() {
  3. return false;
  4. }
  5. //定义返回bean
  6. public Course getObject() throws Exception {
  7. Course course = new Course();
  8. course.setCname("abc");
  9. return course;
  10. }
  11. public Class<?> getObjectType() {
  12. return null;
  13. }
  14. }jagva
  1. <bean id="myBean" class="com.ranin.factoryBean.MyBean">
  2. </bean>

第二步实现接口里面的方法,在实现的方法中定义返回的bean类型

  1. @Test
  2. public void test3(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
  4. Course myBean = context.getBean("myBean", Course.class);
  5. System.out.println(myBean);
  6. }

Ioc操作Bean管理(bean作用域)

1.在spring里面,设置创建bean实例是单实例还是多实例
2.在spring里面,默认情况下,bean是单实例对象

  1. @Test
  2. public void test2(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
  4. Book book = context.getBean("book",Book.class);
  5. Book book1 = context.getBean("book",Book.class);
  6. System.out.println(book);
  7. System.out.println(book1);
  8. //结果打印出来的地址都是同一个
  9. }

3.如何设置单实例还是多实例

  • 1.在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
  • scope属性值
  • 第一个值 默认值 singleton 表示单实例对象
  • 第二个值prototype表示是多实例对象

    1. <bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
    2. <property name="list" ref="bookList"/>
    3. </bean>
  • singleton和prototype区别

  • 第一singleton表示单实例,prototype多实例
  • 第二设置scope的值是singleton时候,加载spring配置文件时候就会创建单实例对象

    • 设置scope值是prototype时候,不是在加在spring配置文件的时候创建对象,在调用getBean方法时候创建多实例的对象

      IOC操作Bean管理(bean生命周期)

      1.生命周期
  • 从对象的创建到对象销毁的过程

2.bean的生命周期

  • 通过构造器创建bean实例(无参数构造)
  • 为bean的属性设置值和对其他bean的引用(调用set方法)
  • 调用bean的初始化方法(需要进行配置初始化的方法)
  • bean可以使用了(对象获取到了)
  • 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

3.加上bean的后置处理器,bean声明周期有七步

  • 通过构造器创建bean实例(无参数构造)
  • 为bean的属性设置值和对其他bean的引用(调用set方法)
  • 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
  • 调用bean的初始化方法(需要进行配置初始化的方法)
  • 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
  • bean可以使用(对象获取到)
  • 当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)

演示添加后置处理器效果
1.创建类,实现接口BeanPostProcessor,创建后置处理器

  1. public class MyBeanPost implements BeanPostProcessor {
  2. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  3. System.out.println("在初始化之前执行的方法");
  4. return bean;
  5. }
  6. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  7. System.out.println("在初始化执行之后的方法");
  8. return bean;
  9. }
  10. }

xml中

  1. <!--配置后置处理器-->
  2. <bean id="myBeanPost" class="com.ranin.bean.MyBeanPost"></bean>
  3. 在同一个xml中的所有bean都会执行这个配置后置处理器

IOC操作Bean管理(xml自动装配)

1.什么是自动装配

  • 根据指定装配规则(属性名称或者属性类型),Spring会自动将匹配的属性值进行注入

2.演示自动装配

  • 根据属性的名称自动注入
    1. public void test4(){
    2. ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    3. Emp emp = context.getBean("emp",Emp.class);
    4. System.out.println(emp);
    5. }
    ```xml

- 根据属性类型进行自动注入 测试代码都相同<br />但是要注意根据属性自动注入的话一个xml中同一个属性只能有一个bean不能有多个不然编译器无法xml

  1. <a name="VT2ak"></a>
  2. #### IOC操作Bean管理(引入外部的属性文件)
  3. 1.直接配置数据库的信息
  4. - 配置德鲁伊连接池
  5. - 引入德鲁伊连接池的jar包
  6. ```xml
  7. <dependency>
  8. <groupId>com.alibaba</groupId>
  9. <artifactId>druid</artifactId>
  10. <version>1.1.16</version>
  11. </dependency>
  1. <!--直接配置连接池-->
  2. <!--DruidDataSource dataSource = new DruidDataSource-->
  3. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  4. <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
  5. <property name="url" value="jdbc:mysql://47.93.182.140:3306/qingcheng_goods?serverTimezone=GMT%2B8&amp;useSSL=false"></property>
  6. <property name="username" value="root"></property>
  7. <property name="password" value="123456"></property>
  8. </bean>

2.引入外部的属性文件配置数据库连接板池

  • 创建一个外部属性文件,properties格式文件,写数据库信息

    1. prop.driverClass=com.mysql.cj.jdbc.Driver
    2. prop.url=jdbc:mysql://47.93.182.140:3306/qingcheng_goods?serverTimezone=GMT%2B8&useSSL=false
    3. prop.username=root
    4. prop.password=123456
  • 把外部properties属性文件引入到spring配置文件中

  • 引入context名称空间 ```xml xmlns:context=”http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

  1. - spring中的配置文件中使用标签引入外部属性文件
  2. ```xml
  3. <!--引入外部的属性文件-->
  4. <context:property-placeholder location="jdbc.properties"/>
  5. <!--配置连接池-->
  6. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  7. <property name="driverClassName" value="${prop.driverClass}"></property>
  8. <property name="url" value="${prop.url}"></property>
  9. <property name="username" value="${prop.username}"></property>
  10. <property name="password" value="${prop.password}"></property>
  11. </bean>

基于注解方式实现

1.什么是注解

  • 注解是代码特殊标记,格式为:@注解名称(属性名称=属性值,属性名称=属性值)
  • 使用注解,注解可以作用在类,方法,属性上面
  • 使用注解的目的:简化xml配置

2.Spring针对Bean管理中创建对象提供注解

  • @Component
  • @Service
  • @Controller
  • @Repository

上面四个注解功能是一样的,都可以用来创建bean实例
3.基于注解方式实现对象创建
第一步引入依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aop</artifactId>
  4. <version>5.2.8.RELEASE</version>
  5. </dependency>

第二步开启组件扫描spring配置文件中头部添加

  1. 第二步开启组件扫描spring配置文件中头部添加
  2. xmlns:context="http://www.springframework.org/schema/context"
  3. http://www.springframework.org/schema/context
  4. http://www.springframework.org/schema/context/spring-context.xsd
  1. <!--开启组件扫描
  2. 1.如果要扫描多个包,多个包之间用逗号隔开
  3. 2.扫描包上层目录
  4. -->
  5. <context:component-scan base-package="com.ranin"></context:component-scan>

第三步创建类,在类上添加创建对象注解

  1. //在注解里面value属性值可以省略不写
  2. //默认值是类名称,首字母小写
  3. //UserService --userService
  4. @Component(value = "userService") //<bean id="" class=""/>写法类似
  5. public class UserService {
  6. public void add(){
  7. System.out.println("service add......");
  8. }
  9. }

4.开启组件扫描细节配置

  1. <!--示例1
  2. use-default-filters="false"表示现在不适用默认的filter而是使用自己配置的filter,即不扫描默认规则自己配置规则
  3. context:include-filter type="annotation" 设置扫描那些内容
  4. expression="org.springframework.stereotype.Controller" 只扫描包里的controller注解
  5. -->
  6. <context:component-scan base-package="com.ranin" use-default-filters="false">
  7. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  8. </context:component-scan>
  9. <!--示例2
  10. 下面配置扫描包里的所有配置
  11. context:exclude-filter 设置那些不内容不进行扫描
  12. expression="org.springframework.stereotype.Controller 不扫描controller
  13. -->
  14. <context:component-scan base-package="com.ranin">
  15. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  16. </context:component-scan>

5.基于注解方式实现属性的注入

  • @Autowired:根据属性类型进行自动装配
    • 第一步把service和dao对象创建,在service和dao类添加创建对象
    • 第二步在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解
  • @Qualifier:根据属性的名称进行注入,和@Autowired一起使用
  • @Resource:可以根据类型注入,可以根据名称注入
  • @Value:注入普通类型属性
    1. @Value(value="abc")
    2. private String name;
    6.完全注解开发
    1.创建配置类,替代xml配置文件 ```java @Configuration//将当前类作为配置类,替代xml配置文件 @ComponentScan(basePackages = {“com.ranin”}) public class SpringConfig {

}

  1. 2.编写测试类
  2. ```java
  3. @Test
  4. public void testService2(){
  5. //加载配置类
  6. ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
  7. UserService userService = context.getBean("userService",UserService.class);
  8. System.out.println(userService);
  9. userService.add();
  10. }