Spring之IOC

二、IOC 容器

2.1 IOC 底层原理

概念和原理

  1. 什么是IOC?

    • 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
    • 使用IOC目的:为了耦合度降低
    • 人门案例就是IOC实现
  2. IOC底层原理

    • xml解析、工厂模式、放射
  3. 图解
    在这里插入图片描述在这里插入图片描述IOC过程
    在这里插入图片描述### 2.2 IOC接口(BeanFactory)

  4. IOC 思想基于IOC 容器完成,IOC 容器底层就是对象工厂

  5. Spring 提供IOC 容器实现两种方式:(两个接口)

    • BeanFactory: IOC容器基本实现,是Spring内部使用的,不提供给开发人员进行使用

      ​ *加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

    • ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

      ​ *加载配置文件时候就会把在配置文件对象进行创建

  6. ApplicationContext接口有实现类:

    CTRL + h: 打开类结构
    在这里插入图片描述主要的两个实现类:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext

    2.3 IOC 操作Bean管理层(基于xml)

2.3.1 IOC操作Bean 管理(概念)

  1. 什么是Bean管理

​ 指两个操作(如下)

  • Spring创建对象
  • Spring注入属性

2.Bean管理操作有两种方式:

  • 基于xml配置文件方式实现
  • 基于注解方式实现

2.3.2 IOC 操作Bean管理(基于xml方式)

  1. 基于xml方式创建对象

    在这里插入图片描述1. - 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

    • 在bean标签有很多属性,常用属性:

      *id属性:唯一标识

      *class属性:类全路径(包类路径)

    • 创建对象时候,默认也是执行无参数构造方法完成对象创建

  2. 基于xml方式注入属性

    1. DI:依赖注入,就是注入属性

      • 第一种注入方式:使用set方法进行注入

        第一步

        public class Book1 {
            private String bname;
            private String bauthor;
            public void setBname(String bname) {
                this.bname = bname;
            }
            public void setBauthor(String bauthor) {
                this.bauthor = bauthor;
            }
        }
        

        第二步:在spring配置文件配置对象创建,配置属性注入

        <!--    set方法注入属性-->
            <bean id="book" class="com.hxl.Book1">
        <!--        property完成属性注入:name 类里面属性名称;value 向属性注入值-->
                <property name="bname" value="《悲惨的时间》"></property>
                <property name="bauthor" value="雨果"></property>
            </bean>
        
      • 第二种注入方式:使用有参数构造注入

        第一步

            private String bname;
            private String bauthor;
        //有参构造
            public Book1(String bname, String bauthor) {
                this.bname = bname;
                this.bauthor = bauthor;
            }
        

        第二步:

        <!--    有参构造-->
            <bean id="book" class="com.hxl.Book1">
               <constructor-arg name="bauthor" value="XxX"></constructor-arg>
                <constructor-arg name="bname" value="秘密花园"></constructor-arg>
            </bean>
        
      • 第三种注入方式:p名称空间注入(了解)

  3. 基于xml方式注入其他类型属性

    1. 特殊值

      • 字面量:空值
        在这里插入图片描述特殊符号
        在这里插入图片描述1. 1. 注入属性-外部bean

      • 第一步:创建两个类service类和dao类

        public interface UserDao {
            public void update();
        }
        
        public class UserImpl implements UserDao {
            @Override
            public void update() {
                System.out.println("Dao update ....");
            }
        }
        
      • 第二步:在service调用dao里面的方法

      • import com.hxl.dao.UserDao;
        public class UserService {
        //    创建UserDao类型属性,生成set方法
            private UserDao userDao;
            public void setUserDao(UserDao userDao) {
                this.userDao = userDao;
            }
        
            public void add(){
                System.out.println("Service add....");
                userDao.update();
            }
        }
        
      • 第三步:在spring配置文件中进行配置

        <!--    1、service和dao对象创建-->
            <bean id="userService" class="com.hxl.service.UserService">
        <!--        注入userDao对象
                    name属性:类里面属性名称
                    ref属性:创建userDao对象bean标签id值-->
                <property name="userDao" ref="userDaoImpl"></property>
            </bean>
            <bean id="userDaoImpl" class="com.hxl.dao.UserImpl"></bean>
        
    2. 注入属性-内部bean

      • 一对多关系:部门与员工

      • 在实体类中表示一对多的关系

        public class Dept {
            private String dname;
            public void setDname(String dname) {
                this.dname = dname;
            }
            @Override
            public String toString() {
                return "Dept{" +
                        "dname='" + dname + '\'' +
                        '}';
            }
        }
        
        public class Emp {
            private String ename;
            private String gender;
            private Dept dept;
        
            public void setEname(String ename) {
                this.ename = ename;
            }
            public void setGender(String gender) {
                this.gender = gender;
            }
            public void setDept(Dept dept) {
                this.dept = dept;
            }
        
            public void add(){
                System.out.println(ename+"::"+gender+"::"+dept);
            }
        }
        
      • 在spring配置文件中进行配置

        <bean id="Emp" class="com.hxl.Emp">
                <property name="ename" value="LJW"></property>
                <property name="gender" value=""></property>
                <property name="dept">
                    <bean id="dept" class="com.hxl.Dept">
                        <property name="dname" value="人事部"></property>
                    </bean>
                </property>
        </bean>
        
    3. 注入属性-级联赋值

      • 第一种写法:与内部bean的区别是xml配置不太一样

        <bean id="Emp" class="com.hxl.Emp">
                <property name="ename" value="LJW"></property>
                <property name="gender" value=""></property>
                <property name="dept" ref="dept"></property>
            </bean>
            <bean id="dept" class="com.hxl.Dept">
                <property name="dname" value="人事部"></property>
            </bean>
        
      • 第二种:

        //添加get的方法 
         private Dept dept;
            public Dept getDept() {
                return dept;
            }
            
           <bean id="Emp" class="com.hxl.Emp">
                <property name="ename" value="LJW"></property>
                <property name="gender" value=""></property>
                <property name="dept" ref="dept"></property>
                <property name="dept.dname" value="技术部"></property>
            </bean>
            <bean id="dept" class="com.hxl.Dept"></bean> 
        
  4. IOC操作Bean管理(xml注入集合属性)

    1. 注入数组类型属性、List集合类型属性、Map集合类型属性

      • 注入数组、List、Map、Set等集合类型属性

        第一步:创建对象

        package com.hxl.collectiontype;
        
        import java.util.List;
        import java.util.Map;
        import java.util.Set;
        
        public class Student {
        //    1.数组类型属性
            private String[] courses;
        //    2.List集合类型属性
            private List<String> lists;
        //    3.Map集合类型属性
            private Map<String, String> maps;
        //    4.set集合类型属性
            private Set<String> sets;
        
            public void setCourses(String[] courses) {
                this.courses = courses;
            }
        
            public void setLists(List[] lists) {
                this.lists = lists;
            }
        
            public void setMaps(Map<String, String> maps) {
                this.maps = maps;
            }
        
            public void setSets(Set<String> sets) {
                this.sets = sets;
            }
        }
        

        第二步:Spring的xml配置

            <bean id="student" class="com.hxl.collectiontype.Student">
                <property name="courses">
                    <array>
                        <value>JavaWeb</value>
                        <value>数据库</value>
                        <value>Java SE</value>
                    </array>
                </property>
                <property name="lists">
                    <list>
                        <value>Lily</value>
                        <value>Candy</value>
                        <value>Mike</value>
                    </list>
                </property>
                <property name="maps">
                    <map>
                        <entry key="JSE" value="JavaSE"></entry>
                        <entry key="JAVA" value="JavaWeb"></entry>
                    </map>
                </property>
                <property name="sets">
                    <set>
                        <value>MySQL</value>
                        <value>Redis</value>
                        <value>Go</value>
                    </set>
                </property>
            </bean>
        
    2. 在集合里面设置对象类型属性

      • 案例代码如下:

        //创建课程实体类
        public class Course {
            private String cname;
            public void setCname(String cname) {
                this.cname = cname;
            }
            @Override
            public String toString() {
                return "Course{" +
                        "cname='" + cname + '\'' +
                        '}';
            }
        }
        
        // 在学生类中
        //    5.学生选了多门课程
            private List<Course> courseList;
        
            public void setCourseList(List<Course> courseList) {
                this.courseList = courseList;
            }
        
        // 在xml中
           <bean>
         	  <property name="courseList">
                    <list>
                        <ref bean="course1"></ref>
                        <ref bean="course2"></ref>
                    </list>
                </property>
             </bean>
               
            <bean id="course1" class="com.hxl.collectiontype.Course">
                <property name="cname" value="SpringBoot"></property>
            </bean>
            <bean id="course2" class="com.hxl.collectiontype.Course">
                <property name="cname" value="Spring5"></property>
            </bean>
        
        1. 把集合注入部分提取出来
      • 第一步:在spring配置文件中引入名称空间util

        //创建实体类
        import java.util.List;
        public class Book {
            private List<String> booklist;
            public void setBooklist(List<String> booklist) {
                this.booklist = booklist;
            }
            public void test(){
                System.out.println(booklist);
            }
        }
        
        //xml注入
        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:p="http://www.springframework.org/schema/p"
               xmlns:util="http://www.springframework.org/schema/util"
               xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
        
        
      • 第二步:使用util标签完成list集合注入提取

        <!--1. 提取list集合类型属性注入-->
            <util:list id="booklist">
                <value>SBCD</value>
                <value>SBCD</value>
                <value>SBCD</value>
            </util:list>
        <!--2.提取list集合类型属性注入使用-->
            <bean id="book" class="com.hxl.collectiontype.Book">
                <property name="booklist" ref="booklist"></property>
            </bean>
        
  5. IOC操作Bean管理(FactoryBean)

    1. bean有两种:普通bean;工厂bean(FactoryBean)

      1. 普通bean:在配置文件中定义bean类型就是返回类型

      2. 工厂bean:在配置文件中定义bean类型可以返回类型不一样

        • 第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean

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

          //类
          import org.springframework.beans.factory.FactoryBean;
          public class MyBean implements FactoryBean<Course> {
          
          //    定义返回bean
              @Override
              public Course getObject() throws Exception {
                  Course course = new Course();
                  course.setCname("abc");
                  return course;
              }
              @Override
              public Class<?> getObjectType() {
                  return null;
              }
              @Override
              public boolean isSingleton() {
                  return FactoryBean.super.isSingleton();
              }
          }
          //xml
          <bean id="myBean" class="com.hxl.collectiontype.MyBean"></bean>
              
           //测试
              @Test
              public void test3(){
                  ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
                  Course course = context.getBean("myBean",Course.class);
                  System.out.println(course);
              }
          
  6. IOC操作Bean管理(作用域)

    1. 在spring里面,设置创建bean实例是单实例还是多实例

      • 在spring里面,默认情况下,bean是单实例对象
              Book book = context.getBean("book",Book.class);
              Book book1 = context.getBean("book",Book.class);
              System.out.println(book);
              System.out.println(book1);
      

      地址一样,证明默认单实例对象。

    2. 如何设置单实例还是多实例对象

      • 在spring配置文件bean标签里面有属性(scope)用于单实例还是多实例

      • scope属性值:

        第一个值 默认值,singleton,表示单实例对象

        第二个值 prototype,表示是多实例对象

         <bean id="book" class="com.hxl.collectiontype.Book" scope="prototype">
                <property name="booklist" ref="booklist"></property>
            </bean>
        

        地址不一样,证明为多实例对象

      • singleton和prototype区别:

        第一、singleton单实例,prototype多实例

        第二、设置scope为singleton时,加载spring配置文件时会创建单实例对象

        ​ 设置scope为prototype时,是在调用getBean方法时创建多实例对象,不是在加载spring配置文件时创

      • request和session,不怎么使用

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

    1. 生命周期

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

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

      public class Order {
          private String oname;
      
          public Order() {
              System.out.println("第一步,无参构造");
          }
      
          public void setOname(String oname) {
              this.oname = oname;
              System.out.println("第二步: 调用set方法");
          }
      
          public void initMethod(){
              System.out.println("第三步: 初始化方法");
          }
      
          public void destroyMethod(){
              System.out.println("第五步: 调用销毁");
          }
      }
      
      
      <?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 https://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <bean id="order" class="com.hxl.beanlife.Order" init-method="initMethod" destroy-method="destroyMethod">
              <property name="oname" value="生命周期"></property>
          </bean>
      </beans>
      
      
       @Test
          public void test4(){
              ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
      
              Order order = context.getBean("order",Order.class);
              System.out.println("第四步: 获取对象");
              System.out.println(order);
      
      //        手动让bean实例销毁
              context.close();
          }
      

    4.bean后置处理器(更完整的生命周期,有7步)

    • 在第三步前后分别把bean实例传递bean后置处理器的方法

    • 演示添加后置处理器效果:

      • 创建类,实现接口BeanPostProcessor,创建后置处理器
      import org.springframework.beans.BeansException;
      import org.springframework.beans.factory.config.BeanPostProcessor;
      import org.springframework.lang.Nullable;
      
      public class MyBeanPost implements BeanPostProcessor {
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("在第三步初始化之前");
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("在第三步初始化之后");
              return bean;
          }
      }
      
      • xml配置后置处理器
      <bean id="myBeanPost" class="com.hxl.beanlife.MyBeanPost"></bean>
      
  8. IOC操作Bean管理(自动装配)

    1. 什么是自动装配

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

    2. 自动装配演示

      <!--    实现自动装配
              bean标签属性autowire,配置自动装配
              autowire属性常用两个属性:
                  byName根据属性名称注入,注入值bean的id值和类属性名称一样
                  byType根据属性类型注入-->
          <bean id="emp" class="com.hxl.Emp" autowire="byType">
      <!--        <property name="dept" ref="dept"></property>&lt;!&ndash;手动装配&ndash;&gt;-->
          </bean>
      <bean id="dept" class="com.hxl.Dept"></bean>
      
  9. IOC操作Bean管理(外部属性文件)

    1. 直接配置数据库信息

      • 配置德鲁伊连接池
      • 引入德鲁伊连接池依赖jar包
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid</artifactId>
                  <version>1.1.21</version>
              </dependency>
      
      <!--直接配置连接池-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
              <property name="url" value="jdbc:mysql://localhost:3306/user_db"></property>
              <property name="username" value="root"></property>
              <property name="password" value="tl123456"></property>
          </bean>
      
    2. 引入外部属性文件配置数据库连接池

      • 创建外部属性文件,properties格式文件,写入数据库信息
      prop.driverClass=com.mysql.jdbc.Driver
      prop.url=jdbc:mysql://localhost:3306/user_db
      prop.userName=root
      prop.password=tl123456
      
      • 把外部properties属性文件引入到spring配置文件中

        引入context名称空间

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
                                   http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        
      • 在spring配置文件使用标签引入外部属性文件

        <!--    引入外部属性文件-->
            <context:property-placeholder location="classpath:jdbc.properties"/>
            <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${prop.driverClass}"></property>
                <property name="url" value="${prop.url}"></property>
                <property name="username" value="${prop.userName}"></property>
                <property name="password" value="${prop.password}"></property>
            </bean>
        

2.4 IOC 操作Bean管理(基于注解)

  1. 什么是注解

    • 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值…)
    • 使用注解,注解作用在类上面,方法上面,属性上面
    • 使用注解的目的:简化xml配置
  2. Spring针对Bean管理中创建对象提供的注解

    • @Component:普通注解,泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注

    • @Service:用于标注业务层组件,业务逻辑层,service层

    • @Controller:用于标注控制层组件(如struts中的action)

    • @Repository:于标注数据访问组件,即DAO组件.

      *上面四个注解功能一样,都可以用来创建bean实例

  3. 基于注解方式实现对象创建

    • 引入依赖

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>5.3.19</version>
      </dependency>
      
    • 开启组件扫描

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
                                 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
      <!--    开启组件扫描
                  1.如果扫描多个包,使用逗号隔开
                  2.扫描包上层目录-->
      <!--示例1	 use-default-filters="false"  表示现在不使用默认filter,用自己的filter
      		<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
      		context:include-filter  设置扫描哪些内容-->    
       <!--示例2  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
      	context:exclude-filter  设置哪些内容不扫描-->
          <context:component-scan base-package="com.hxl.zhujie.dao,com.hxl.zhujie.service"></context:component-scan>
          <context:component-scan base-package="com.hxl.zhujie"></context:component-scan>
      </beans>
      
    • 创建类,在类上面添加创建对象注解

      import org.springframework.stereotype.Component;
      
      //在注解里面value属性值可以忽略不行;默认值是类名称,首写字母小写
      @Component(value = "userService")//<bean id="userService" class="..."/> 这里@Controller等等都行
      public class UserService {
          public void add(){
              System.out.println("Service add......");
          }
      }
      
  4. 基于注解方式实现属性注入
    在这里插入图片描述@Autowired:根据属性类型进行自动装配
    在这里插入图片描述
    @Qualifier:根据属性名称进行注入
    在这里插入图片描述在这里插入图片描述

@Resource:可以根据类型注入,可以根据名称注入
![在这里插入图片描述](https://img-blog.csdnimg.cn/6f8e2c1c6c8c4b84bc814a1951e1fb4b.png#pic_center

@Value:注入普通类型属性

在这里插入图片描述完全注解开发

  1. 创建配置类,替代xml配置文件
    在这里插入图片描述

  2. 编写测试类
    在这里插入图片描述


版权声明:本文为weixin_47125289原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。