1 Spring是什么

  • Spring是一个开源框架。
  • Spring为简化企业级应用开发而生,使用Spring可以使简单的JavaBean实现以前只有EJB才能实现的功能。
  • Spring是一个IOC(DI)和AOP容器框架。
  • 具体描述Spring:
    • 轻量级:Spring是非侵入的。基于Spring开发的应用中的对象可以不依赖于Spring的API。
    • 依赖注入(DI,Dependency Injection、IOC)
    • 面向切面编程(AOP,Aspect Oriented Programming)
    • 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
    • 框架:Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。
    • 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。
  • Spring模块:

图片说明

2 Spring的HelloWorld

  • Spring项目目录结构:

图片说明

  • HelloWorld类(JavaBean):

    package com.xianhuii.beans;
    
    public class HelloWorld {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
        public void hello() {
            System.out.println("hello " + name);
        }
    
    }
  • Main类(不使用Spring前):

    package com.xianhuii.beans;
    
    public class Main {
        public static void main(String[] args) {
            // 1、实例化
            HelloWorld helloWorld = new HelloWorld();
            // 2、设值
            helloWorld.setName("xianhuii");
            helloWorld.hello();
        }
    }
  • 创建SpringConfig配置文件(applicationContext.xml),配置Bean:

    <?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 配置Bean -->
        <bean id="helloworld" class="com.xianhuii.beans.HelloWorld">
            <property name="name" value="xianhuii"/>
        </bean>
    
    </beans>
  • 使用Bean:

    package com.xianhuii.beans;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
        public static void main(String[] args) {
            // 1、创建Spring的IOC容器对象
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取Bean
            HelloWorld helloworld = (HelloWorld) context.getBean("helloworld");
            // 3、使用Bean
            helloworld.hello();
        }
    }
  • Spring使用步骤:

    1. 创建Bean类;
    2. 配置Bean;
    3. 使用Bean:
      1. 创建Spring的IOC容器对象
      2. 从容器中获取Bean
      3. 使用Bean

3 什么是IOC、DI

图片说明

1、IOC

  • IOC(Inversion of Control),控制反转:其思想是反转资源获取的方向
  • 传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适时的返回资源。
  • 而应用了IOC之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源
  • 这种行为也被称为查找的被动形式。
  • 个人理解:将对象的创建、赋值等操作交由容器来做。

2、DI

  • DI(Dependency Injection),依赖注入:是IOC的另一种表述方式。
  • 组件以一些预先定义要的方式(如setter方法)接受来自容器的资源注入。
  • 个人理解:对象的赋值等操作由容器来做。

4 Spring IOC容器

  • 在Spring IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化。只有在容器实例化后,才可以从IOC容器中获取Bean实例并使用。
  • Spring提供了两种类型的IOC容器实现:
    • BeanFactory:面向框架本身,IOC容器的基本实现。
    • ApplicationContext:面向开发者,提供了更多高级的特性,是BeanFactory的子接口。
  • 无论使用何种方式,配置文件时都是相同的。

4.1 ApplicationContext

1、主要实现类

  • ApplicationContext的两个主要实现类:
    • ClassPathXmlApplicationContext:从类路径下加载配置文件。
    • FileSystemXmlApplicationContext:从文件系统中加载配置文件。
  • ApplicationContext在初始化上下文时就实例化所有单例的Bean(scope="singleton")

2、获取Bean的方法

  • 通过id;

  • 通过类型;

  • ……

  package com.xianhuii.beans;

  import org.springframework.context.ApplicationContext;
  import org.springframework.context.support.ClassPathXmlApplicationContext;

  public class Main {

      public static void main(String[] args) {
          // 1、创建Spring的IOC容器对象
          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
          // 2、获取Bean
          // 方式一:根据id获取Bean
          HelloWorld bean1 = (HelloWorld) context.getBean("helloworld");
          // 方式二:根据class类型获取Bean,要求IOC容器中只能有一个该类型的Bean
          HelloWorld bean2 = context.getBean(HelloWorld.class);
          // 3、使用Bean
      }

  }

5 依赖注入:set方法注入、构造方法注入

  • Spring支持3种依赖注入的方式:

    • 属性注入(setter方法注入)
    • 构造器注入
    • 工厂方法注入(很少使用,不推荐)
  • 实验类:

    public class Car {
        private String brand;   // 品牌
        private String crop;    // 厂商
        private double price;   // 价格
    
        public Car() {
        }
    
        public Car(String brand, String crop, double price) {
            this.brand = brand;
            this.crop = crop;
            this.price = price;
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        public String getCrop() {
            return crop;
        }
    
        public void setCrop(String crop) {
            this.crop = crop;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
        }
    
        @Override
        public String toString() {
            return "Car{" +
                    "brand='" + brand + '\'' +
                    ", crop='" + crop + '\'' +
                    ", price=" + price +
                    '}';
        }
    
    }

1、属性注入

  • setter方法注入的前提:先调用无参构造器创建对象。

  • name:setXxx()方法对应的xxx,与成员变量名无关。

  • value:注入的字面量值。

  • ref:注入其他Bean。

  <?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
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <!-- 配置Car,属性注入(setter方法注入) -->
      <bean id="car" class="Car">
          <property name="brand" value="Audi"></property>
          <property name="crop" value="Yiqi"></property>
          <property name="price" value="40000"></property>
      </bean>

  </beans>

2、构造器注入

  • 默认通过在构造器形参中的顺序进行赋值。

  • 可以显示指明index、name、type等进行注入,它们可以配合使用。

  • 如果有多个构造器,注意使用的注入方法不能引起歧义,否则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"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <!-- 配置Car,构造器注入 -->
      <!-- 默认按照构造器形参的顺序进行注入 -->
      <bean id="car1" class="Car">
          <constructor-arg value="BMW"></constructor-arg>
          <constructor-arg value="huachen"></constructor-arg>
          <constructor-arg value="500000"></constructor-arg>
      </bean>
      <!-- 显示指定构造器形参的索引进行注入,可以自定义顺序 -->
      <bean id="car2" class="Car">
          <constructor-arg index="0" value="BMW"></constructor-arg>
          <constructor-arg index="2" value="500000"></constructor-arg>
          <constructor-arg index="1" value="huachen"></constructor-arg>
      </bean>
      <!-- 显示指定构造器形参的名字进行注入 -->
      <bean id="car3" class="Car">
          <constructor-arg name="brand" value="BMW"></constructor-arg>
          <constructor-arg name="price" value="500000"></constructor-arg>
          <constructor-arg name="crop"  value="huachen"></constructor-arg>
      </bean>
      <!-- 显示指定构造器形参的类型进行注入 -->
      <bean id="car4" class="Car">
          <constructor-arg type="java.lang.String" value="BMW"></constructor-arg>
          <constructor-arg type="double" value="500000"></constructor-arg>
          <constructor-arg type="java.lang.String" value="huachen"></constructor-arg>
      </bean>

  </beans>
  • 可以将value属性,转换成value子标签的方式进行注入:
  <?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
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <bean id="car1" class="Car">
          <constructor-arg>
              <value>Audi</value>
          </constructor-arg>
          <constructor-arg>
              <value>huachen</value>
          </constructor-arg>
          <constructor-arg>
              <value>50000</value>
          </constructor-arg>
      </bean>

  </beans>

6 特殊字符的注入:value子标签

  • 实验类:
  public class Book {
      private int isbn;
      private String bookName;

      public int getIsbn() {
          return isbn;
      }

      public void setIsbn(int isbn) {
          this.isbn = isbn;
      }

      public String getBookName() {
          return bookName;
      }

      public void setBookName(String bookName) {
          this.bookName = bookName;
      }

      @Override
      public String toString() {
          return "Book{" +
                  "isbn=" + isbn +
                  ", bookName='" + bookName + '\'' +
                  '}';
      }

  }
  • 特殊字符注入:
  <?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
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <bean id="book1" class="Book">
          <property name="isbn">
              <value>1001</value>
          </property>
          <!-- 方式一:使用转义字符 -->
          <property name="bookName" value="&lt;&lt;Spring&gt;&gt;"></property>
      </bean>
      <bean id="book2" class="Book">
          <property name="isbn">
              <value>1001</value>
          </property>
          <!-- 方式二:使用CDATA -->
          <property name="bookName">
          <!--
              <![CDATA[想些啥,就写啥]]>
          -->
          <value><![CDATA[<<Spring>>]]]></value>
          </property>
      </bean>

  </beans>

7 引用其他的Bean:ref属性

  • 实验类(Car如上):
  public class Person {
      private String name;
      private int age;
      private Car car;    // 人有一辆车

      public String getName() {
          return name;
      }

      public void setName(String name) {
          this.name = name;
      }

      public int getAge() {
          return age;
      }

      public void setAge(int age) {
          this.age = age;
      }

      public Car getCar() {
          return car;
      }

      public void setCar(Car car) {
          this.car = car;
      }

      @Override
      public String toString() {
          return "Person{" +
                  "name='" + name + '\'' +
                  ", age=" + age +
                  ", car=" + car +
                  '}';
      }

  }
  • 引用其他Bean:
  <?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
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <bean id="person" class="Person">
          <property name="name" value="莫小贝"></property>
          <property name="age" value="12"></property>
          <property name="car" ref="car"></property>
      </bean>
      <bean id="car" class="Car">
          <property name="brand" value="Audi"></property>
          <property name="crop" value="Yiqi"></property>
          <property name="price" value="40000"></property>
      </bean>

  </beans>

8 内部Bean

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="person" class="Person">
            <property name="name" value="莫小贝"></property>
            <property name="age" value="12"></property>
            <property name="car">
                <bean class="Car">
                    <property name="brand" value="Audi"></property>
                    <property name="crop" value="Yiqi"></property>
                    <property name="price" value="40000"></property>
                </bean>
            </property>
        </bean>

    </beans>

9 集合注入:List、Set、数组

1、List

  • 实验类:

    import java.util.List;
    
    public class PersonList {
        private String name;
        private int age;
        private List<Car> cars;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public List<Car> getCars() {
            return cars;
        }
    
        public void setCars(List<Car> cars) {
            this.cars = cars;
        }
    
        @Override
        public String toString() {
            return "PersonList{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", cars=" + cars +
                    '}';
        }
    
    }
  • 注入List:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="person" class="PersonList">
            <property name="name" value="莫小贝"></property>
            <property name="age" value="12"></property>
            <property name="cars">
                <list>
                    <ref bean="car1"/>
                    <ref bean="car2"/>
                    <bean class="Car">
                        <property name="brand" value="Audi"></property>
                        <property name="crop" value="Yiqi"></property>
                        <property name="price" value="40000"></property>
                    </bean>
                </list>
            </property>
        </bean>
        <bean id="car1" class="Car">
            <property name="brand" value="Audi"></property>
            <property name="crop" value="Yiqi"></property>
            <property name="price" value="40000"></property>
        </bean>
        <bean id="car2" class="Car">
            <property name="brand" value="Audi"></property>
            <property name="crop" value="Yiqi"></property>
            <property name="price" value="40000"></property>
        </bean>
    
    </beans>
  • Set、数组只需将标签改为set、array即可。

10 Map注入

  • 实验类:

    import java.util.Map;
    
    public class PersonMap {
        private String name;
        private int age;
        private Map<String, Car> cars;
    
        @Override
        public String toString() {
            return "PersonMap{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", cars=" + cars +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public Map<String, Car> getCars() {
            return cars;
        }
    
        public void setCars(Map<String, Car> cars) {
            this.cars = cars;
        }
    
    }
  • Map注入:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="personMap" class="PersonMap">
            <property name="name" value="莫小贝"></property>
            <property name="age" value="12"></property>
            <property name="cars">
                <map>
                    <!--<entry key="" key-ref="" value="" value-ref="" value-type=""></entry>-->
                    <entry key="Audi" value-ref="car"></entry>
                </map>
            </property>
        </bean>
        <bean id="car" class="Car">
            <property name="brand" value="Audi"></property>
            <property name="crop" value="Yiqi"></property>
            <property name="price" value="40000"></property>
        </bean>
    
    </beans>

11 p命名空间

  • 为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。

  • Spring从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置Bean的属性。</bean>

  • 使用p命名空间后,基于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"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <!-- p命名空间 -->
      <bean id="car" class="Car" p:brand="Audi" p:crop="Yiqi" p:price="40000"></bean>
      <bean id="person" class="Person" p:age="30" p:name="张三" p:car-ref="car"></bean>

  </beans>

12 自动装配

  • Spring IOC容器可以自动装配Bean,需要做的仅仅是在<bean>的autowire属性里指定自动装配的模式。</bean>

  • 基本值不可以自动装配,必须手动配置。

  • 引用值才可以设置成自动装配。

  • 方式:

    • byType(根据类型自动装配)通过已设置好的bean的类型,与此Bean的属性的类型进行匹配。若IOC容器中有多个与目标Bean类型一致的Bean,Spring将无法判定哪个Bean最适合该属性,所以不能执行自动装配。
    • byName(根据名称自动装配):将已设置好的Bean的id,与本类中的setter方法进行匹配。
    • constructor(通过构造器自动装配):当Bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
  • 实验类1:

    public class Address {
        private String city;
        private String street;
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = city;
        }
    
        public String getStreet() {
            return street;
        }
    
        public void setStreet(String street) {
            this.street = street;
        }
    
        @Override
        public String toString() {
            return "Address{" +
                    "city='" + city + '\'' +
                    ", street='" + street + '\'' +
                    '}';
        }
    
    }
  • 实验类2:

    public class Car {
        private String brand;   // 品牌
        private double price;   // 价格
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
        }
    
        @Override
        public String toString() {
            return "Car{" +
                    "brand='" + brand + '\'' +
                    ", price=" + price +
                    '}';
        }
    
    }
  • 实验类3:

    public class Person {
        private String name;
        private Address address;
        private Car car;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
    
        public Car getCar() {
            return car;
        }
    
        public void setCar(Car car) {
            this.car = car;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", address=" + address +
                    ", car=" + car +
                    '}';
        }
    
    }
  • 配置:

    <?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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="address" class="Address">
            <property name="city" value="beijing"></property>
            <property name="street" value="huilongguan"></property>
        </bean>
    
        <bean id="car" class="Car" p:brand="Audi" p:price="400000"></bean>
    
        <!-- autowire="byName":通过已设置好的bean的id值,与此Bean的setter方法名进行匹配 -->
        <bean id="person1" class="Person" p:name="Tom" autowire="byName"></bean>
    
        <!-- autowire="byType":通过已设置好的bean的类型,与此Bean的属性的类型进行匹配 -->
        <bean id="person2" class="Person" p:name="Tom" autowire="byType"></bean>
    
    </beans>

13 Bean之间的关系:继承、依赖

1、继承

  • parent:指定父Bean,实现继承关系。

  • abstract="true":指定为抽象Bean,抽象的Bean不能实例化。

  • abstract不是必须的。

  <?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"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <bean id="address" class="Address" abstract="true">
          <property name="city" value="Beijing"></property>
          <property name="street" value="HuiLongGuan"></property>
      </bean>
      <bean id="address1" parent="address">
          <property name="street" value="WuDaoKou"></property>
      </bean>

  </beans>

2、依赖

  • depends-on="car":表明此Bean创建之前,必须先创建car这个Bean。

  • 依赖只是指定关系,并不会赋值。

  <?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"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans.xsd">

      <!-- 继承 -->
      <bean id="address" class="Address" abstract="true">
          <property name="city" value="Beijing"></property>
          <property name="street" value="HuiLongGuan"></property>
      </bean>
      <bean id="address1" parent="address">
          <property name="street" value="WuDaoKou"></property>
      </bean>

      <!-- 依赖 -->
      <bean id="person" class="Person" depends-on="car">
          <property name="name" value="Jerry"></property>
          <property name="address" ref="address1"></property>
          <property name="car" ref="car"></property>
      </bean>

      <bean id="car" class="Car">
          <property name="brand" value="BMW"></property>
          <property name="price" value="400000"></property>
      </bean>

  </beans>

14 Bean的作用域

  • 在Spring中,可以在<bean>元素的scope属性里设置Bean的作用域。</bean>

  • 默认情况下,Spring只为每个在IOC容器里声明的Bean创建唯一一个实例,整个实例IOC容器范围内都能共享该Bean。所有后续的getBean()调用和Bean引用都将返回这个唯一的Bean实例。该作用域被称为singleton,它是所有Bean的默认作用域。

  • scope:

    • singleton:单例(默认值)。在IOC容器中,只有一个该bean的实例对象。并且该bean的对象会在IOC容器初始化的时候创建。
    • prototype:原型。在IOC容器中,有多个该bean的实例对象。不会在IOC容器初始化的时候创建,而是在每次getBean()的时候才会创建一个新的对象返回。
    • request:一次请求期间。每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境。
    • session:一次会话期间。同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。该作用于仅适用于WebApplicationContext环境。
  • 配置:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--
            scope:配置bean的作用域
                singleton:1、单例,整个IOC容器中只有一个bean的实例;
                           2、该bean的实例会在IOC容器实例化的时候创建。
    
                prototype:1、原型,每次请求getBean()都会新创建一个bean的实例;
                           2、在IOC容器实例化的时候不会创建,在请求getBean()时才会创建。
    
                request:一次请求之间。
                session:一次会话之间。
        -->
        <bean id="address1" class="Address" scope="singleton">
            <property name="city" value="Beijing"></property>
            <property name="street" value="SiDaokou"></property>
        </bean>
    
        <bean id="address2" class="Address" scope="prototype">
            <property name="city" value="Beijing"></property>
            <property name="street" value="SiDaokou"></property>
        </bean>
    
    </beans>
  • 测试:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Object singleton1 = context.getBean("address1");
            Object singleton2 = context.getBean("address1");
            System.out.println(singleton1 == singleton2);   // true
            Object prototype1 = context.getBean("address2");
            Object prototype2 = context.getBean("address2");
            System.out.println(prototype1 == prototype2);   // false
        }
    }

15 使用外部属性文件

  • 在src下创建db.properties文件:

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    jdbc.username=root
    jdbc.password=root
  • 在applicationContext.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: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 
                               https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 引入外部化的配置文件 -->
        <context:property-placeholder location="classpath:db.properties"/>
    
        <!-- 配置c3p0连接池(数据源) -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"></property>
            <property name="jdbcUrl" value="${jdbc.url}"></property>
            <property name="user" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
    
    </beans>
  • 使用:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class SpringTest {
        public static void main(String[] args) throws SQLException {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取数据源
            DataSource dataSource = (DataSource) context.getBean("dataSource");
            // 3、获取连接
            Connection connection = dataSource.getConnection();
            // 4、使用
        }
    }

16 Bean的生命周期

  • 生命周期(不考虑后置处理器):

    1. 调用构造器创建对象;
    2. 给对象的属性赋值;
    3. 调用初始化方法。初始化方法是通过init-method来指定的;
    4. 使用对象;
    5. 调用销毁方法。销毁方法是通过destroy-method来指定的。
  • 实验类:

    public class Car {
        private String brand;   // 品牌
        private double price;   // 价格
    
        public Car() {
            System.out.println("1、创建对象");
        }
    
        public void init() {
            System.out.println("3、init");
        }
    
        public void destroy() {
            System.out.println("5、destroy");
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
            System.out.println("2、setBrand");
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
            System.out.println("2、setPrice");
        }
    
        @Override
        public String toString() {
            return "Car{" +
                    "brand='" + brand + '\'' +
                    ", price=" + price +
                    '}';
        }
    
    }
  • 配置:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="car" class="Car" init-method="init" destroy-method="destroy">
            <property name="brand" value="Ford"></property>
            <property name="price" value="250000"></property>
        </bean>
    
    </beans>
  • 测试:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取bean
            Car car = (Car) context.getBean("car");
            // 3、使用bean
            System.out.println("4、使用car:" + car);
            // 4、关闭容器
            ((ClassPathXmlApplicationContext) context).close();
        }
    }
  • 结果:

    图片说明

17 静态工厂配置Bean

  • 实验类:

    import java.util.HashMap;
    import java.util.Map;
    
    public class StaticObjectFactory {
        private static Map<String, Car> cars = new HashMap<>();
    
        static {
            cars.put("audi", new Car("Audi", 400000));
            cars.put("bmw", new Car("bmw", 500000));
        }
    
        /**
         * 静态工厂方法
         * @return
         */
        public static Car getCar(String carName) {
            return cars.get(carName);
        }
    }
  • 配置:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 静态工厂配置bean
                class:静态工厂类
                factory-method:静态工厂方法
        -->
        <bean id="car" class="StaticObjectFactory" factory-method="getCar">
            <constructor-arg value="audi"></constructor-arg>
        </bean>
    
    </beans>
  • 测试:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取bean
            Car car = (Car) context.getBean("car");
            // 3、使用bean
            System.out.println(car);    //Car{brand='Audi', price=400000.0}
        }
    }

18 实例工厂方法

  • 实验类:

    import java.util.HashMap;
    import java.util.Map;
    
    /**
    
     * 获取Car对象的工厂:
    
     * InstanceObjectFactory iof = new InstanceObjectFactory()
    
     * Car car = iof.getCar("bmw")
       */
       public class InstanceObjectFactory {
       private Map<String, Car> cars = new HashMap<>();
    
       public InstanceObjectFactory() {
           cars.put("audi", new Car("Audi", 400000));
           cars.put("bmw", new Car("bmw", 500000));
       }
    
       /**
    
        * 实例工厂方法
        * @return
          */
          public Car getCar(String carName) {
          return cars.get(carName);
          }
          }
  • 配置:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 实例工厂配置bean
                factory-bean:指定工厂bean
                factory-method:指定工厂方法 
        -->
        <bean id="iof" class="InstanceObjectFactory"></bean>
        <bean id="car" factory-bean="iof" factory-method="getCar">
            <constructor-arg value="bmw"></constructor-arg>
        </bean>
    
    </beans>
  • 测试:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取bean
            Car car = (Car) context.getBean("car");
            // 3、使用bean
            System.out.println(car);    //Car{brand='bmw', price=500000.0}
        }
    }

19 FactoryBean方式配置Bean

  • FactoryBean:

图片说明

  • 实验类:

    import org.springframework.beans.factory.FactoryBean;
    
    public class MyFactoryBean implements FactoryBean<Car> {
        /**
         * 返回最终的对象。
         * @return
         * @throws Exception
         */
        @Override
        public Car getObject() throws Exception {
            return new Car("QQ", 30000);
        }
    
        /**
         * 返回对象的类型。
         * @return
         */
        @Override
        public Class<?> getObjectType() {
            return Car.class;
        }
    
        /**
         * 对象是否为单例。
         * @return
         */
        @Override
        public boolean isSingleton() {
            return true;
        }
    
    }
  • 配置:

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- FactoryBean的方式配置Bean:通过MyFactoryBean的getObject获取。-->
        <bean id="car" class="MyFactoryBean"></bean>
    
    </beans>
  • 测试:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取bean
            Car car = (Car) context.getBean("car");
            // 3、使用bean
            System.out.println(car);    //Car{brand='QQ', price=30000.0}
        }
    }

20 注解方式配置Bean

  • 组件扫描(component scanning):Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件。
  • 特定组件包括:
    • @Comonent:基本注解,标识一个受Spring管理的组件。
    • @Respository:标识持久层组件。
    • @Service:标识服务层(业务层)组件。
    • @Controller:标识表现层组件。
  • 对于扫描到的组件,Spring有默认的命名策略:使用非限定类名,第一个字母小写。也可以在注解中通过value属性值标识组件的名称。
  • context:component-scan:开启注解扫描。
  • 测试目录:

图片说明

  • Controller层:

    package com.xianhuii.controller;
    
    import org.springframework.stereotype.Controller;
    
    /**
    
     * 表现层组件
     * @Controller的作用:等价于在xml配置文件中:
     * <bean id="userController" class="com.xianhuii.controller.UserController"></bean>
     * @Controller注解默认的id值是类名首字母小写,如果想要自己制定,可以使用value属性来指定:
     * @Controller(value = "id") 或 @Controller("id")
        */
       @Controller
       public class UserController {
       }
  • Service层:

    package com.xianhuii.service;
    
    import org.springframework.stereotype.Service;
    
    /**
    
     * 业务层组件
       */
       @Service
       public class UserService {
       }
  • DAO层接口:

    package com.xianhuii.dao;
    
    public interface UserDao {
    }
  • DAO层实现类:

    package com.xianhuii.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoJdbcImpl implements UserDao {
    }
  • 配置:

    <?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 https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 开启注解扫描 -->
        <context:component-scan base-package="com.xianhuii"></context:component-scan>
    
    </beans>
  • 测试:

    package com.xianhuii.test;
    
    import com.xianhuii.controller.UserController;
    import com.xianhuii.dao.UserDaoJdbcImpl;
    import com.xianhuii.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取bean
            UserController userController = (UserController) context.getBean("userController");
            UserService userService = (UserService) context.getBean("userService");
            UserDaoJdbcImpl userDaoJdbc = (UserDaoJdbcImpl) context.getBean("userDaoJdbcImpl");
            // 3、使用bean
            System.out.println(userController);
            System.out.println(userService);
            System.out.println(userDaoJdbc);
        }
    }```
  • 测试结果:

图片说明

21 context:component-scan

    <?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 https://www.springframework.org/schema/context/spring-context.xsd">

        <!-- 开启注解扫描 -->
        <context:component-scan base-package="com.xianhuii"></context:component-scan>

        <!-- 1、include-filter 需要配合use-default-filters="false" 来使用 -->
        <!--<context:component-scan base-package="com.xianhuii" use-default-filters="false">-->
            <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>-->
        <!--</context:component-scan>-->

        <!-- 2、exclude-filter 不需要配合use-default-filters="false" 来使用 -->
        <!--<context:component-scan base-package="com.xianhuii">-->
        <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>-->
        <!--</context:component-scan>-->

        <!-- 3、assignable 通过指定类名的方式来指定扫描的类与不扫描的类 -->
        <!--<context:component-scan base-package="com.xianhuii" use-default-filters="false">-->
            <!--<context:include-filter type="assignable" expression="com.xianhuii.service.UserService"></context:include-filter>-->
        <!--</context:component-scan>-->
    </beans>

22 基于注解的方式装配bean

  • @Autowired:自动装配bean。

  • DAO层接口:

    package com.xianhuii.dao;
    
    public interface UserDao {
        void getUserByUserNameAndPassword();
    }
  • DAO层实现类:

    package com.xianhuii.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoJdbcImpl implements UserDao {
        @Override
        public void getUserByUserNameAndPassword() {
            System.out.println("UserDaoJdbcImpl.getUserByUserNameAndPassword()");
        }
    }
  • 业务层:

    package com.xianhuii.service;
    
    import com.xianhuii.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
    
     * 业务层组件
       */
       @Service
       public class UserService {
       @Autowired
       private UserDao userDao;
    
       public void handleLogin() {
           System.out.println("UserService.handleLogin()");
           // 处理登录的逻辑
           userDao.getUserByUserNameAndPassword();
       }
       }
  • 控制层:

    package com.xianhuii.controller;
    
    import com.xianhuii.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    /**
    
     * 表现层组件
       *
    
     * @Controller的作用:等价于在xml配置文件中: <bean id="userController" class="com.xianhuii.controller.UserController"></bean>
    
     * @Controller注解默认的id值是类名首字母小写,如果想要自己制定,可以使用value属性来指定:
    
     * @Controller(value = "id") 或 @Controller("id")
       */
       @Controller
       public class UserController {
       @Autowired
       private UserService userService;
    
       public void login() {
           System.out.println("UserController.login()");
           userService.handleLogin();
       }
       }
  • 测试:

    package com.xianhuii.test;
    
    import com.xianhuii.controller.UserController;
    import com.xianhuii.dao.UserDaoJdbcImpl;
    import com.xianhuii.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
        public static void main(String[] args) {
            // 1、实例化容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2、获取bean
            UserController userController = (UserController) context.getBean("userController");
            // 3、使用bean
            userController.login();
        }
    }
  • 测试结果:

图片说明

23 @Autowired具体解释

  • @Autowired优先采用类型匹配的方式进行bean的装配。

  • 如果有多个类型兼容的bean匹配了,会使用属性名与bean的id值进行匹配。

  • @Autowired默认情况下required=true,表明此注解修饰的属性必须被装配。可以改为required=false,有就装配,没有就算了。

  • 如下,新增一个DAO实现类:

    package com.xianhuii.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoMyBatisImpl implements UserDao {
        @Override
        public void getUserByUserNameAndPassword() {
            System.out.println("UserDaoMyBatisImpl.getUserByUserNameAndPassword()");
        }
    }
  • 此时,运行由于类型不匹配,同时属性名也不匹配,会报错。

  • 方式一:修改属性名

    package com.xianhuii.service;
    
    import com.xianhuii.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
    
     * 业务层组件
       */
       @Service
       public class UserService {
       @Autowired
       private UserDao userDaoMyBatisImpl;
    
       public void handleLogin() {
           System.out.println("UserService.handleLogin()");
           // 处理登录的逻辑
           userDaoMyBatisImpl.getUserByUserNameAndPassword();
       }
       }
  • 此时,Spring会根据属性名找到id为userDaoMyBatisImpl的bean,测试结果如下:

图片说明

  • 方式二:使用@Qualifier

    package com.xianhuii.service;
    
    import com.xianhuii.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    /**
    
     * 业务层组件
       */
       @Service
       public class UserService {
       @Autowired
       @Qualifier("userDaoMyBatisImpl")
       private UserDao userDao;
    
       public void handleLogin() {
           System.out.println("UserService.handleLogin()");
           // 处理登录的逻辑
           userDao.getUserByUserNameAndPassword();
       }
       }
  • 此时,Spring会根据@Qualifier注解的value值找到id为userDaoMyBatisImpl的bean,测试结果如下:

图片说明