第四章:Spring上

news2024/9/27 19:22:21

第四章:Spring上

4.1:Spring简介

  1. Spring概述

    官网地址:https://spring.io/

    • Spring是最受欢迎的企业级的java应用程序开发框架,数以百万的来自世界各地的开发人员使用Spring框架来创建性能好、易于测试、可重用的代码。
    • Spring框架是一个开源的Java平台,它最初是由Rod Johnson编写的,并且于2003年5月首次在Apache 2.0许可下发布。
    • Spring是轻量级的框架,其基础版本只有2MB左右的大小。
    • Spring框架的核心特性是可以用于开发任何Java应用程序,但是在Java EE平台上构建web应用程序是需要扩展的。Spring框架的目标是使J2EE开发变得更容易使用,通过启用基于POJO编程模型来促进良好的编程实践。
  2. Spring家族
    在这里插入图片描述

  3. Spring Framework

    Spring基础框架,可以视为Spring基础设施,基本上任何其他Spring项目都是以Spring Framework为基础的。

    • Spring Framework特性

      1. 非入侵式

        ​ 使用Spring Framework开发应用程序是,Spring对应用程序本身的结构影响非常小。对领域模型可以做到零污染,对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于Spring Framework开发应用程序时结构清晰、简洁优雅。

      2. 控制反转

        IOC————Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境资源准备好,我们享受资源注入。

      3. 面向切面编程

        AOP————Aspect Oriented Programming,在不修改资源代码的基础上增强代码功能。

      4. 容器

        Spring IOC是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。

      5. 组件化

        Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XMLJava注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。

      6. 声明式

        ​ 很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。

      7. 一站式

        ​ 在IOCAOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库,而且Spring旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在Spring Framework的基础上全部使用Spring来实现。

    • Spring Framework五大功能模块

      功能模块功能介绍
      Core Container核心容器,在Spring环境下使用任何功能都必须基于IOC容器
      AOP & Aspects面向切面编程
      Testing提供了对JunitTestNG测试框架的整合
      Data Access/ Integration提供了对数据访问/集成的功能
      Spring MVC提供了面向Web应用程序的集成功能

4.2:IOC

  1. IOC容器

    • IOC思想

      IOCInversion of Control,翻译过来是反转控制

      1. 获取资源的传统方式

        ​ 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。

      2. 反转控制方式获取资源

        ​ 反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推动给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。

      3. DI

        DIDependency Injection,翻译过来是依赖注入

        DIIOC的另一种表述方式:即组件以一些预先定义好的方式接收来自于容器的资源注入,相对于IOC而言,这种表述更直接。

      IOC就是一种反转控制的思想,而DI是对IOC的一种具体实现。

    • IOC容器在spring中的实现

      SpringIOC容器就是IOC思想的一个落地的产品实现。IOC容器中管理的组件也叫作bean。在创建bean之前,首先需要创建IOC容器。Spring提供了IOC容器的两种实现方式:

      1. BeanFactory

        ​ 这是IOC容器的基本实现,是Spring内部使用的接口。面向Spring本身,不提供给开发人员使用。

      2. ApplicationContext

        BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory

      3. ApplicationContext的主要实现类
        在这里插入图片描述

      类型名简介
      ClassPathXmlApplicationContext通过读取类路径下的XML格式的配置文件创建IOC容器对象
      FileSystemXmlApplicationContext通过文件系统读取XML格式的配置文件创建IOC容器对象
      ConfigurableApplicationContextApplicationContext的子接口,包含一些扩展方法refresh()close(),让ApplicationContext具有启动、关闭和刷新上下文的能力
      WebApplicationContext专门为Web应用准备,基于Web环境创建IOC容器对象,并将对象引入存入ServletContext域中
  2. 基于XML管理bean

    • 实验一:入门案例

      1. 创建模块

        创建一个新的模块,spring_helloworld_07

      2. 引入依赖

        <packaging>jar</packaging>
        
        <dependencies>
            <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.1</version>
            </dependency>
            <!-- junit测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        
      3. 创建类:HelloWorld

        package com.wang.pojo;
        
        public class HelloWorld {
            public void sayHello() {
                System.out.println("Hello, spring");
            }
        }
        
      4. 创建spring的配置文件
        在这里插入图片描述

        <!--
        	bean: 配置一个bean对象,将对象交给IOC容器管理
        		id: bean的唯一标识,不能重复
        		class: 设置bean对象所对应的类型
        -->
        <bean id="helloworld" class="com.wang.pojo.HelloWorld"></bean>
        
      5. 测试

        @Test
        public void test() {
            // 获取IOC容器
            ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 获取IOC容器中的bean
            HelloWorld helloworld = (HelloWorld)ioc.getBean("helloworld");
            helloworld.sayHello();
        }
        

在这里插入图片描述

  • 实验二:获取bean

    创建一个新的模块,spring_ioc_xml_08,并引入spring_helloworld_07模块pom.xmljar包。创建一个Student实体类。

    package com.wang.pojo;
    
    public class Student {
        private Integer sid;
        private String name;
        private Integer age;
        private String gender;
    
        // 如下省略空参构造、全参构造、get/set方法、toString方法
    }
    
    <!-- 创建spring-ioc.xml配置文件配置一个bean对象 -->
    <bean id="studentOne" class="com.wang.pojo.Student"></bean>
    
    1. 根据id获取

      由于id属性指定了bean的唯一标识,所以根据bean标签的id属性可以精确获取到一个组件对象。

    2. 根据类型获取

      // 当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
      	// 若没有任何一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException
      	// 若有多个类型匹配的bean,此时抛出异常:NoUniqueBeanDefinitionException
      @Test
      public void testIOC() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
          Student student = ioc.getBean(Student.class);
          System.out.println(student);
      }
      
    3. 根据id和类型

      // 如果组件实现了接口,根据接口类型可以获取bean,但是要求bean唯一
      @Test
      public void testIOC() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
          Student student = ioc.getBean("studentOne", Student.class);
          // 其中Student实现了Perso接口,且只有唯一的Student类实现了Person接口
          // Person  person = ioc.getBean(Person.class);
          System.out.println(person);
      }
      

      ​ 根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:对象 instanceof 指定的类型的返回结果,只要返回结果为true就可以认定为为和类型匹配,能够获取到。

  • 实验三:依赖注入值setter注入

    <bean id="studentTwo" class="com.wang.pojo.Student">
        <!--
    		property: 通过成员变量的set方法进行赋值
    			name:设置需要赋值的属性名(和set方法有关)
    			value:设置为属性所赋的值
        -->
        <property name="sid" value="1001"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="23"></property>
        <property name="gender" value=""></property>
    </bean>
    
    @Test
    public void testDI() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        Student student = ioc.getBean("studentTwo", Student.class);
        System.out.println(student);
    }
    
  • 实验四:依赖注入之构造器注入

    <!-- constructor-arg标签属性
    	name: 指定参数名
    	index: 指定参数所在位置的索引(从0开始)
    -->
    <bean id="studentThree" class="com.wang.pojo.Student">
        <constructor-arg value="1002"></constructor-arg>
        <constructor-arg value="李四"></constructor-arg>
        <constructor-arg value=""></constructor-arg>
        <constructor-arg value="24"></constructor-arg>
    </bean>
    
    @Test
    public void testDI() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        Student student = ioc.getBean("studentThree", Student.class);
        System.out.println(student);
    }
    
  • 实验五:特殊值处理

    1. 字面量赋值

      <!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
      <property name="name" value="张三"></property>
      
    2. null

      <property name="gender"><null/></property>
      
    3. xml实体

      <!-- value属性值为<王五> -->
      <property name="name" value="&lt;王五&gt;"></property>
      
    4. CDATA

      <!--
      	CDATA中的C代表Character,是文本、字符的含义、CDATA就表示纯文本数据。
      	XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析,所以CDATA节中写什么符号都随意
      -->
      <property name="name">
          <value><![CDATA[<王五>]]></value>
      </property>
      
  • 实验六:为类类型属性值赋值

    1. 创建一个Clazz

      package com.wang.pojo;
      
      public class Clazz {
          private Integer cid;
          private String cname;
          
          // 如下省略空参构造、全参构造、get/set方法、toString方法
      }
      
    2. Stduent中添加一个属性

      private Clazz clazz;
      
    3. 方式一:引用外部以声明的bean

      <bean id="studentFive" class="com.wang.pojo.Student">
          <property name="sid" value="1004"></property>
          <property name="name" value="赵六"></property>
          <property name="age" value="26"></property>
          <property name="gender" value=""></property>
          <!-- ref:引用IOC容器中的某个bean的id -->
          <property name="clazz" ref="clazzOne"></property>
      </bean>
      
      <bean id="clazzOne" class="com.wang.pojo.Clazz">
          <property name="cid" value="1111"></property>
          <property name="cname" value="最强王者班"></property>
      </bean>
      
    4. 方式二:级联属性赋值

      <bean id="studentFive" class="com.wang.pojo.Student">
          <property name="sid" value="1004"></property>
          <property name="name" value="赵六"></property>
          <property name="age" value="26"></property>
          <property name="gender" value=""></property>
          <!-- ref:引用IOC容器中的某个bean的id -->
          <property name="clazz" ref="clazzOne"></property>
          <!-- 级联的方式,要保证提前为clazz属性赋值或者实例化 -->
          <property name="clazz.cid" value="2222"></property>
          <property name="clazz.cname" value="远大前程班"></property>
      </bean>
      
    5. 方式三:内部bean

      <bean id="studentFive" class="com.wang.pojo.Student">
          <property name="sid" value="1004"></property>
          <property name="name" value="赵六"></property>
          <property name="age" value="26"></property>
          <property name="gender" value=""></property>
          <property name="clazz">
              <!-- 内部bean,只能在当前bean的内部使用,不能直接通过IOC容器获取 -->
              <bean id="clazzInner" class="com.wang.pojo.Clazz">
                  <property name="cid" value="2222"></property>
                  <property name="cname" value="远大前程班"></property>
              </bean>
          </property>
      </bean>
      
  • 实验七:为数组类型属性赋值

    1. Student类添加一个属性

      private String[] hobby;
      
    2. 配置bean

      <bean id="studentFive" class="com.wang.pojo.Student">
          <property name="sid" value="1004"></property>
          <property name="name" value="赵六"></property>
          <property name="age" value="26"></property>
          <property name="gender" value=""></property>
          <property name="hobby">
              <array>
                  <value>抽烟</value>
                  <value>喝酒</value>
                  <value>烫头</value>
              </array>
          </property>
      </bean>
      
  • 实验八:为集合类型属性赋值

    1. List集合类型属性赋值

      • Clazz类中添加一个students属性

        private List<Student> students;
        
      • 方式一:在内部配置bean

        <bean id="clazzOne" class="com.wang.pojo.Clazz">
            <property name="cid" value="1111"></property>
            <property name="cname" value="最强王者班"></property>
            <property name="students">
                <list>
                    <ref bean="studentOne"></ref>
                    <ref bean="studentTwo"></ref>
                    <ref bean="studentThree"></ref>
                </list>
            </property>
        </bean>
        
      • 方式二:在外部配置bean

        <bean id="clazzOne" class="com.wang.pojo.Clazz">
            <property name="cid" value="1111"></property>
            <property name="cname" value="最强王者班"></property>
            <property name="students" ref="studentList"></property>
        </bean>
        
        <!-- 配置一个集合类型额bean,需要使用util的约束 -->
        <util:list id="studentList">
            <ref bean="studentOne"></ref>
            <ref bean="studentTwo"></ref>
            <ref bean="studentThree"></ref>
        </util:list>
        
    2. Map集合类型属性赋值

      • 创建Teacher

        package com.wang.pojo;
        
        public class Teacher {
            private Integer tid;
            private String tname;
        }
        
      • Student类中添加一个属性

        private Map<String, Teacher> teacherMap;
        
      • 方式一:在内部配置bean

        <bean id="studentFive" class="com.wang.pojo.Student">
            <property name="sid" value="1004"></property>
            <property name="name" value="赵六"></property>
            <property name="age" value="26"></property>
            <property name="gender" value=""></property>
            <property name="teacherMap">
                <map>
                    <entry key="10086" value-ref="teacherOne"></entry>
                    <entry key="10010" value-ref="teacherTwo"></entry>
                </map>
            </property>
        </bean>
        
        <bean id="teacherOne" class="com.wang.pojo.Teacher">
            <property name="tid" value="10086"></property>
            <property name="tname" value="大宝"></property>
        </bean>
        <bean id="teacherTwo" class="com.wang.pojo.Teacher">
            <property name="tid" value="10010"></property>
            <property name="tname" value="小宝"></property>
        </bean>
        
      • 方式二:在外部配置bean

        <bean id="studentFive" class="com.wang.pojo.Student">
            <property name="sid" value="1004"></property>
            <property name="name" value="赵六"></property>
            <property name="age" value="26"></property>
            <property name="gender" value=""></property>
            <property name="teacherMap" ref="studentMap"></property>
        </bean>
        
        <util:map id="studentMap">
            <entry key="10086" value-ref="teacherOne"></entry>
            <entry key="10010" value-ref="teacherTwo"></entry>
        </util:map>
        
  • 实验九:p命名空间

    ​ 引入p命名空间后,可以通过以下方式为bean的各个属性赋值。

    <bean id="studentSix" class="com.wang.pojo.Student"
              p:sid="1005" p:name="小明" p:teacherMap-ref="studentMap"></bean>
    
  • 实验十:引入外部属性文件

    1. 加入依赖

      <!-- MySQL驱动 -->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.16</version>
      </dependency>
      <!-- 数据源 -->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.0.31</version>
      </dependency>
      
    2. 创建外部文件jdbc.properties

      jdbc.driver=com.mysql.cj.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
      jdbc.username=root
      jdbc.password=abc123
      
    3. 创建spring-datasource.xml并引入外部文件

      <!-- 引入jdbc.properties, 之后可以通过${key}的方式访问value-->
      <context:property-placeholder location="jdbc.properties" ></context:property-placeholder>
      
    4. 配置bean

      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
          <property name="driverClassName" value="${jdbc.driver}"></property>
          <property name="url" value="${jdbc.url}"></property>
          <property name="username" value="${jdbc.username}"></property>
          <property name="password" value="${jdbc.password}"></property>
      </bean>
      
    5. 测试,新建DataSourceTest.java文件

      @Test
      public void testDataSource() throws SQLException {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
          DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
          System.out.println(dataSource.getConnection());
      }
      
  • 实验十一:bean的作用域

    Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

    取值含义创建对象的时机
    singleton(默认)IOC容器中,这个bean的对象始终为单实例IOC容器初始化时
    prototype这个beanIOC容器中有多个实例获取bean

    如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):

    取值含义
    request在一个请求范围内有效
    session在一个会话范围内有效
    <!-- 创建spring-scope.xml配置文件配置一个bean对象 -->
    <bean id="student" class="com.wang.pojo.Student" scope="prototype">
        <property name="sid" value="1001"></property>
        <property name="name" value="张三"></property>
    </bean>
    
    @Test
    public void testScope() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
        Student student1 = ioc.getBean(Student.class);
        Student student2 = ioc.getBean(Student.class);
        System.out.println(student1 == student2); // false
    }
    
  • 实验十二:bean生命周期

    1. 具体的生命周期过程

      • 实例化:bean对象创建(调用无参构造器)。
      • 依赖注入:给bean对象设置属性。
      • 后置处理器的postProcessBeforeInitializationbean对象初始化之前操作(由bean的后置处理器负责)。
      • 初始化,需要通过beaninit-method:对象的初始化(需要配置bean时指定初始化方法)。
      • 后置处理器的postProcessAfterInitializationbean对象初始化之后操作(由bean的后置处理器负责)。
      • IOC容器关闭时销毁,需要通过beandestroy-method属性指定销毁的方法:bean对象销毁(需在配置bean时指定销毁方法)。
    2. 注意:

      • bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行。
      • bean的作用域为多例时,生命周期的前三个步骤会在获取bean时执行。
    3. 创建User

      package com.wang.pojo;
      
      public class User {
          private Integer id;
          private String username;
          private String password;
          private Integer age;
          
          // 如下省略空参构造、全参构造、get/set方法、toString方法
          
          // 空参构造和这一个set方法按下面方式改动即可,其他的不变
          public User() {
              System.out.println("生命周期1:实例化");
          }
          public void setId(Integer id) {
              System.out.println("生命周期2:依赖注入");
              this.id = id;
          }
          
          public void initMethod() {
              System.out.println("生命周期3:初始化");
          }
          public void destroyMethod() {
              System.out.println("生命周期4:销毁");
          }
      }
      
    4. 创建spring-lifecyc.xml配置文件配置bean对象

      <bean id="user" class="com.wang.pojo.User" init-method="initMethod" destroy-method="destroyMethod">
          <property name="id" value="1"></property>
          <property name="username" value="admin"></property>
          <property name="password" value="123456"></property>
          <property name="age" value="23"></property>
      </bean>
      
    5. bean的后置处理器

      bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行。

      package com.wang.process;
      
      public class MyBeanPostProcessor implements BeanPostProcessor {
          // 此方法在bean的生命周期初始化之前执行
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("MyBeanPostProcessor--->后置处理器postProcessBeforeInitialization");
              return bean;
          }
          // 此方法在bean的生命周期初始化之后执行
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("MyBeanPostProcessor--->前置处理器postProcessAfterInitialization");
              return bean;
          }
      }
      
      <bean id="myBeanPostProcessor" class="com.wang.process.MyBeanPostProcessor"></bean>
      
    6. 测试

      @Test
      public void test() {
          ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
          User user = ioc.getBean(User.class);
          System.out.println(user);
          ioc.close();
      }
      
  • 实验十三:FactoryBean

    1. 简介

      FactoryBeanspring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

      package org.springframework.beans.factory;
      
      public interface FactoryBean<T> {
      	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
      
      	@Nullable
      	T getObject() throws Exception;
      
      	@Nullable
      	Class<?> getObjectType();
      
      	default boolean isSingleton() {
      		return true;
      	}
      }
      
    2. 创建类UserFactoryBean

      package com.wang.factory;
      
      public class UserFactoryBean implements FactoryBean<User> {
          @Override
          public User getObject() throws Exception {
              return new User();
          }
      
          @Override
          public Class<?> getObjectType() {
              return User.class;
          }
      }
      
    3. 配置bean

      <bean class="com.wang.factory.UserFactoryBean"></bean>
      
    4. 测试

      @Test
      public void testFactoryBean() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
          User user = ioc.getBean(User.class);
          System.out.println(user);
      }
      
  • 实验十四:基于xml的自动装配

    自动装配:根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。

    1. 场景模拟

      • dao层:

        package com.wang.dao;
        
        public interface UserDao {
            void saveUser();
        }
        
        package com.wang.dao.impl;
        
        public class UserDaoImpl implements UserDao {
        
            @Override
            public void saveUser() {
                System.out.println("保存成功");
            }
        }
        
      • service层:

        package com.wang.service;
        
        public interface UserService {
            void saveUser();
        }
        
        package com.wang.service.impl;
        
        public class UserServiceImpl implements UserService {
        
            private UserDao userDao;
        
            public UserDao getUserDao() {
                return userDao;
            }
            public void setUserDao(UserDao userDao) {
                this.userDao = userDao;
            }
        
            @Override
            public void saveUser() {
                userDao.saveUser();
            }
        }
        
      • controller层:

        package com.wang.controller;
        
        public class UserController{
        
            private UserService userService;
        
            public void setUserService(UserService userService){
                this.userService = userService;
            }
        
            public void saveUser() {
                userService.saveUser();
            }
        }
        
      • 配置bean:手动配置

        <!-- 创建spring-autowire-xml.xml配置文件配置bean对象  -->
        <bean id="userController" class="com.wang.controller.UserController">
            <property name="userService" ref="userService"></property>
        </bean>
        
        <bean id="userService" class="com.wang.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"></property>
        </bean>
        
        <bean id="userDao" class="com.wang.dao.impl.UserDaoImpl"></bean>
        
      • 测试

        @Test
        public void testAutowire() {
            ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire-xml.xml");
            UserController userController = ioc.getBean(UserController.class);
            userController.saveUser();
        }
        
    2. 使用byType方式自动装配

      <!--
      	自动装配的策略:
      		1. no, default: 表示不装配,即bean中的属性不会自动匹配某个bean为属性值,此时属性使用默认值
      		2. byType:根据要赋值的属性的类型,在IOC容器中匹配某个bean,为属性赋值
      	注意:
      		1. 若通过类型没有找到任何一个类型匹配的bean,此时不装配,属性使用默认值。
      		2. 若通过类型找到了多个类型匹配的bean,此时会抛出异常: NoUniqueBeanDefinitionException。
      -->
      <bean id="userController" class="com.wang.controller.UserController" autowire="byType"></bean>
      <bean id="userService" class="com.wang.service.impl.UserServiceImpl" autowire="byType"></bean>
      <bean id="userDao" class="com.wang.dao.impl.UserDaoImpl"></bean>
      
    3. 使用byName方式自动装配

      <!--
      	byName:将要赋值的属性的属性名作为bean的id在IOC容器中匹配某个bean,为属性赋值。当类型匹配的bean有多个时,此时可以使用byName实现自动装配
      -->
      <bean id="userController" class="com.wang.controller.UserController" autowire="byName"></bean>
      <bean id="userService" class="com.wang.service.impl.UserServiceImpl" autowire="byName"></bean>
      <bean id="userDao" class="com.wang.dao.impl.UserDaoImpl"></bean>
      
  1. 基于注解管理bean

    • 实验一:标记与扫苗

      1. 注解

        ​ 和XML配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

        ​ 本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

      2. 扫描

        Spring为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后跟注解进行后续操作。

      3. 新建模块

        ​ 创建一个新的模块,spring_ioc_annotation_09,并引入下面的jar包。

        <packaging>jar</packaging>
        
        <dependencies>
            <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.1</version>
            </dependency>
            <!-- junit测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        
      4. 标识组件的常用注解

        • @Component:将类标识为普通组件。
        • @Repository:将类标识为持久层组件。
        • @Service:将类标识为业务层组件。
        • @Controller:将类标识为持久层组件。

        ​ 通过源码我们得知:@Repository@Service@Controller这三个注解只是在@Component注解的基础上起了三个新名字。对于Spring使用IOC容器管理这些组件来说没有区别。所以@Repository@Service@Controller这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

      5. 创建组件

        • dao层:

          package com.wang.dao;
          
          public interface UserDao {
          }
          
          package com.wang.dao.impl;
          
          @Repository
          public class UserDaoImpl implements UserDao {
          }
          
        • service层:

          package com.wang.service;
          
          public interface UserService {
          }
          
          package com.wang.service.impl;
          
          @Service
          public class UserServiceImpl implements UserService {
          }
          
        • controller层:

          package com.wang.controller;
          
          @Controller
          public class UserController {
          }
          
      6. 扫描组件

        • 情况一:最基本的扫描方式

          <!-- 创建spring-ioc-annotation.xml配置文件-->
          <context:component-scan base-package="com.wang"></context:component-scan>
          
        • 情况二:指定要排除的组件

          <!--
          	context:exclude-filter: 排除扫描
          		type: 设置排除扫描的方式
          			annotation: 根据注解的类型进行排除, expression需要设置排除的注解的全类名
          			assignable: 根据类的类型进行排除, expression需要设置排除的类的全类名
          -->
          <context:component-scan base-package="com.wang" use-default-filters="false">
           <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          <!-- <context:exclude-filter type="assignable" expression="com.wang.controller.UserController"/>-->
          </context:component-scan>
          
        • 情况三:仅扫描指定组件

          <!--
          	context:include-filter: 包含扫描
          	注意: 需要在context:component-scan标签中设置use-default-filters="false"
          		use-default-filters: 默认为true, 设置的包下所有的类是否都需要扫描
          -->
          <context:component-scan base-package="com.wang" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          </context:component-scan>
          
      7. 测试

        @Test
        public void test() {
            ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
            UserController userController = ioc.getBean(UserController.class);
            System.out.println(userController);
            UserService userService = ioc.getBean(UserService.class);
            System.out.println(userService);
            UserDao userDao = ioc.getBean(UserDao.class);
            System.out.println(userDao);
        }
        
      8. 组件所对应的beanid

        ​ 通过注解+扫描所配置的beanid,默认值为类的小驼峰,即类名的首字母为小写的结果。可以通过标识组件的注解的value属性值设置bean的自定义的id

    • 实验二:基于注解的自动装配

      1. 场景模拟

        • dao层:

          void saveUse();
          
          @Override
          public void saveUse() {
              System.out.println("保存成功");
          }
          
        • service层:

          void saveUser();
          
          @Autowired
          private UserDao userDao;
          
          @Override
          public void saveUser() {
              userDao.saveUse();
          }
          
        • controller层:

          @Autowired
          private UserService userService;
          
          //    @Autowired
          //    public UserController(UserService userService) {
          //        this.userService = userService;
          //    }
          
          //    @Autowired
          //    public void setUserService(UserService userService) {
          //        this.userService = userService;
          //    }
          
          public void saveUser() {
              userService.saveUser();
          }
          
      2. Autowired注解能够标识的位置

        • 标识在成员变量上,此时不需要设置成员变量的set方法。
        • 标识在set方法上。
        • 标识在为当前成员变量赋值的有参构造上。
      3. Autowired注解的原理

        • 默认通过byType的方式,在IOC容器中通过类型匹配某个bean为属性赋值。
        • 若有多个类型匹配的bean,此时会自动转换为byName的方式实现自动装配的效果。
        • byTypebyName的方式都无法实现自动装配,即IOC容器中有多个类型匹配的bean且这些beanid要赋值的属性的属性名都不一致,此时抛异常:NoUniqueBeanDefinitionException
        • 此时可以在要赋值的属性上,添加一个注解@Qualifier,通过该注解的value属性值,指定某个beanid,将这个bean为属性赋值。
      4. Autowired注解的注意事项

        ​ 若IOC容器中没有任何一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException。在@Autowired注解中有个属性required,默认值为true,要求必须完成自动装配,可以将required设置为false,此时能装配则装配,无法装配则使用属性的默认值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/805193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【多模态】18、ViLD | 通过对视觉和语言知识蒸馏来实现开集目标检测(ICLR2022)

文章目录 一、背景二、方法2.1 对新类别的定位 Localization2.2 使用 cropped regions 进行开放词汇检测2.3 ViLD 三、效果 论文&#xff1a;Open-vocabulary Object Detection via Vision and Language Knowledge Distillation 代码&#xff1a;https://github.com/tensorflo…

Verilog语法学习——LV10_使用函数实现数据大小端转换

LV10_使用函数实现数据大小端转换 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 描述 在数字芯片设计中&#xff0c;经常把实现特定功能的模块编写成函数&…

HBuilder 编辑器终端窗口无法输入,未响应的解决方案

HBuilder 编辑器终端窗口无法输入&#xff0c;未响应的解决方案 一、找到 HBuilder 安装目录 找到 main.js HBuilderX - plugins - builtincef3terminal - script - main.js 二、编辑 main.js 将 main.js 文件中的 powershell.exe 和 cmd.exe 路径都改为绝对路径 C:/Windows…

【渗透测试】漏洞扫描AWVS安装使用教程,三分钟手把手教会,非常简单

一、AWS简介 Acunetix Web Vulnerability Scanner(简称AWVS)是一个自动化的Web漏洞扫描工具&#xff0c;它可以扫描任何通过Web浏览器访问和遵循HITP/HTTPS规则的Web站点。 AWVS原理是基于漏洞匹配方法&#xff0c;通过网络爬虫测试你的网站安全&#xff0c;检测流行安全AWVS可…

Hadoop学习日记-YARN组件

YARN(Yet Another Resource Negotiator)作为一种新的Hadoop资源管理器&#xff0c;是另一种资源协调者。 YARN是一个通用的资源管理系统和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度 YARN架构图 YARN3大组件&#xff1a; &#xff08;物理层面&#xff09…

Spring学习笔记,包含Spring IOC、AOP基本原理、Bean管理、Spring 事务等等

&#x1f600;&#x1f600;&#x1f600;创作不易&#xff0c;各位看官点赞收藏. 文章目录 Spring 基础笔记1、控制反转 (IOC)1.1、IOC 底层原理1.2、IOC 之Bean管理 ( XML )1.3、IOC 之Bean管理 (FactoryBean)1.4、Bean的作用域1.5、Bean的生命周期1.6、Bean的自动装配1.7、I…

SFP6002-ASEMI代理海矽美快恢复二极管参数、尺寸、规格

编辑&#xff1a;ll SFP6002-ASEMI代理海矽美快恢复二极管参数、尺寸、规格 型号&#xff1a;SFP6002 品牌&#xff1a;ASEMI 封装&#xff1a;TO-247AB 恢复时间&#xff1a;30ns 正向电流&#xff1a;60A 反向耐压&#xff1a;200V 芯片大小&#xff1a;102MIL*2 芯…

几个影响 cpu cache 性能因素及 cache 测试工具介绍

》内核新视界文章汇总《 文章目录 1 cache 性能及影响因素1.1 内存访问和性能比较1.2 cache line 对性能的影响1.3 L1 和 L2 缓存大小1.4 指令集并行性对 cache 性能的影响1.5 缓存关联性对 cache 的影响1.6 错误的 cacheline 共享 (缓存一致性)1.7 硬件设计 2 cpu cache benc…

【EI/SCOPUS会议征稿】第四届机器学习与计算机应用国际学术会议(ICMLCA 2023)

ICMLCA 2023 第四届机器学习与计算机应用国际学术会议 2023 4th International Conference on Machine Learning and Computer Application 第四届机器学习与计算机应用国际学术会议(ICMLCA 2023)定于2023年10月27-29日在中国杭州隆重举行。本届会议将主要关注机器学习和计算…

rk3568 Debian11 如何打开热点

思路&#xff1a;1. 下载必要工具&#xff08;hostapt、dnsmasq&#xff09;2. 配置网络&#xff08;无线网卡配置静态IP&#xff09;3. 配置hostapt配置文件4. 配置DHCP服务5. 启动服务&#xff08;hostapd/dnsmasq/network&#xff09;6. IP转发&#xff08;这一步决定了是否…

【QT 网络云盘客户端】——登录界面功能的实现

目录 1.注册账号 2.服务器ip地址和端口号设置 3. 登录功能 4.读取配置文件 5.显示主界面 1.注册账号 1.点击注册页面&#xff0c;将数据 输入 到 用户名&#xff0c;昵称&#xff0c;密码&#xff0c;确认密码&#xff0c;手机&#xff0c;邮箱 的输入框中&#xff0c; 点…

Vue3 导出word

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;导出word 目录 1.首先&#xff0c;你需要安装docxtemplater库。可以使用npm或yarn来安装&#xff1a; 2.在Vue组件中&#xff0c;你可以使用docxtemplater来生成Word文档并提供一个导出按钮供用户下载…

线性表之顺序表

在计算机科学中&#xff0c;数据结构是非常重要的基础知识之一。数据结构为我们提供了组织和管理数据的方法和技巧&#xff0c;使得我们可以高效地存储、检索和操作数据。而顺序表作为数据结构中最基本、最常用的一种存储结构&#xff0c;也是我们学习数据结构的第一步。 本文将…

idea 关于高亮显示与选中字符串相同的内容

dea 关于高亮显示与选中字符串相同的内容&#xff0c;本文作为个人备忘的同时也希望可以作为大家的参考。 依次修改File-settings-Editor-Color Scheme-General菜单下的Code-Identifier under caret和Identifier under caret(write)的Backgroud色值&#xff0c;可以参考下图。…

阿里云域名备案

最好的爱情&#xff0c;不是因为我们彼此需要在一起&#xff0c;而是因为我们彼此想要在一起。 阿里云的域名如何备案&#xff0c;域名备案和ICP备案一样吗&#xff1f;&#xff1f; 截至我所掌握的知识&#xff08;2021年9月&#xff09;&#xff0c;阿里云的域名备案和ICP备案…

【GoLang】基础语法(上)

Go基础语法(上) 文章目录 Go基础语法(上)01注释02变量定义初始化打印内存地址变量交换匿名变量变量的作用域 03常量iota 04基本数据类型布尔类型数字类型整型浮点型 字符与字符串 05数据类型转换06运算符算术运算符关系运算符逻辑运算符位运算符赋值运算符 07获取键盘输入 01注…

Java 设计模式 - 简单工厂模式 - 创建对象的简便之道

简单工厂模式是一种创建型设计模式&#xff0c;它提供了一种简单的方式来创建对象&#xff0c;而无需暴露对象创建的逻辑。在本篇博客中&#xff0c;我们将深入了解简单工厂模式的概念、实现方式以及如何在Java中使用它来创建对象。 为什么使用简单工厂模式&#xff1f; 在软…

PC音频框架学习

1.整体链路 下行播放&#xff1a; App下发音源→CPU Audio Engine 信号处理→DSP数字信号处理→Codec DAC→PA→SPK 上行录音&#xff1a; MIC拾音→集成运放→Codec ADC→DSP数字信号处理→CPU Audio Engine 信号处理→App 2.硬件 CPU PCH DSP(可选) Codec PA SPKbox MIC…

Vue 3 中的插槽(Slots)用法

插槽&#xff08;Slots&#xff09;是 Vue 组件中一种非常有用的功能&#xff0c;用于在父组件中向子组件传递内容。Vue 3 引入了 <script setup> 语法&#xff0c;使得组件的写法更加简洁和易读。在本篇博客中&#xff0c;我们将探讨在 Vue 3 中使用插槽的不同方式&…

数据结构与算法基础-学习-27-图之最短路径之Dijkstra(迪杰斯特拉)算法

一、最短路径应用案例 例如从北京到上海旅游&#xff0c;有多条路可以到目的地&#xff0c;哪条路线最短&#xff0c;哪条路线最省钱&#xff0c;就是典型的最短路径问题。 二、最短路径问题分类 最短路径问题可以分为两类&#xff0c;第一类为&#xff1a;两点间最短路径。第…