IOC概念

100 阅读9分钟

IOC概念

  1. 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
  2. 使用IOC目的:为了耦合度降低
  3. Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系

IOC底层原理

底层原理 + 图解

  1. XML解析
  2. 工厂模式
  3. 反射机制

问题所在: 耦合度很高

工厂模式解决方案:这样还是存在对象与工厂对象之间存在耦合

IOC解决方案:将耦合度降低到最低限度:主要是利用XML解析、工厂模式和反射机制

IOC(BeanFactory接口)

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

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

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

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

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

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

  3. ApplicationContext接口有实现类

FileSystemXmlApplicitonContext: 用于其他位置的文件夹中的XML配置文件

ClassPathXmlApplicationContext: 用于当前项目的加载XML配置文件

还有其他扩展方法

IOC操作Bean管理

概念

  1. Bean管理指的是两个操作

    1. Spring创建对象
    2. Spring注入属性(getting 和 setting 方法)
  2. Bean管理操作有两种方式

    1. 基于xml配置文件方式实现
    2. 基于注解方式实现

基于XML

  1. 基于xml方式创建对象

    1. 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

    2. 在bean标签有很多属性,介绍常用的属性

      id属性:唯一标识

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

      name属性:与ID效果相似,相较于id 可以加入符号

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

<bean id="user" name="user" class="com.wxy.bean.User"></bean>
  1. 基于xml方式注入属性

    1. DI:依赖注入,就是注入属性
  2. 第一种注入方式:使用set方法进行注入

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

      public class Book { 
          private String name;
          private String author;
          
          public void setAuthor(String author) {
              this.author = author;
          }
          public void setName(String name) {
              this.name = name;
          }
      }
      
      public void testBook() {
          ApplicationContext applicationContext =
                  new ClassPathXmlApplicationContext("bean1.xml");
          Book book = applicationContext.getBean("book", Book.class);
          book.setName("wwwxxxxxxx");
          book.setAuthor("ww");
          System.out.println(book);
      }
      
      <bean id="book" name="book" class="com.wxy.bean.Book"></bean>
      
    2. 在spring配置文件配置对象创建,配置属性注入

      @Test
      public void testBook2() {
          ApplicationContext applicationContext =
                  new ClassPathXmlApplicationContext("bean1.xml");
          Book book = applicationContext.getBean("book", Book.class);
          System.out.println(book);
      }
      
      <bean id="book" name="book" class="com.wxy.bean.Book">
          <property name="name" value="xxxzzzz"></property>
          <property name="author" value="vv"></property>
      </bean>
      
  3. 第二种注入方式:使用有参数构造进行注入

    1. 创建类,定义属性,创建属性对应有参数构造方法

      public Book(String name, String author) {
          this.name = name;
          this.author = author;
      }
      
    2. 在spring配置文件中进行配置

      @Test
      public void testBook4() {
          ApplicationContext applicationContext =
                  new ClassPathXmlApplicationContext("bean1.xml");
          Book book = applicationContext.getBean("book", Book.class);
          System.out.println(book);
      }
      
      <bean id="book" class="com.wxy.bean.Book">
          <constructor-arg name="name" value="zzzzzzxxxx"></constructor-arg>
          <constructor-arg name="author" value="zz"></constructor-arg>
      </bean>
      
  4. p名称空间注入(了解)

    1. 使用p名称空间注入,可以简化基于xml配置方式

      1. 第一步 添加p名称空间在配置文件中

        <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"
               xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
            
        
      2. 第二步 进行属性注入,在bean标签里面进行操作

        <bean id="book" class="com.wxy.bean.Book" p:bname="zxczxczxc" p:bauthor="aa"></bean>
        

xml注入其他类型属性

  1. 字面量

    1. null值

      <property name="author">
              <null/>
      </property>
      
    2. 属性值包含特殊符号

      <!--属性值包含特殊符号 
          1 把<>进行转义 &lt; &gt; 
          2 把带特殊符号内容写到CDATA 
      -->
      <property name="author">
              <value><![CDATA[<<wang>>]]></value>
      </property>
      
  2. 注入属性--外部bean

    1. 创建两个类 service类和dao类

      public class UserService {
          private UserDao userDao;
      ​
          public void setUserDao(UserDao userDao) {
              this.userDao = userDao;
          }
      ​
          public void add() {
              System.out.println("service ......");
              userDao.update();
          }
      }
      ​
      public class UserDao {
      ​
          public void update() {
              System.out.println("dao ......");
          }
      }
      
    2. 在service调用dao里面的方法

      @Test
          public void userService2() {
              ApplicationContext applicationContext =
                      new ClassPathXmlApplicationContext("bean1.xml");
              UserService userService = applicationContext.getBean("userService", UserService.class);
              UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
              userService.setUserDao(userDao);
              userService.add();
          }
      ​
      ​
      // 或者
      ​
      ​
      @Test
      public void userService1() {
          ApplicationContext applicationContext =
                  new ClassPathXmlApplicationContext("bean1.xml");
          UserService userService = applicationContext.getBean("userService", UserService.class);
          userService.add();
      }
      
    3. 在spring配置文件中进行配置

      <beans>
              <bean id="userDao" class="com.wxy.bean.UserDao"></bean>
              <bean id="userService" class="com.wxy.bean.UserService"></bean>
      </beans><!-- 或者 --><beans>
          <bean id="userDao" class="com.wxy.bean.UserDao"></bean>
          <bean id="userService" class="com.wxy.bean.UserService">
              <property name="userDao" ref="userDao"></property>
          </bean>
      </beans>
      
  3. 注入属性--内部bean

    1. 一对多关系:部门和员工

      public class Dept {
          private String name;
          public void setName(String name) {
              this.name = name;
          }
      }
      ​
      public class Emp {
      ​
          private String name;
          private String gender;
          private Dept dept;
      ​
          public void setName(String name) {
              this.name = name;
          }
      ​
          public void setGender(String gender) {
              this.gender = gender;
          }
      ​
          public void setDept(Dept dept) {
              this.dept = dept;
          }
      }
      
    2. 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示

      @Test
      public void emp1() {
          ApplicationContext applicationContext =
                  new ClassPathXmlApplicationContext("bean2.xml");
          Emp emp = applicationContext.getBean("emp", Emp.class);
          System.out.println(emp);
      }
      
    3. 在spring配置文件中进行配置

      <bean id="emp" class="com.wxy.bean.Emp">
          <property name="name" value="zhangsan"></property>
          <property name="gender" value="femain"></property>
          <property name="dept">
              <bean id="dept" class="com.wxy.bean.Dept">
                  <property name="name" value="soft"></property>
              </bean>
          </property>
      </bean>
      
  4. 注入属性--级联赋值

    1. 第一种方式--外部

      <bean id="emp" class="com.wxy.bean.Emp">
          <property name="name" value="zhangsan"></property>
          <property name="gender" value="femain"></property>
          <property name="dept" ref="dept"></property>
      </bean>
      ​
      <bean id="dept" class="com.wxy.bean.Dept">
          <property name="name" value="soft"></property>
      </bean>
      
    2. 第二种方式

      // 需要在Emp中加入get方法
      public Dept getDept() {
          return dept;
      }
      
      <bean id="emp" class="com.wxy.bean.Emp">
          <property name="name" value="zhangsan"></property>
          <property name="gender" value="femain"></property>
          <property name="dept" ref="dept"></property>
          <property name="dept.name" value="xxx"></property>
      </bean>
      
      <bean id="dept" class="com.wxy.bean.Dept">
          <property name="name" value="soft"></property>
      </bean>
      

注入集合属性

  1. 注入数组类型属性

  2. 注入List集合类型属性

  3. 注入Map集合类型属性

    1. 创建类,定义数组、list、map、set类型属性,生成对应set方法

      public class Student {
          private String[] course;
          private List<String> list;
          private Map<String, String> map;
          private Set<String> set;
      ​
          public void setCourse(String[] course) {
              this.course = course;
          }
      ​
          public void setList(List<String> list) {
              this.list = list;
          }
      ​
          public void setMap(Map<String, String> map) {
              this.map = map;
          }
      ​
          public void setSet(Set<String> set) {
              this.set = set;
          }
      }
      ​
      @Test
          public void testStudent() {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
              Student student = context.getBean("student", Student.class);
              System.out.println(student);
          }
      
    2. 在spring配置文件进行配置

      <bean id="student" class="com.wxy.bean1.Student">
      ​
          <!-- 注入数组 -->
          <property name="course">
              <array>
                  <value>java</value>
                  <value>golang</value>
              </array>
          </property>
          <!-- 注入list -->
          <property name="list">
              <list>
                  <value>zhangsan</value>
                  <value>lisi</value>
              </list>
          </property>
          <!-- 注入Map -->
          <property name="map">
              <map>
                  <entry key="JAVA" value="java"></entry>
                  <entry key="GO" value="go"></entry>
              </map>
          </property>
          <!-- 注入Set -->
          <property name="set">
              <set>
                  <value>Mysql</value>
                  <value>Redis</value>
              </set>
          </property></bean>
      
  4. 在集合里面设置对象类型值

            <!--注入list集合类型,值是对象-->
            <property name="courses">
                <list>
                    <ref bean="course1"></ref>
                    <ref bean="course2"></ref>
                </list>
            </property><!--创建多个course对象-->
    <bean id="course1" class="com.wxy.bean1.Course">
        <property name="courses" value="mysql"></property>
    </bean><bean id="course2" class="com.wxy.bean1.Course">
        <property name="courses" value="spring"></property>
    </bean>
    
  5. 把集合注入部分提取出来

    <?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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    ​
        <util:list id="booksList">
            <value>mysql</value>
            <value>spring</value>
            <value>springboot</value>
            <value>Java</value>
        </util:list>
    ​
        <bean id="books" class="com.wxy.bean2.Books">
            <property name="books" ref="booksList"></property>
        </bean>
    ​
    ​
    </beans>
    

FactoryBean

  1. Spring有两种类型bean,一种普通bean,另外一种工厂beanFactoryBean

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

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

      1. 创建类,让这个类作为工厂bean,实现接口 FactoryBean

      2. 实现接口里面的方法,在实现的方法中定义返回的bean类型

        public class MyBean implements FactoryBean<Books> {
        ​
            @Override
            public Books getObject() throws Exception {
                Books books = new Books();
                return books;
            }
        ​
            @Override
            public Class<?> getObjectType() {
                return null;
            }
        ​
            @Override
            public boolean isSingleton() {
                return FactoryBean.super.isSingleton();
            }
        }
        ​
        ​
        @Test
            public void testMyBean() {
                ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
                Books books = context.getBean("myBean", Books.class);
                System.out.println(books);
            }
        
        <bean id="myBean" class="com.wxy.bean2.MyBean"></bean>
        

Bean作用域

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

  2. Spring里面,默认情况下,bean是单实例对象

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

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

    2. scope属性值

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

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

        <bean id="teather" class="com.wxy.bean3.Teather" scope="prototype"></bean>
        
    3. singleton和prototype区别

      1. 第一 singleton单实例,prototype多实例

      2. 第二 设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象

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

Bean生命周期

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

  2. bean生命周期

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

    <bean id="order" class="com.wxy.bean3.Order" init-method="initMethod"
    destroy-method="destroyMethod">
        <property name="orderID" value="UI123"></property>
    </bean>
    
    public class Order {
        private String orderID;
        public Order() {
            System.out.println("1. 无参构造函数构造方法");
        }
    ​
        public void setOrderID(String orderID) {
            this.orderID = orderID;
            System.out.println("2. 调用setting方法设置属性值");
        }
    ​
        public void initMethod() {
            System.out.println("3. 执行初始化的方法");
        }
    ​
        public void destroyMethod() {
            System.out.println("5. 执行销毁的方法");
        }
    ​
        public String getOrderID() {
            return orderID;
        }
    }
    ​
    ​
    @Test
        public void testOrder() {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/wxy/ApplicationContext/bean7.xml");
            Order order = context.getBean("order", Order.class);
            System.out.println("4. order main = " + order.getOrderID());
            context.close();
        }
    
  4. bean的后置处理器,bean生命周期有七步

    1. 通过构造器创建bean实例(无参数构造)

    2. 为bean的属性设置值和对其他bean引用(调用set方法

    3. // 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization

      @Override
      public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
          return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
      }
      
    4. 调用bean的初始化的方法(需要进行配置初始化的方法)

    5. // 把bean实例传递bean后置处理器的方法 postProcessAfterInitialization

      @Override
      public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
          return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
      }
      
    6. bean可以使用了(对象获取到了)

    7. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

      <bean id="myBeanPostProcessor" class="com.wxy.bean3.MyBeanPostProcessor"></bean>
      

Xml自动装配

  1. 什么是自动装配

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

  2. 演示自动装配过程

    1. 根据属性名称自动注入
    2. 根据属性类型自动注入
    <!--实现自动装配 bean标签属性autowire,配置自动装配
            autowire属性常用两个值:
                    byName根据属性名称注入 ,注入值bean的id值和类属性名称一样
                    byType根据属性类型注入
    -->
<!--    <bean id="emp" class="com.wxy.bean4.Emp" autowire="byName"></bean>-->
<!--    <bean id="dept" class="com.wxy.bean4.Dept"></bean>-->
    <bean id="emp" class="com.wxy.bean4.Emp" autowire="byType"></bean>
    <bean id="dept" class="com.wxy.bean4.Dept"></bean>

外部属性文件

  1. 直接配置数据库信息

    1. 配置德鲁伊连接池

    2. 引入德鲁伊连接池依赖jar包

      <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/userDb"></property>
          <property name="username" value="root"></property>
          <property name="password" value="root"></property>
      </bean>
      
  2. 引入外部属性文件配置数据库连接池

    1. 创建外部属性文件,properties格式文件,写数据库信息

    2. 把外部properties属性文件引入到spring配置文件中

      1. 引入context名称空间

      2. 在spring配置文件使用标签引入外部属性文件

        <?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:context="http://www.springframework.org/schema/context"
               xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        ​
            <context:property-placeholder location="properties/druid.properties"></context:property-placeholder>
        ​
        ​
            <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${mysql.driver}"></property>
                <property name="url" value="${mysql.url}"></property>
                <property name="username" value="${mysql.username}"></property>
                <property name="password" value="${mysql.password}"></property>
            </bean></beans>
        

基于注解

  1. 注解

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

    1. @Component
    2. @Service
    3. @Controller
    4. @Repository

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

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

    1. 引入依赖

      spring-aop-5.2.24.RELEASE.jar jar包

    2. 开启组件扫描

      <context:component-scan base-package="com.wxy" use-default-filters="false">
          <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
      </context:component-scan>
      
    3. 创建类,在类上面添加创建对象注解

      // 注解里面的value属性值可以不写
      // 默认值是类名,首字母小写
      // UserService -- userService
      @Service(value = "userService") // <bean id ="userService" class=""....>
      public class UserService {
      ​
          public void add() {
              System.out.println("service add method");
          }
      }
      
  4. 开启组件扫描细节配置

    <!--示例1 use-default-filters="false"
    表示现在不使用默认filter,自己配置filter
    context:include-filter ,设置扫描哪些内容 --><context:component-scan base-package="com.wxy" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan><!--示例2
    下面配置扫描包所有内容
     context:exclude-filter: 设置哪些内容不进行扫描
     -->
    <context:component-scan base-package="com.wxy" use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Indexed"/>
    </context:component-scan>
    
  5. 基于注解方式实现属性注入

    1. @Autowired:根据属性类型进行自动装配

      1. 把service和dao对象创建,在service和dao类添加创建对象注解

      2. 在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解

        @Service(value = "userService") // <bean id ="userService" class=""....>
        public class UserService {
            //定义dao类型属性 
            //不需要添加set方法 
            //添加注入属性注解
            @Autowired
            private UserDao userDao;
        ​
            public void add() {
                System.out.println("service add method");
                userDao.add();
            }
        }
        
    2. @Qualifier:根据名称进行注入

      这个@Qualifier注解的使用,和上面@Autowired一起使用

      @Service(value = "userService") // <bean id ="userService" class=""....>
      public class UserService {
      ​
          //定义dao类型属性
          //不需要添加set方法
          //添加注入属性注解
          @Autowired // 根本类型进行注入
          @Qualifier(value = "userDaoImp") // 根据名称进行注入
          private UserDao userDao;
      ​
          public void add() {
              System.out.println("service add method");
              userDao.add();
          }
      }
      
    3. @Resource:可以根据类型注入,可以根据名称注入

    4. @Value:注入普通类型属性

      @Service(value = "userService") // <bean id ="userService" class=""....>
      public class UserService {
      ​
          //定义dao类型属性
          //不需要添加set方法
          //添加注入属性注解
          @Autowired // 根本类型进行注入
          @Qualifier(value = "userDaoImp") // 根据名称进行注入
          private UserDao userDao;
      ​
          @Value(value = "123")
          private int num;
      ​
          public void add() {
              System.out.println("service add method");
              userDao.add();
              System.out.println(num);
          }
      }
      
  6. 完全注解开发

    1. 创建配置类,替代xml配置文件

      @Configuration
      @ComponentScan(basePackages = {"com.wxy"})
      public class SpringConfig {
      ​
      }
      
    2. 编写测试类

      @Test
      public void testConfig() {
          ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
          UserService userService = context.getBean("userService", UserService.class);
          userService.add();
      }