Spring学习笔记

一个核心思想:将对象交给Spring容器管理。

OCP原则:开闭原则。对扩展开发,对修改关闭。

1 基于xml的方式注册Bean

1.1 注册自定义的Bean

1.1.1 <beans>​标签

  • default-autowire​:

  • default-autowire-candidates​:

  • default-destory-method​:

  • default-init-method​:

  • default-lazy-init​:

  • profile​:指定开发环境。

    可以使用以下两种方式指定被激活的环境:

    1. 使用命令行动态参数,虚拟机参数位置加载:-Dspring.profiles.active=test​。
    2. 使用代码的方式设置环境变量:System.setProperty("spring.profiles.active", "dev")​。
    3.      <beans profile="dev">
               <bean id="userService1" class="com.zxs.service.impl.UserServiceImpl"/>
           </beans>
      
  • default-merge​:

  • xml:base​:

  • xml:id​:

  • xml:lang​:

  • xml:space​:

  • xsi:nil​:

  • xsi:noNamespaceSchemaLocation​:

  • xsi:type

1.1.2 <bean>​标签

对于xml配置文件中的<bean>​标签,常用的属性有:

  • id​:bean的id。

  • class​:bean的全限定类名。

  • name​:bean的别名,多个别名之间用逗号,​隔开。

  • scope​:bean的作用域。取值为singleton​或者prototype​。

    • singleton​:单例,默认值,在spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean()​时都是从单例池中获取相同的bean实例对象。
    • prototype​:spring容器初始化的时候不会实例化Bean,当调用getBean()​时才会实例化Bean,每次调用getBean()​方法都会创建一个新的Bean实例对象。
  • lazy-init​:bean的实例化时机。即是否延迟加载。BeanFactory​作为容器时失效,因为BeanFactory​作为容器时,所有的bean都是懒加载的。

    • true​时,当spring容器创建的时候,不会立即创建Bean实例对象,等待用到时再创建bean实例并存储到单例池中去,后续再使用该Bean直接从单例池(需要scope配置为单例)获取即可。
  • init-method​:在bean实例化后,指定的初始化方法。

  • factory-method​:bean工厂方法,应用于静态工厂或实例工厂。

  • factory-bean​:实例工厂bean。

  • destroy-method​:该bean被销毁前要调用的方法。

  • autowire​:自动注入。可以为default|byType|byName|constructor|no。

  • autowire-candidate​:可以为true|default|false。

  • depends-on​:

  • parent​:

  • primary​:

  • abstract​:

1.1.3 <import>标签

用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务模块进行拆分,拆分后,最终通过<import>​标签导入到一个主配置文件中,项目加载主配置文件就连同<import>​导入的文件一并加载了。

<?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">

    <!--导入商品模块配置文件-->
    <import resource="classpath:productModule.xml"/>
    <!--导入用户模块配置文件-->
    <import resource="classpath:userModule.xml"/>
</beans>

1.1.4 <alias>标签(很少使用)

作用:为某个Bean添加别名,与在<bean>​标签上使用name属性添加别名的方式一样。

beanFactory​中维护着一个名为aliasMap​的Map<String, String>​集合,存储别名和beanName之间的映射关系。

<?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="userService" class="com.itheima.service.impl.UserServiceImpl"/>
    <alias name="userService" alias="service"/>
</beans>

1.1.5 <description>标签

作用:用于描述配置文件的信息。它通常位于配置文件的头部,用来提供关于该配置文件的一些额外信息或注释,比如配置的目的、作者、创建日期等。这些信息对于开发人员来说是非常有用的,因为它们能够帮助开发团队更好地理解配置文件的作用及其背景。

    <description>
        <![CDATA[
          This is the Spring configuration file for the Spring-based JUnit tests.
        ]]>
    </description>

1.2 注册非自定义的Bean

1.3 引入外部的配置文件

常见的配置文件为properties​文件。

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
<!--  system-properties-mode="NEVER"表示不加载系统属性,同时也就不会加载系统环境变量了
	  多个配置文件之间用逗号隔开
	  location="classpath:*.properties"  加载所有以properties结尾的配置文件
	  location="classpath*:*.properties"  加载所有以properties结尾的配置文件,前面的一个*表示不加可以从当前工程目录下读取,还可以从它所依赖的jar包中读取
  -->
<context:property-placeholder location="classpath:jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>

<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

2 基于注解方式注册Bean

3 Bean实例化的基本流程

spring容器在初始化的时候,会读取xml配置文件中的<bean>​标签,,并且将每一个<bean>​标签的信息封装成一个BeanDefinition​对象。所有的BeanDefinition​对象存储到BeanDefinitionMap​(该集合在类中定义)集合中,然后Spring遍历该集合,使用反射创建Bean实例对象,创建好所有单例的Bean对象存储在DefaultSingletonBeanRegistry​对象中的一个名为singletonObjects​的集合中,当调用getBean()​方法时从该Map集合中取出Bean的实例对象。

注:BeanDefinitionMap​和singletonObjects​都是ConcurrentHashMap​集合,都定义在org.springframework.beans.factory.support.DefaultListableBeanFactory​类中。

4 Bean的生命周期

3.1 Bean后处理器

spring的后处理器是spring对外开放的重要扩张点,允许我们介入到bean的整个实例化流程中,以达到动态注册BeanDefinition,动态修改BeanDefinition​,以及动态修改Bean的作用。Spring主要有两种后处理器:

  1. BeanFactoryPostProcessor​:Bean工厂后处理器,在BeanDefinitionMap​填充完毕,Bean实例化之前执行。该处理器主要是对BeanDefinition​进行增删改查。
  2. BeanPostProcessor​:Bean后处理器,一般在Bean实例化之后,填充到单例池之前执行。改处理器主要是对Bean​对象进行增删改查。

BeanFactoryPostProcessor​是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。

package com.zxs;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 动态的注册一个BeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition("com.zxs.dao.impl.PeronDaoImpl");
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        defaultListableBeanFactory.registerBeanDefinition("personDao", beanDefinition);


        // 强制修改指定beanName对象的className名
        BeanDefinition userDao = beanFactory.getBeanDefinition("userDao");
        userDao.setBeanClassName("com.zxs.service.impl.UserServiceImpl");
        System.out.println("BeanDefinitionMap填充完毕后回调该方法");
    }
}

spring提供了一个BeanFactoryPostProcessor​的子接口BeanDefinitionRegistryPostProcessor​专门用来注册BeanDefinition​。

对于使用注解注册Bean的方式,其底层是通过ClassPathBeanDefinitionScanner​(该类实现了BeanDefinitionRegistryPostProcessor​接口)类来扫描并注册为Bean。


BeanPostProcessor

image

3.2 Bean的完整生命周期

spring bean的生命周期大体上可以分为三个阶段:

  1. Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否是延迟加载的,是否是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化。
  2. Bean的初始化阶段:Bean创建之后还仅仅是个“半成品”,还需要对Bean实例化的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitalizingBean接口方法的初始化方法、执行自定义init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,Spring的注解功能等。Spring高频面试题Bean的循环引用问题都是在这个阶段体现的。
  3. Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个声明周期。

Bean的初始化阶段是重点。

3.1.1 Bean的初始化阶段

Bean的初始化过程涉及以下几个过程:

  • Bean实例的属性填充。
  • Aware接口属性注入。
  • BeanPostProcessor​的before()​方法回调。
  • Initializing​接口的初始化回调。
  • 自定初始化方法init回调。
  • BeanPostProcessor​的after()​方法回调。

5 spring整合其他框架

5.1 spring整合MyBatis

5.2 spring整合junit

注意:junit必须是5版本。

需要导入的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>6.1.14</version>
</dependency>

具体的测试类的写法

import com.zxs.config.SpringConfig;
import com.zxs.domain.Student;
import com.zxs.service.StudentService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.util.List;


@SpringJUnitConfig(classes = {SpringConfig.class})
public class TestMybatis {
    private StudentService studentService;

    @Autowired
    public TestMybatis(StudentService studentService) {
        this.studentService = studentService;
    }

    @Test
    void test01() {
        List<Student> students = studentService.getStudents();
        for (Student student : students) {
            System.out.println(student);
        }
    }
}