首页 > 其他分享 >Spring6

Spring6

时间:2023-08-11 15:33:25浏览次数:47  
标签:java String spring void class Spring6 public

0x00 环境配置

环境:

  • IDEA >= 2022.1.4
  • JDK 17
  • Maven 3.8.6
  • Spring 6.0.0
  • JUnit 4.13.2
  • Log4j2
  • 新建模块 spring001 -> 高级设置 -> 组 ID

  • 在 /spring001/src/main/java 分别新建软件包

    • com.spring.dao:持久层
    • com.spring.service:业务层,调用持久层
    • com.spring.web:表示层,调用业务层
    • com.spring.client:测试
  • 层和层之间使用接口交互

    • dao

      • 新建接口 UserDao.java

        public interface UserDao {
            void deleteById();
        }
        
      • 新建实现包 impl

        • 新建实现类 UserDaoImplForMySQL.java

          public class UserDaoImplForMySQL implements UserDao {
              @Override
              public void deleteById() {
                  System.out.println("Deleting...");
              }
          }
          
    • service

      • 新建接口 UserService.java

        public interface UserService {
            void deleteUser();
        }
        
      • 新建实现包 impl

        • 新建实现类 UserServiceImpl.java

          public class UserServiceImpl implements UserService {
              private UserDao userDao = new UserDaoImplForMySQL();
              
              @Override
              public void deleteUser() {
                  userDao.deleteById();
              }
          }
          
    • web

      • 新建类 UserAction.java

        public class UserAction {
            private UserService userService = new UserServiceImpl();
        
            public void deleteRequest() {
                userService.deleteUser();
            }
        }
        
    • client

      • 新建类 Test.java

        public class Test {
            public static void main(String[] args) {
                UserAction userAction = new UserAction();
                userAction.deleteRequest();
            }
        }
        
  • 上述项目在需求升级后需要大面积替换原代码,很不方便且容易出错,故使用 Spring 进行调整

0x01 框架主要思想

  • OCP

    • OCP 是软件七大开发原则开闭原则
      • 对扩展开发,对修改关闭
    • OCP 是七大开发原则中最核心、最基本的原则,核心为:扩展功能时未修改原代码
  • DIP

    • DIP 是软件七大开发原则依赖倒置原则
      • 下层依赖上层时符合该原则
    • DIP 核心为面向接口编程
  • IoC

    • IoC 是控制反转,是一种编程思想,是新型设计模式
      • 在程序中不使用硬编码的方式来新建对象和维护对象关系
    • IoC 用于解决当前程序设计既违反了 OCP 又违反 DIP 的问题
  • Spring 框架

    • Spring 是实现 IoC 思想的容器
    • IoC 的实现方法很多,其中比较重要的是依赖注入(DI)
    • DI 常见的方式
      • set 注入
      • 构造方法注入

0x02 框架概述

(1)八大模块

  1. Spring AOP:面向切面编程
  2. Spring Web:支持集成常见的 Web 框架
  3. Spring Web MVC:Spring 自有的 MVC 框架
  4. Spring Webflux:Spring 自有的 响应式 Web 框架
  5. Spring DAO:单独的支持 JDBC 操作的 API
  6. Spring ORM:集成常见的 ORM 框架,如 MyBatis 等
  7. Spring Context:国际化消息、事件传播、验证支持、企业服务、Velocity 和 FreeMarket2 集成
  8. Spring Core:控制反转

(2)特点

  1. 轻量、非侵入式
  2. 控制反转
  3. 面向切面
  4. 容器
  5. 框架

0x03 入门程序

(1)Spring 下载与引用

  • Spring 官网

    graph LR A(Projects<br />Spring Framework)-->Github -->B(Access to Binaries<br />Spring Framework Artifacts) -->C(Spring Repositories<br />https://repo.spring.io) -->Artifacts -->D(plugins-release/<br />org/springframework/<br />spring/6.0.0-RC2) -->E(General/URL to file) -->spring-x.x.x-dist.zip
    字符 说明
    第一个数字 主版本,有可能进行大的架构调整,各大版本之间并不一定兼容
    第二个数字 次版本,在主版本架构不变的前提下,增加了一些新的特性或变化
    第三个数字 增量版本,bug修复,细节的完善
    M 里程碑版本,测试版本,发布版本的前兆
    RC 候选发布版本,稳定版本,并不一定会发布
    RELEASE/NULL 发布版本,稳定版本,在项目中真正可用的版本
  • Maven 引用 Spring

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.2.2RC2</version>
        </dependency>
    </dependencies>
    

(2)创建项目

  • 修改 pom.xml 文件内容

    <!--    修改打包方式-->
        <packaging>jar</packaging>
    
    <!--    配置多个仓库-->
        <repositories>
            <repository>
                <id>repository.spring.milestone</id>
                <name>Spring Milestone Repository</name>
                <url>https://repo.spring.io/milestone</url>
            </repository>
        </repositories>
    
    <!--    依赖项-->
        <dependencies>
    <!--        Spring Context 依赖-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>6.0.4</version>
            </dependency>
    
    <!--        junit 单元测试依赖-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
  • 添加 Spring 配置

    • 在 src/main/resources 目录下新建 spring.xml 文件(名称可变)来配置 Spring

    • 在该文件中配置 bean,从而使 Spring 可以帮助我们管理对象(实现 IoC 的作用)

    • bean 有两个重要属性:idclass

      • id:bean 的唯一标识
      • class:必须填写类的全路径,如com.xxx.xxx.ClassName
        • 带包名的类名,双击需要引用的类名,右键选择“复制引用”
    • 如:<bean id="userBean" class="com.spring6.bean.User" />

    • 也可以引用 Java 自带的无参类,如:<bean id="date" class="java.util.Date" />

(3)编写程序

  • 获取对象

    • 在 src/text/java 目录下新建软件包

    • 在该软件包中新建 Java 类,并写入以下代码:

      public class FirstSpringTest {
          @Test
          public void testFirstSpringCode() {
              ApplicationContext appContext = new ClassPathXmlApplicationContext("spring.xml");
              Object userBean = appContext.getBean("userBean");
          }
      }
      
      • 第一步,获取 Spring 容器对象——ApplicationContext

        • ApplicationContext本质是接口,其中有一个实现类为ClassPathXmlApplicationContext,专门从类路径当中加载 Spring 配置文件的一个 Spring 上下文对象
        • BeanFactoryApplicationContext接口的超级父接口,是 IoC 容器的顶级接口,反映了 Spring 的 IoC 容器底层实际上使用了工厂模式
          • XML 解析、工厂模式、反射机制
      • 第二步,根据 bean 的 id 从 Spring 容器中获取这个对象,通过getBean()方法实现

  • 默认情况下,Spring 会通过反射机制,调用类的无参构造方法来实例化对象,其构造方法如下:

    Class example = Class.forName("com.example");
    Object obj = example.newInstance();
    
  • 获取多个 Spring 容器对象

    ApplicationContext appContext = new ClassPathXmlApplicationContext("spring_1.xml", "spring_2.xml", "spring_3.xml");
    
  • 如果需要访问子类特有属性和方法时

    • 方法一:强转

      import java.util.Date;
      
      public class FirstSpringTest {
          @Test
          public void testFirstSpringCode() {
              // <bean id="date" class="java.util.Date" />
              ApplicationContext appContext = new ClassPathXmlApplicationContext("spring.xml");
              Date date = (Date)appContext.getBean("date");
          }
      }
      
    • 方法二:

      import java.util.Date;
      
      public class FirstSpringTest {
          @Test
          public void testFirstSpringCode() {
              // <bean id="date" class="java.util.Date" />
              ApplicationContext appContext = new ClassPathXmlApplicationContext("spring.xml");
              Date date = appContext.getBean("date", Date.class);
          }
      }
      

(4)启用 Log4j2 日志框架

  • 环境配置

    • 引入依赖

      // pom.xml
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-core</artifactId>
          <version>2.19.0</version>
      </dependency>
      
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-slf4j2-impl</artifactId>
          <version>2.19.0</version>
      </dependency>
      
    • 在 src/main/resources 目录下新建文件 log4j2.xml,并写入以下内容

      <?xml version="1.0" encoding="UTF-8" ?>
      
      <configuration>
      
          <loggers>
      <!--        level 指定日志级别,以下是从低到高的排序-->
      <!--        ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF-->
              <root level="DEBUG">
                  <appender-ref ref="spring6log" />
              </root>
          </loggers>
      
          <appenders>
      <!--        输出日志信息到控制台-->
              <console name="spring6log" target="SYSTEM_OUT">
      <!--            控制日志输出的格式-->
                  <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n" />
              </console>
          </appenders>
      
      </configuration>
      
  • 使用 log4j2 记录日志信息

    • 在 test 中的类 FirstSpringTest.java 里写入以下代码:

      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      public class FirstSpringTest {
          @Test
          public void testFirstSpringCode() {
              Logger logger = LoggerFactory.getLogger(FirstSpringTest.class);
      
              logger.info("Info Message");
              logger.debug("Debug Message");
              logger.trace("Trace Message");
          }
      }
      
      • 第一步,创建日志记录器对象
      • 第二步,记录日志并根据不同的级别来输出日志

0x04 IoC 的实现

(1)IoC 控制反转

  • 控制反转是一种思想
  • 控制反转的目的
    • 降低程序耦合度
    • 达到 OCP 原则
    • 达到 DIP 原则
  • 反转内容
    • 将对象的创建权力交出去,由第三方容器负责
    • 将对象和对象之间的关系维护权交出去,由第三方容器负责
  • 实现方法
    • 依赖注入 DI

(2)依赖注入

  • Spring 通过依赖注入完成对 Bean 对象的创建和其中属性赋值的管理

  • 依赖注入解释

    • 依赖:对象和对象之间的关联关系
    • 注入:一种数据传递行为,使对象之间产生关系
  • 依赖注入常见形式

    • set 注入

      • 基于 set 方法实现的,底层会通过反射机制调用属性对应的 set 方法而后给属性赋值,此方法要求属性必须对外提供 set 方法
      • 构造 set 方法时,方法名必须以 set 开始
      public void setUserDao(UserDao userDao) {
          this.userDao = userDao;
      }
      
      • Spring 调用对应 set 方法前,需要在 spring.xml 中配置相应 Bean 的属性 property

        • property 标签中包含的 name 属性以首字母小写的方式填写 set 方法名中 set 后的全部内容
        • property 标签中包含的 type 属性填写形参的 Bean 的 id
        <bean id="userDaoBean" class="com.spring6.dao.UserDao" />
        <bean id="userServiceBean" class="com.spring6.service.UserService">
            <property name="userDao" ref="userDaoBean" />
        </bean>
        
    • 构造注入

      • 通过构造方法为属性赋值
      public UserService(UserDao userDao) {
          this.userDao = userDao;
      }
      
      • 在 spring.xml 中配置相应 Bean 的属性 constructor-arg

        • constructor-arg 标签中包含的 index 属性指定构造方法的第 index+1 个参数
        • constructor-arg 标签中包含的 ref 属性指定对应的 Bean 的 id
        <bean id="userDaoBean" class="com.spring6.dao.UserDao" />
        <bean id="userServiceBean" class="com.spring6.service.UserService">
            <constructor-arg index="0" ref="userDaoBean" />
        </bean>
        
      • 或类似于 set 注入方法配置 spring.xml

        <bean id="userDaoBean" class="com.spring6.dao.UserDao" />
        <bean id="userServiceBean" class="com.spring6.service.UserService">
            <constructor-arg name="userDao" ref="userDaoBean" />
        </bean>
        
      • 或根据类型注入

        <bean id="userDaoBean" class="com.spring6.dao.UserDao" />
        <bean id="userServiceBean" class="com.spring6.service.UserService">
            <constructor-arg ref="userDaoBean" />
        </bean>
        

(3)set 注入详解

public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
}

a. 注入外部 Bean

  • Bean 定义到外面,在 property 标签中使用 ref 属性进行注入
<bean id="userDaoBean" class="com.spring6.dao.UserDao" />
<bean id="userServiceBean" class="com.spring6.service.UserService">
    <property name="userDao" ref="userDaoBean" />
</bean>

b. 注入内部 Bean

  • 在 Bean 标签中嵌套着 Bean 标签
<bean id="userServiceBean" class="com.spring6.service.UserService">
    <property name="userDao">
        <bean class="com.spring6.dao.UserDao" />
    </property>
</bean>

c. 注入简单类型

  • 可以通过 set 注入的方式给属性赋值

    • 在 src/main/java 新建 com.spring6.bean.User 类

      package com.spring6.bean;
      
      public class User {
          private String username;
          private String password;
          private int age;
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public void setPassword(String password) {
              this.password = password;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "username='" + username + '\'' +
                      ", password='" + password + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
    • 在 src/main/resources/spring.xml 中配置如下,其中重点在于 property 标签的 value 属性用于传值

      <bean id="userBean" class="com.spring6.bean.User">
          <property name="username" value="张三" />
          <property name="password" value="123" />
          <property name="age" value="20" />
      </bean>
      
    • 测试该注入代码如下

      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
      User user = applicationContext.getBean("userBean", User.class);
      System.out.println(user);
      
  • 查看简单类型

    • 在 IDEA 中双击 Shift 键,搜索 BeanUtils
    • 在 BeanUtils.class 中按 Ctrl + F12 搜索 isSimpleValueType 即可查看 Spring 中的全部简单类型

d. 级联属性赋值

  • User.java

    private String name;
    
    // 级联注入添加
    private Group group;
    
    public void setName(String name) {
        this.name = name;
    }
    
    // 级联注入添加
    // 对应 spring.xml 中的 group.name
    public Group getGroup() {
        return group;
    }
    
    // 级联注入添加
    public void setGroup(Group group) {
        this.group = group;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", group=" + group +
                '}';
    }
    
  • Group.java

    private String name;
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Group{" +
                "name='" + name + '\'' +
                '}';
    }
    
  • Spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="name" value="张三" />
    	<property name="group" ref="groupBean" />
        <property name="group.name" value="第一组" />
    </bean>
    
    <bean id="groupBean" class="com.spring6.bean.Group" />
    
  • Test.java

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    User user = applicationContext.getBean("userBean", User.class);
    System.out.println(user);
    
  • 级联注入重点

    • 需要 get 方法
    • 配置中先指定 ref 后再赋值 value

e. 注入数组

  • User.java

    private String[] hobbies;
    
    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "hobbies=" + Arrays.toString(hobbies) +
                '}';
    }
    
  • spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="hobbies">
            <array>
                <value>吃饭</value>
                <value>睡觉</value>
                <value>打豆豆</value>
            </array>
        </property>
    </bean>
    

f. 注入 List 集合

有序可重复

  • User.java

    private List<String> hobbies;
    
    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "hobbies=" + hobbies +
                '}';
    }
    
  • spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="hobbies">
            <list>
                <value>吃饭</value>
                <value>睡觉</value>
                <value>打豆豆</value>
            </list>
        </property>
    </bean>
    

g. 注入 Set 集合

无序不可重复

  • User.java

    private Set<String> hobbies;
    
    public void setHobbies(Set<String> hobbies) {
        this.hobbies = hobbies;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "hobbies=" + hobbies +
                '}';
    }
    
  • spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="hobbies">
            <set>
                <value>吃饭</value>
                <value>吃饭</value>
                <value>睡觉</value>
                <value>睡觉</value>
                <value>打豆豆</value>
            </set>
        </property>
    </bean>
    
    • 有重复元素不会报错,但是仅输出一次

h. 注入 Map 集合

  • User.java

    private Map<Integer, String> phones;
    
    public void setPhones(Map<Integer, String> phones) {
        this.phones = phones;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "phones=" + phones +
                '}';
    }
    
  • spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="phones">
            <map>
                <entry key="1" value="110" />
                <entry key="2" value="119" />
                <entry key="3" value="120" />
            </map>
        </property>
    </bean>
    
    • 对应非简单类型可以使用key-refvalue-ref

i. 注入 Properties

  • User.java

    private Properties properties;
    
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "properties=" + properties +
                '}';
    }
    
  • spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="properties">
            <props>
                <prop key="name">张三</prop>
                <prop key="sex">男</prop>
            </props>
        </property>
    </bean>
    
    • prop 的 key 和 value 只能是 String 型

j. 注入 null 和空字符串

未设置时默认为 null

  • User.java

    private String string1;
    
    private String string2;
    
    public void setString1(String string1) {
        this.string1 = string1;
    }
    
    public void setString2(String string2) {
        this.string2 = string2;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "string1='" + string1 + '\'' +
                ", string2='" + string2 + '\'' +
                '}';
    }
    
  • spring.xml

    <bean id="userBean" class="com.spring6.bean.User">
        <property name="string1">
            <null />
        </property>
    
        <property name="string2" value=""/>
    </bean>
    

k. 注入的值含有特殊字符

  • XML 中有 5 个特殊字符:<>'"&,这些字符会被当做 XML 语法的一部分进行解析

  • 解决方法:

    • 使用转义字符代替

      特殊字符 转义字符
      > &gt;
      < &lt;
      ' &apos;
      " &quot;
      & &amp;
      • 在 spring.xml 中配置值

        <bean id="userBean" class="com.spring6.bean.User">
            <property name="string1" value="2 &lt; 3" />
        </bean>
        
    • 将含有特殊字符的字符串放到<![CDATA[]]>

      • 在 spring.xml 中配置值

        <bean id="userBean" class="com.spring6.bean.User">
            <property name="string2">
                <value><![CDATA[2 < 3]]></value>
            </property>
        </bean>
        

(4)p 命名空间注入

  • 目的:简化 set 注入配置

  • 前提条件

    • 在 XML 头部信息中添加 p 命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p"
    • 需要对应的属性提供 setter 方法
  • User.java

    private String string;
    
    private Date date;
    
    public void setString(String string) {
        this.string = string;
    }
    
    public void setDate(Date date) {
        this.date = date;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "string='" + string + '\'' +
                ", date=" + date +
                '}';
    }
    
  • spring.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:p="http://www.springframework.org/schema/p"
           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="userBean" class="com.spring6.bean.User" p:string="字符串" p:date-ref="dateBean" />
    
        <bean id="dateBean" class="java.util.Date" />
    
    </beans>
    

(5)c 命名空间注入

  • 目的:简化构造方法注入配置

  • 前提条件

    • 在 XML 头部信息中添加 p 命名空间的配置信息:xmlns:c="http://www.springframework.org/schema/c"
    • 需要提供构造方法
  • User.java

    private String string;
        
    private int number;
        
    public User(String string, int number) {
        this.string = string;
        this.number = number;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "string='" + string + '\'' +
                ", number=" + number +
                '}';
    }
    
  • spring.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:c="http://www.springframework.org/schema/c"
           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="userBean" class="com.spring6.bean.User" c:_0="字符串" c:number="123" />
    
    </beans>
    

(6)util 命名空间

  • 目的:配置复用

  • 前提条件:在 XML 头部信息中添加配置信息

    xmlns:util="http://www.springframework.org/schema/util"
    
    xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
    
  • User.java(User2.java)

    private Properties properties;
    
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "properties=" + properties +
                '}';
    }
    
  • spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:util="http://www.springframework.org/schema/util"
           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
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    
        <util:properties id="prop">
            <prop key="key1">value1</prop>
            <prop key="key2">value2</prop>
            <prop key="key3">value3</prop>
        </util:properties>
    
        <bean id="userBean" class="com.spring6.bean.User">
            <property name="properties" ref="prop" />
        </bean>
    
        <bean id="userBean2" class="com.spring6.bean.User2">
            <property name="properties" ref="prop" />
        </bean>
    
    </beans>
    
  • Test.java

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    User user = applicationContext.getBean("userBean", User.class);
    User2 user2 = applicationContext.getBean("userBean2", User2.class);
    System.out.println(user);
    System.out.println(user2);
    

(7)基于 XML 的自动装配

  • Spring 可以完成自动化的注入,又称自动装配,基于 set 方法

  • 方式:根据名称或类型装配

  • 根据名称

    • spring.xml

      <bean id="userService" class="com.spring6.service.UserService" autowire="byName" />
      <bean id="userDao" class="com.spring6.dao.UserDao" />
      
      • 其中,在第二行的 bean 里,属性 id 必须为 set 方法名中除set外其他内容首字母小写
  • 根据类型

    • spring.xml

      <bean id="userDao" class="com.spring6.dao.UserDao"></bean>
      <bean id="userDao2" class="com.spring6.dao.UserDao2"></bean>
      <bean id="userService" class="com.spring6.service.UserService" autowire="byType" />
      

(8)Spring 引入外部属性配置

  • 设置外部属性配置。在 src/main/resources 下新建 setting.properties

    username=root
    password=1234
    
  • 引入外部 properties 文件。在 spring.xml 中进行下述操作

    • 引入 context 命名空间

    • 指定属性配置文件的路径

    • 使用${key}方法从外部属性配置文件中取值

      • key 值首先加载当前操作系统默认的值,导致username对应的 value 为当前系统管理员名称,为避免上述问题并使 key 名称易于理解,可使用添加前缀的操作

        user.username=root
        user.password=1234
        
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 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="setting.properties" />
    
        <bean id="userBean" class="com.spring6.bean.User">
            <property name="username" value="${user.username}" />
            <property name="password" value="${user.password}" />
        </bean>
    
    </beans>
    

0x05 Bean 的作用域

(1)singleton

Singleton:单例

  • 默认情况下,Bean 是单例的,其在 Spring 上下文初始化的时候实例化,每一次调用getBean()方法时,都返回那个单例对象
  • 在配置 Bean 时可以通过添加 scope 属性值来设置 Bean 是单例或多例

(2)prototype

Prototype:原型 / 多例

  • 当 Bean 的 scope 属性设置为Prototype时,Spring 上下文初始化时不会初始化这些 Bean,每一次调用getBean()方法时,才实例化该对象

(3)其他 Scope

在 pom.xml 中添加依赖 SpringMVC,此时该 Spring 项目为 Web 项目

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-webmvc</artifactId>
 <version>6.0.4</version>
</dependency>
  • request
    • 仅限于在 Web 应用中使用
    • 一次请求中只有一个 Bean
  • session
    • 仅限于在 Web 应用中使用
    • 一次会话中只有一个 Bean
  • global session
    • portlet 应用中专用的
    • 如果在 Servlet 的 Web 应用中使用时,其效果与 session 相同
  • application
    • 仅限于在 Web 应用中使用
    • 一个应用对应一个 Bean
  • websocket
    • 仅限于在 Web 应用中使用
    • 一个 websocket 生命周期对应一个 Bean
  • 自定义 Scope

(4)自定义 Scope

以自定义线程级 scope 为例:一个线程对应一个 Bean

  • 自定义 Scope,实现 Scope 接口

    • Spring 内置了线程范围的类:org.springframework.context.support.SimpleThreadScope
  • 将自定义的 Scope 注册到 Spring 容器中

    <!-- filename: spring-scope.xml -->
    
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="myThread">
                    <bean class="org.springframework.context.support.SimpleThreadScope" />
                </entry>
            </map>
        </property>
    </bean>
    
  • 使用 Scope

    <!-- filename: spring-scope.xml -->
    <bean id="userBean" class="com.spring6.bean.User" scope="myThread" />
    
  • 测试

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    User user = applicationContext.getBean("userBean", User.class);
    System.out.println(user);
    
    new Thread(() -> {
        User user2 = applicationContext.getBean("userBean", User.class);
        System.out.println(user2);
    }).start();
    

0x06 GoF 之工厂模式

  • 设计模式定义:一种可以被重复利用的解决方案

  • GoF:Gang of Four,四人组,出自《设计模式》

    • 《设计模式》描述了 23 种设计模式,可分成三大类

      1. 创建型:解决对象创建问题

        单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式

      2. 结构型:一些类或对象组合在一起的经典结构

        代理模式、装饰模式、适配器模式、组合模式、享元模式、外观模式、桥接模式

      3. 行为型:解决类或对象之间交互问题

        策略模式、模板方法模式、责任链模式、观察者模式、迭代子模式、命令模式、备忘录模式、状态模式、访问者模式、中介模式、解释器模式

(1)工厂模式的三种形态

  • 简单工厂模式(Simple Factory)
    • 又称静态工厂方法模式,不属于 23 种设计模式之一,是工厂方法模式的一种特殊实现
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)

(2)简单工厂模式

  • 抽象产品角色

    // filename: Weapon.java
    
    public abstract class Weapon {
        public abstract void attack();
    }
    
  • 具体产品角色

    // filename: Tank.java
    
    public class Tank extends Weapon {
        @Override
        public void attack() {
            System.out.println("Tank attack");
        }
    }
    
  • 工厂类角色

    // filename: WeaponFactory.java
    
    public class WeaponFactory {
        public static Weapon get(String weaponType) {
            if ("Tank".equals(weaponType)) {
                return new Tank();
            } else {
                throw new RuntimeException("Weapon type not supported");
            }
        }
    }
    
  • 测试

    // filename: Test.java
    
    public class Test {
        public static void main(String[] args) {
            Weapon tank = WeaponFactory.get("Tank");
            tank.attack();
        }
    }
    
  • 简单工厂模式优点

    • 客户端程序不需要关心对象的创建细节,需要时索要即可,初步实现了生产和消费的责任分离
  • 简单工厂模式缺点

    • 工厂类如果出现问题会导致整个系统瘫痪
    • 不符合 OCP 开闭原则,在系统扩展时需要修改工厂类
  • Spring 中的 BeanFactory 就使用了简单工厂模式

(3)工厂方法模式

  • 抽象产品角色

    // filename: Weapon.java
    
    public abstract class Weapon {
        public abstract void attack();
    }
    
  • 具体产品角色

    // filename: Tank.java
    
    public class Tank extends Weapon {
        @Override
        public void attack() {
            System.out.println("Tank attack");
        }
    }
    
  • 抽象工厂角色

    // filename: WeaponFactory.java
    
    public abstract class WeaponFactory {
        public abstract Weapon get();
    }
    
  • 具体工厂角色

    // filename: TankFactory.java
    
    public class TankFactory extends WeaponFactory {
        @Override
        public Weapon get() {
            return new Tank();
        }
    }
    
  • 测试

    // filename: Test.java
    
    public class Test {
        public static void main(String[] args) {
            WeaponFactory weaponFactory = new TankFactory();
            Weapon tank = weaponFactory.get();
            tank.attack();
        }
    }
    
  • 工厂方法模式优点

    • 创建对象时仅需要知到名称即可
    • 扩展性高
    • 屏蔽产品的具体实现
  • 工厂方法模式缺点

    • 每增加一个产品就需要对应增加一个工厂,使得系统中类的个数成倍上升,增加了系统复杂度

(4)抽象工厂模式

  • 对比

    工厂方法模式 抽象工厂模式
    针对目标 一个产品系列 多个产品系列
    实现效果 一个产品系列一个工厂类 多个产品系列一个工厂类
  • 特点:抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式,抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。多个抽象产品类中,每个抽象产品类可以派生出多个具体产品类;一个抽象工厂类可以创建出多个具体产品类的实例

  • 抽象工厂模式优点:

    • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
  • 抽象工厂模式缺点

    • 产品族扩展十分困难

0x07 Bean 的实例化方式

(1)通过构造方法实例化

  • 默认情况下会调用 Bean 的无参数构造方法

(2)通过简单工厂模式实例化

  • 创建产品类 Star.java

    public class Star {
        public Star() {
            System.out.println("Star 无参构造方法");
        }
    }
    
  • 创建工厂类 StarFactory.java

    • 静态方法
    public class StarFactory {
        public static Star get() {
            return new Star();
        }
    }
    
  • 在配置文件中实例化 Bean

    <bean id="starFactory" class="com.spring.bean.StarFactory" factory-method="get"/>
    
  • 在测试文件中测试

    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Star star = applicationContext.getBean("starFactory", Star.class);
        System.out.println(star);
    }
    

(3)通过 factory-bean 实例化

  • 具体产品类:Tank.java

    public class Tank {
        public Tank() {
            System.out.println("Tank 无参构造");
        }
    }
    
  • 具体工厂类:TankFactory.java

    • 实例方法
    public class TankFactory {
        public Tank get() {
            return new Tank();
        }
    }
    
  • 在配置文件中实例化 Bean

    <bean id="tankFactory" class="com.spring.bean.TankFactory" />
    <bean id="tank" factory-bean="tankFactory" factory-method="get" />
    
  • 在测试文件中测试

    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Tank tank = applicationContext.getBean("tank", Tank.class);
        System.out.println(tank);
    }
    

(4)通过 FactoryBean 接口实例化

  • 在 Spring 中,当编写了类直接实现 FactoryBean 接口后,factory-bean 会自动指向实现 FactoryBean 接口的类,factory-method 会自动指向getObject()方法

  • 创建产品类:Person.java

    public class Person {
        public Person() {
            System.out.println("person");
        }
    }
    
  • 创建工厂类:PersonFactory.java

    import org.springframework.beans.factory.FactoryBean;
    
    public class PersonFactory implements FactoryBean<Person> {
        @Override
        public Person getObject() throws Exception {
            return new Person();
        }
    
        @Override
        public Class<?> getObjectType() {
            return null;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    
  • 在 spring.xml 配置文件中配置

    <bean id="personFactory" class="com.spring.bean.PersonFactory" />
    
  • 测试

    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Person person = applicationContext.getBean("personFactory", Person.class);
        System.out.println(person);
    }
    

(5)BeanFactory 与 FactoryBean 的区别

  • BeanFactory 是 Spring IoC 容器的顶级对象,负责创建 Bean 对象
  • FactoryBean 是一个 Bean,能够辅助 Spring 实例化其他 Bean 对象的一个 Bean
    • 在 Spring 中,Bean 可分为普通 Bean 和 工厂 Bean

(6)注入自定义 Date

  • Student.java

    private Date birth;
    
    public void setBirth(Date birth) {
        this.birth = birth;
    }
    
    @Override
    public String toString() {
        return "Student{" +
                "birth=" + birth +
                '}';
    }
    
  • StudentFactory.java

    public class StudentFactory implements FactoryBean<Date> {
        private String strDate;
    
        public StudentFactory(String strDate) {
            this.strDate = strDate;
        }
    
        @Override
        public Date getObject() throws Exception {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse(strDate);
            return date;
        }
    
        @Override
        public Class<?> getObjectType() {
            return null;
        }
    }
    
  • spring.xml

    <bean id="studentFactory" class="com.spring.bean.StudentFactory">
        <constructor-arg index="0" value="2000-01-01" />
    </bean>
    
    <bean id="student" class="com.spring.bean.Student">
        <property name="birth" ref="studentFactory" />
    </bean>
    
  • Test.java

    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = applicationContext.getBean("student", Student.class);
        System.out.println(student);
    }
    

0x08 Bean 的生命周期

生命周期的本质在于程序在某个时间点调用了某个类的某个方法

Bean 生命周期的管理可以参考 Spring 源码中 AbstractAutowireCapableBeanFactory 类的 doCreateBean()方法

(1)五步

  1. 实例化 Bean
  2. Bean 属性赋值
  3. 初始化 Bean
  4. 使用 Bean
  5. 销毁 Bean
  • User.java

    public class User {
        private String name;
    
        public void destroyBean() {
            System.out.println("Step 5");
        }
    
        public void initBean() {
            System.out.println("Step 3");
        }
    
        public void setName(String name) {
            System.out.println("Step 2");
            this.name = name;
        }
    
        public User() {
            System.out.println("Step 1");
        }
    }
    
  • spring.xml

    <bean id="user" class="com.spring.bean.User" init-method="initBean" destroy-method="destroyBean">
        <property name="name" value="字符串" />
    </bean>
    
  • Test.java

    @Test
    public void test() {
        ApplicationContext applicationContext = new sPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println("Step 4  " + user);
    
        // 手动关闭 Spring 容器后 Spring 才会销毁 Bean
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        context.close();
    }
    
  • 测试结果

    Step 1
    Step 2
    Step 3
    Step 4 com.spring.bean.User@1165b38
    Step 5
    

(2)七步

  1. 实例化 Bean
  2. Bean 属性赋值
  3. 标签:java,String,spring,void,class,Spring6,public
    From: https://www.cnblogs.com/SRIGT/p/17623129.html

相关文章

  • 《Spring6核心源码解析》已完结,涵盖IOC容器、AOP切面、AOT预编译、SpringMVC,面试杠杠
    作者:冰河星球:http://m6z.cn/6aeFbs博客:https://binghe.gitcode.host文章汇总:https://binghe.gitcode.host/md/all/all.html源码地址:https://github.com/binghe001/spring-annotation-book沉淀,成长,突破,帮助他人,成就自我。大家好,我是冰河~~提起Spring,可以这么说,Spring几乎......
  • spring6 ioc aop 从入门到精通零基础进阶学习路线?
    当你已经掌握了Spring框架的基础知识以及IoC和AOP的核心概念后,可以进一步深化你的学习。以下是更详细的学习路线:1.IoC容器进阶:-学习如何自定义Bean的初始化和销毁方法,并了解Bean生命周期的各个阶段。-深入了解Spring的作用域(Scope)概念,如单例模式、原型模式、会话模式和请求模......
  • Spring6 初始
    Spring6初始@目录Spring6初始每博一文案:1.初始Spring61.1OCP开闭原则1.2依赖倒置原则DIP1.3控制反转IoC2.Spring初始2.1Spring特点2.2Spring6的下载:2.3Spring的jar文件3.第一个Spring程序的编写4.第一个Spring程序详细剖析4.1bean标签的id属性可以重复吗?4.2......
  • 运行 Spring6 报错 Internal error: (java.lang.ExceptionInInitializerError)
    问题描述:使用2019.3.5版本的IDEA构建Spring6项目。因为Spring6要求JDK的版本最低是Java17,我就直接在2019.3.5版本的IDEA中创建新的项目时使用了JDK17。 运行项目时IDEA 出现如下报错信息:Error:Internalerror:(java.lang.ExceptionInInitializerErro......
  • spring6整合JUnit5
    1. Spring对JUnit4的支持   136准备工作:pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:sch......
  • Spring6 探析之@Lazy 注解
    Spring6探析之@Lazy注解介绍在实际项目中,如果我们加载了过多的实例或配置,会导致Spring的启动速度非常慢,@Lazy注解就是为了解决这种问题。@Lazy注解采用懒汉模式的单例模式,Spring容器启动时,被标记的实例不会创建,只有在第一次使用时才会创建实例,这样就解决了Spring启动......
  • Spring6 探析之@Conditional 注解
    Spring6探析之@Conditional注解介绍我们写业务逻辑时,会用到if-else条件,@Conditional注解可以通过条件判断是否要将Bean注入到IOC容器中,它可以标记在类和方法上,我们先看一下源码吧@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME......
  • Spring6 探析之@PropertySource 注解
    Spring6探析之@PropertySource注解介绍@PropertySource注解用于加载配置类,在使用Spring时,我们可以使用@PropertySource注解将自定义的配置文件加载到Spring中,方便我们的自定义的开发下面是@PropertySource的源码@Target(ElementType.TYPE)@Retention(RetentionPol......
  • Spring6 探析之@Import 注解
    Spring6探析之@Import注解简介@Import注解与@Bean注解的作用相同,都可以将实例注册到IOC中,但区别是,@Import只能作用在类上,并且使用@Import时,不需要再使用@Configuration注解下面是@Import的源码@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documen......
  • Spring6 探析之@Bean 注解
    Spring6探析之@Bean注解介绍@Bean是spring的常用注解,他可以标记在方法和注解上,可以将方法的返回值放在IOC容器中,它又以下特点单例可指定bean的名称可控制bean的初始化与销毁方法让我们看一下@Bean的源码吧@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})......