Spring学习笔记——1

news2024/11/25 19:12:26

Spring学习笔记——1

  • 一、Spring入门
    • 1.1、学习路线
    • 1.2、传统Javaweb开发困惑及解决方法
    • 1.3、三种思想的提出和框架概念
      • 1.3.1、IoC、DI和AOP思想提出
      • 1.3.2、框架的基本特点
    • 1.4、Spring概述
    • 1.5、BeanFactory快速入门
    • 1.6、ApplicationContext快速入门
    • 1.7、BeanFactory与ApplicationContext的关系
    • 1.8、BeanFactory和ApplicationContext的继承体系
  • 二、基于XML方式Bean应用
    • 2.1、Bean的配置概述
    • 2.2、beanName和别名配置
    • 2.3、Bean的作用范围scope配置
    • 2.4、Bean的延迟加载
    • 2.5、Bean的初始化方法和销毁方法
    • 2.6、InitializingBean方式
    • 2.7、实例化Bean的方式-构造方法方式
      • 2.7.1、静态工厂方法方式
      • 2.7.2、实例工厂方法方式
      • 2.7.3、有参数的静态工厂和实例工厂方法
      • 2.7.4、实现FactoryBean规范延迟实例化Bean
    • 2.8、注入
      • 2.8.1、注入方式和注入数据类型
      • 2.8.2、自动装配
    • 2.9、命名空间的种类
      • 2.9.1、beans的profile属性切换环境
      • 2.9.2、import标签
      • 2.9.3、alias标签
      • 2.9.1、自定义命名空间的使用步骤
  • 三、Spring常用的三种getBean的API
  • 四、Spring配置非自定义Bean
    • 4.1、DruidDatasource
    • 4.2、Connection
    • 4.3、Date
    • 4.4、SqlSessionFactory
  • 五、Bean实例化的基本流程
    • 5.1、BeanDefinition
    • 5.2、单例池和流程总结
  • 六、Spring的Bean工厂后处理器
    • 6.1、入门
    • 6.2、注册BeanDefinition
    • 6.3、BeanDefinitionRegistryPostProcessor
    • 6.4、完善实例化流程图
    • 6.5、自定义@Component
  • 七、Spring的Bean后处理器
    • 7.1、概念和入门
    • 7.2、before和after方法的执行时机
    • 7.3、案例-日志功能增强
    • 7.4、再次完善实例化基本流程图
  • 八、SpringBean的生命周期
    • 8.1、概述
    • 8.2、初始化阶段执行步骤
    • 8.3、初始化阶段注入属性信息封装
    • 8.4、属性注入的三种情况
    • 8.5、单向对象注入的代码验证
    • 8.6、循环依赖概念及解决方案
    • 8.7、三级缓存的设计原理
    • 8.8、循环依赖源码流程剖析
    • 8.9、Aware接口
  • 九、Spring IoC容器实例化Bean整体流程图
  • 十、Spring xml方式整和第三方框架
    • 10.1、Mybatis整合Spring实现
    • 10.2、Mybatis整合Spring源码解析
    • 10.3、加载外部properties文件
    • 10.4、自定义空间步骤
    • 10.5、自定义命名空间案例

一、Spring入门

1.1、学习路线

在这里插入图片描述

1.2、传统Javaweb开发困惑及解决方法

问题一:层与层之间紧密耦合在了一起,接口与具体实现紧密耦合在了一起

解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象

在这里插入图片描述
问题二:通用的事务功能耦合在业务代码中,通用的日志功能耦合在业务代码中

解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象的代理对象
在这里插入图片描述

1.3、三种思想的提出和框架概念

1.3.1、IoC、DI和AOP思想提出

  • IoC思想

    • lnversion of Control,控制反转,强调的是原来在程序中创建Bean的权利反转给第三方。
  • DI思想

    • Dependency Injection,依赖注入,强调的Bean之间关系,这种关系第三方负责去设置。
  • AOP思想

    • Aspect Oriented Programming,面向切面编程,功能的横向抽取,主要的实现方式就是Proxy

在这里插入图片描述

1.3.2、框架的基本特点

  • 框架(Framework),是基于基础技术之上,从众多业务中抽取出的通用解决方案;
  • 框架是一个半成品,使用框架规定的语法开发可以提高开发效率可以用简单的代码就能完成复杂的基础业务;
  • 框架内部使用大量的设计模式、算法、底层代码操作技术,如反射、内省、xml解析、注解解析等;
  • 框架一般都具备扩展性;
  • 有了框架,我们可以将精力尽可能的投入在纯业务开发上而不用去费心技术实现以及一些辅助业务。

Java中常用的框架:

不同语言,不同领域都有属于自己的框架,使用框架开发是作为程序员的最基础的底线。Java语言中的框架,可以分为基础框架服务框架:

  • 基础框架:完成基本业务操作的框架,如MyBatis、Spring、SpringMVC、Struts2、Hibernate等
  • 服务框架:特定领域的框架,一般还可以对外提供服务框架,如MQ、ES、Nacos等

1.4、Spring概述

spring是一个开源的轻量级Java开发应用框架,可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题,提供了功能强大IOCAOPWeb MVC等功能。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善,不管是Spring哪个领域的解决方案都是依附于在Spring
Framework基础框架的。

Spring框架的历史

  • Jsp 默默扛下所有;
  • MVC+三层架构分工明确,但开发成本及其高;
  • EJB重量级框架出现,走出一个困境,有进入另一个困境;
  • Spring春天来到,随之,SSH风生水起、称霸武林;
  • Spring 稳住江湖大哥位置,SSM开始上位;、
  • Spring本着“拿来主义”的思维快速发展,生态不断健全;
  • SpringBoot 又一里程碑崛起,把“约定大于配置“思想玩儿的炉火纯青;
  • SpringCloud打包了微服务众多解决方案,应对互联网项目更加easy!

Spring Framework技术栈图示

在这里插入图片描述

1.5、BeanFactory快速入门

  • 通过BeanFactory完成IoC思想的实现
    在这里插入图片描述
  1. 导入Spring的jar包或Maven坐标;
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.3.7</version>
</dependency>
  1. 定义UserService接口及其UserServicelmpl实现类;
public interface UserService {
}
public class UserServiceImpl implements UserService {
}
  1. 创建beans.xml配置文件,将UserServicelmpl的信息配置到该xml中;
<bean id="userService" class="com. itheima.service.impl.UserServiceImpl"></bean>
  1. 编写测试代码,创建BeanFactory,加载配置文件,获取UserService实例对象。
//创建工厂对象
DefaultListableBeanFactory BeanFactory = new DefaultListableBeanFactory();
//创建一个读取器(XML文件)
XmlBeanDefinitionReader Reader = new XmlBeanDefinitionReader(BeanFactory);
//读取配置文件给工厂
Reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象
UserService userService = (UserService)BeanFactory.getBean("UserService");
  • 实现DI依赖注入
  1. 定义UserDao接口及其UserDaolmpl实现类;
  2. 修改UserServicelmpl代码,添加一个setUserDao(UserDao userDao)用于接收注入的对象;
public class UserServiceImpl implements UserService {
    public void setUserDao(UserDao userDao){
        System.out.println("BeanFactory调用方法获取userDao设置到此处"+userDao);
    }
}
  1. 修改beans.xml配置文件,在UserDaolmpl的<bean>中嵌入<property>配置注入;
<bean id="UserService" class="com.Smulll.service.Impl.UserServiceImpl">
   <property name="userDao" ref="UserDao"/>
</bean>
<bean id="UserDao" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>
  1. 修改测试代码,获得UserService时,setUserService方法执行了注入操作。
//创建工厂对象
DefaultListableBeanFactory BeanFactory = new DefaultListableBeanFactory();
//创建一个读取器(XML文件)
XmlBeanDefinitionReader Reader = new XmlBeanDefinitionReader(BeanFactory);
//读取配置文件给工厂
Reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象
UserService userService = (UserService)BeanFactory.getBean("UserService");

1.6、ApplicationContext快速入门

ApplicationContext 称为Spring容器,内部封装了BeanFactory,比BeanFactory功能更丰富更强大,使用ApplicationContext进行开发时,xml配置文件的名称习惯写成applicationContext.xml

//创建ApplicationContext,加载配置文件,实例化容器
ApplicationContext applicationContext = new ClassPathxmlApplicationContext("applicationContext.xml");
//根据beanName获得容器中的Bean实例
UserService userService = (UserService) applicationContext.getBean("UserService");
System.out.println(userService) ;

1.7、BeanFactory与ApplicationContext的关系

  1. BeanFactory是Spring的早期接口,称为Spring的Bean工厂
    ApplicationContext是后期更高级接口,称之为Spring容器;
  2. ApplicationContext在BeanFactory基础上对功能进行了扩展,例如:监听功能、国际化功能等。
    BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装;
  3. Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而且ApplicationContext内部还维护着BeanFactory的引用,所以,ApplicationContext与BeanFactory既有继承关系,又有融合关系
  4. Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建,
    ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好

ApplicationContext除了继承了BeanFactory外,还继承了ApplicationEventPublisher(事件发布器)、ResouresPatternResolver(资源解析器)、MessageSource(消息资源)等。但是ApplicationContext的核心功能还是BeanFactory
在这里插入图片描述
在这里插入图片描述

1.8、BeanFactory和ApplicationContext的继承体系

  • BeanFactory的继承体系

BeanFactory是核心接口,项目运行过程中肯定有具体实现参与,这个具体实现就是DefaultListableBeanFactory,而ApplicationContext内部维护的Beanfactory的实现类也是它

在这里插入图片描述

  • ApplicationContext的继承体系

只在Spring基础环境下,即只导入spring-context坐标时,此时ApplicationContext的继承体系
在这里插入图片描述

只在Spring基础环境下,常用的三个ApplicationContext作用如下:

实现类功能描述
ClassPathXmlApplicationContext加载类路径下的xml配置的ApplicationContext
FileSystemXmlApplicationContext加载磁盘路径下的xml配置的ApplicationContext
AnnotationConfigApplicationContext加载注解配置类的ApplicationContext

如果Spring基础环境中加入了其他组件解决方案,如web层解决方案,即导入spring-web坐标,此时ApplicationContext的继承体系

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.7</version>
</dependency>

只在Spring的Web环境下,常用的两个ApplicationContext作用如下:

实现类功能描述
XmlWebApplicationContextweb环境下,加载类路径下的xml配置的ApplicationContext
AnnotationConfigWebApplicationContextweb环境下,加载磁盘路径下的xml配置的ApplicationContext

二、基于XML方式Bean应用

2.1、Bean的配置概述

Spring开发中主要是对Bean的配置,Bean的常用配置:

Xml配置方式功能描述
<bean id=“” class=“”>Bean的id和全限定名配置
<bean name=“”>通过name设置Bean的别名,通过别名也能直接获取到Bean实例
<bean scope=“”>Bean的作用范围, BeanFactory作为容器时取值singleton和prototype
<bean lazy-init=“”>Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效
<bean init-method=“”>Bean实例化后自动执行的初始化方法,method指定方法名
<bean destroy-method=“”>Bean实例销毁前的方法,method指定方法名
<bean autowire=“byType”>设置自动注入模式,常用的有按照类型byType,按照名字byName
<bean factory-bean=“” factory-method=“”/>指定哪个工厂Bean的哪个方法完成Bean的创建

2.2、beanName和别名配置

Bean的基础配置

例如:配置UserDaolmpl由Spring容器负责管理

<bean id="userDao" class="com.itheima.dao .impl.UserDaoImp! " />

此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaolmpl对象,可以根据beanName获取Bean实例

applicationContext.getBean("userDao");

如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName

applicationContext.getBean("com.itheima.dao.impl.UserDaoImpl");

Bean的别名配置

可以为当前Bean指定多个别名,根据别名也可以获得Bean对象

<bean id="userDao" name="aaa,bbb" class="com.itheima.dao.impl.UserDaoImp1" />

此时多个名称都可以获得UserDaolmpl实例对象

applicationContext.getBean("userDao");
applicationContext.getBean("aaa");
applicationContext.getBean("bbb");

2.3、Bean的作用范围scope配置

默认情况下,单纯的Spring环境Bean的作用范围有两个: SingletonPrototype

  • singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;
  • prototype:原型,Spring容器初始化时不会创建Bean实例当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。

2.4、Bean的延迟加载

当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的

<bean id="userDao" class=" com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/>

2.5、Bean的初始化方法和销毁方法

Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作,初始化方法名称和销毁方法名称通过

<bean id="userDao" class=" com.itheima.dao .impl.UserDaoImpl" init-method="init" destroy-method="destroy" />
public class UserDaoImpl implements UserDao {
	public UserDaoImpl() {	System.out.println("UserDaoImpl创建了...");}
	public void init(){	System.out.println("初始化方法...");}
	public void destroy(){ system.out.println("销毁方法...");}
}

2.6、InitializingBean方式

扩展:除此之外,我们还可以通过实现InitializingBean接口,完成一些Bean的初始化操作,如下:

public class UserDaoImpl implements UserDao,InitializingBean {
	public UserDaoImpl(){System.out.println("UserDaoImpl创建了...");}
	public void init(){System.out.println("初始化方法...");}
	public void destroy(){System.out.println("销毁方法...");}
	//执行时机早于init-method配置的方法
	public void afterPropertiesSet() throws Exception {
		System.out.println("InitializingBean...");
	}
}

2.7、实例化Bean的方式-构造方法方式

Bean的实例化配置

Spring的实例化方式主要如下两种:

  • 构造方式实例化: 底层通过构造方法对Bean进行实例化
  • 工厂方式实例化: 底层通过调用自定义的工厂方法对Bean进行实例化

构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的<bean>几乎都是无参构造该方式,此处不在赘述。下面讲解有参构造方法实例化Bean

//有参构造方法
public UserDaoImpl (String name) {}

有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签,嵌入在<bean>标签内部提供构造参数,如下:

<bean id="userDao" class="com.itheima.dao .impl.UserDaoImpl">
	<constructor-arg name="name" value="haohao" />
</bean>

2.7.1、静态工厂方法方式

工厂方式实例化Bean,又分为如下三种:

  • 静态工厂方法实例化Bean
  • 实例工厂方法实例化Bean
  • 实现FactoryBean规范延迟实例化Bean

使用该静态工厂方法的优点:

  1. 可以执行一些创建对象之前其他的逻辑操作
  2. 可以生成一些其他工具类或jar包使用静态方法创造的对象
public class FactoryDemo1 {
    public static UserService CreateUserService(){
        return new UserServiceImpl();
    }
}
<bean id="FactoryDemo1" class="com.Smulll.Factory.FactoryDemo1" factory-method="CreateUserService"></bean>

2.7.2、实例工厂方法方式

使用该工厂方法的优点:

  1. 可以执行一些创建对象之前其他的逻辑操作
  2. 可以生成一些其他工具类或jar包通过方法来创造的对象
package com.Smulll.Factory;

import com.Smulll.service.Impl.UserServiceImpl;
import com.Smulll.service.UserService;

public class FactoryDemo2 {
    public  UserService CreateUserService(){
        return new UserServiceImpl();
    }
}
<!--配置实例工厂方法-->
<!--先设置工厂对象-->
<bean id="factoryDemo2" class="com.Smulll.Factory.FactoryDemo2"></bean>
<!--在设置工厂方法-->
<bean id="factoryUser" factory-bean="factoryDemo2" factory-method="CreateUserService"></bean>

2.7.3、有参数的静态工厂和实例工厂方法

<bean id="factoryUser" factory-bean="factoryDemo2" factory-method="CreateUserService">
    <constructor-arg name="name" value="zhangsan"/>
</bean>

该参数是工厂方法构造Bean方法的参数

2.7.4、实现FactoryBean规范延迟实例化Bean

使工厂类继承FactoryBean<UserDao>
通过getObject()方法返回对象

public class FactoryDemo3 implements FactoryBean<UserDao> {

    @Override
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
<bean id="factoryDemo3" class="com.Smulll.Factory.FactoryDemo3"></bean>

该方法有延迟功能,只有调用类时,才会将getObject()方法返回的类储存到FactoryBean缓存当中

2.8、注入

2.8.1、注入方式和注入数据类型

Bean的依赖注入又两种方式:

注入方式配置方式
通过Bean的set方法注入<property name="userDao" ref="userDao" /><property name="userDao" value="haohao"/>
通过构造Bean的方法进行注入<constructor-arg name="name" ref="userDao"/><constructor-arg name="name" value="haohao"/>

其中,ref是reference的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。value用于注入普通属性值。

依赖注入的数据类型有如下三种:

  • 普通数据类型,例如: String、int、 boolean等,通过value属性指定
  • 引用数据类型,例如: UserDaolmpl、DataSource等,通过ref属性指定。
  • 集合数据类型,例如: List、Map、Properties等。

注:property和constructor-arg的name属性和参数的名字必须要一样

public class UserDaoImpl implements UserDao {
    //注入一个List集合
    private List<String> list;
    public void setList(List<String> list) {
        this.list = list;
    }
    //注入一个字符串类型的Set集合
    private Set<String> userSet;
    public void setUserSet(Set<String> userSet) {
        this.userSet = userSet;
    }
    //注入一个UserDao类的Set集合
    private Set<UserDao> userDaoSet;
    public void setUserDaoSet(Set<UserDao> userDaoSet) {
        this.userDaoSet = userDaoSet;
    }
    //注入一个map集合
    private Map<String,UserDao> map;
    public void setMap(Map<String,UserDao> map) {
        this.map = map;
    }
    //注入Properties
    private Properties properties;
    public void setProperties(Properties properties){
        this.properties = properties;
    }
    public void show(){
        System.out.println(list);
        System.out.println(userSet);
        System.out.println(userDaoSet);
        System.out.println(map);
        System.out.println(properties);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl">
        <!--配置注入一个List类型的集合-->
        <property name="list">
            <list>
                <value>aaa</value>
                <value>sss</value>
            </list>
        </property>
        <!--配置注入一个Set集合-->
        <property name="userSet">
            <set>
                <value>asdsa</value>
                <value>11111</value>
                <value>22222</value>
                <value>33333</value>
            </set>
        </property>
        <!--配置注入一个UserDao类的Set集合-->
        <property name="userDaoSet">
            <set>
                <ref bean="userDao1"></ref>
                <ref bean="userDao2"></ref>
                <ref bean="userDao3"></ref>
                <ref bean="userDao4"></ref>
            </set>
        </property>
        <!--配置注入一个map集合-->
        <property name="map">
            <map>
                <entry key="m1" value-ref="userDao1"></entry>
                <entry key="m2" value-ref="userDao2"></entry>
                <entry key="m3" value-ref="userDao3"></entry>
                <entry key="m4" value-ref="userDao4"></entry>
            </map>
        </property>
        <!--配置一个properties(键值都是string的键值对)-->
        <property name="properties">
            <props>
                <prop key="p1">p111</prop>
                <prop key="p2">p222</prop>
                <prop key="p3">p333</prop>
                <prop key="p4">p444</prop>
            </props>
        </property>
    </bean>

    <bean id="userDao1" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>
    <bean id="userDao2" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>
    <bean id="userDao3" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>
    <bean id="userDao4" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>
</beans>
@Test
public void demo5(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
    UserDao userDao = (UserDao)classPathXmlApplicationContext.getBean("userDao");
    userDao.show();
}

2.8.2、自动装配

扩展:自动装配方式

如果被注入的属性类型是Bean引用的话,那么可以在<bean>标签中使用autowire属性去配置自动注入方式,属
性值有两个:

  • byName:通过属性名自动装配,即去匹配 setXxxid="xxx" (name="xxx")是否一致;
  • byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错。
private UserService userService;
public void setUserService(UserService userService){
    this.userService  = userService;
}
<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl" autowire="byName"></bean>
<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl"></bean>

2.9、命名空间的种类

Spring的xml标签大体上分为两类,一种是默认标签,一种是自定义标签

  • 默认标签:就是不用额外导入其他命名空间约束的标签,例如<bean>标签
  • 自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如<context:property-placeholder/>标签

Spring的默认标签用到的是Spring的默认命名空间

<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http: //www.w3.org/2001/xMschema-instance"
		xsi:schemalocation="http: //www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

该命名空间约束下的默认标签如下:

标签作用
<beans>一般作为xml配置根标签,其他标签都是该标签的子标
<bean>Bean的配置标签,上面已经详解了,此处不再阐述
<import>外部资源导入标签
<alias>指定Bean的别名标签,使用较少

2.9.1、beans的profile属性切换环境

<beans>标签,除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境

<!--配置测试环境下,需要加载的Bean实例-->
<beans profile="test">

</beans>
<!--配置开发环境下,需要加载的Bean实例-->
<beans profile="dev">

</beans>

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

  • 使用命令行动态参数,虚拟机参数位置加载 -Dspring.profiles.active=test
  • 使用代码的方式设置环境变量 System.setProperty("spring.profiles.active","test")

2.9.2、import标签

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

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

2.9.3、alias标签

<alias>标签是为某个Bean添加别名,与在<bean>标签上使用name属性添加别名的方式一样,我们为UserServicelmpl指定四个别名: aaa、bbb、xxx、yyy

<!--配置UserService-->
<bean id="userService" name="aaa,bbb" class="com.itheima.service.imp1.UserserviceImp1">
	<property name="userDao" ref="userDao"/>
</bean>
<!--指定别名-->
<alias name="userservice" alias="xxx"/>
<alias name="userservice" alias="yyy"/>

2.9.1、自定义命名空间的使用步骤

Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用<前缀:标签>形式的标签,称之为自定义标签,自定义标签的解析流程也是Spring xml扩展点方式之一

<!--默认标签-->
<bean id="userDao" class="com.itheima. dao .imp1.userDaoImpl" />
<!--自定义标签-->
<context:property-placeholder/>
<mvc:annotation-driven/>
<dubbo:application name="application"/>

步骤:

  1. 引入坐标,引入jar包
  2. 找命名空间xmlns和xsi
  3. 通过前缀来引入自定义标签

三、Spring常用的三种getBean的API

方法定义返回值和参数
Object getBean (String beanName)根据beanName从容器中获取Bean实例,要求容器中Bean唯一,返回值为Object,需要强转
T getBean (Class type)根据Class类型从容器中获取Bean实例,要求容器中Bean类型唯一,返回值为Class类型实例,无需强转
T getBean (String beanName,Class type)根据beanName从容器中获得Bean实例,返回值为Class类型实例,无需强转
//根据beanName获取容器中的Bean实例,需要手动强转
UserService userService = (UserService)applicationContext.getBean("userService");
//根据Bean类型去容器中匹配对应的Bean实例,如存在多个匹配Bean则报错
UserService userService2 = applicationContext.getBean(UserService.class);
//根据beanName获取容器中的Bean实例,指定Bean的Type类型
UserService userService3 = applicationContext.getBean("userService",UserService.class) ;

四、Spring配置非自定义Bean

以上在xml中配置的Bean都是自己定义的,例如: UserDaolmpl,UserServicelmpl。但是,在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置

配置非自定义的Bean需要考虑如下两个问题:

  • 被配置的Bean的实例化方式是什么?无参构造、有参构造、静态工厂方式还是实例化工厂方式;
  • 被配置的Bean是否需要注入必要属性

4.1、DruidDatasource

配置Druid数据源交由Spring管理
导入Druid坐标

<!--mysql驱动-->
<dependency>
	<groupId>mysql</groupid>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.49</version>
</dependency>
<!-- druid数据源-->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.1.23</version>
</dependency>

配置文件

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property>
    <property name="username" value="xxxxxx"></property>
    <property name="password" value="xxxxxx"></property>
</bean>

4.2、Connection

Connection的产生是通过DriverManager的静态方法getConnection获取的,所以我们要用静态工厂方式配置

<bean class="java.lang.class" factory-method="forName">
	<constructor-arg name="className" value="com.mysql.jdbc.Driver" />
</bean>
<bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype">
	<constructor-arg name="url" value="jdbc:mysql:///mybatis" />
	<constructor-arg name="user" value="root"/>
	<constructor-arg name="password" value="root"/>
</bean>

4.3、Date

产生一个指定日期格式的对象,原始代码如下

String currentTimeStr = "2023-08-27 07:20:00" ;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(currentTimeStr);

可以看成是实例工厂方式,使用Spring配置方式产生Date实例

<bean id="simpleDateFormat" class="java.text.SimpleDateFormat">
	<constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss" />
</bean>
<bean id="date" factory-bean="simpleDateFormat" factory-method="parse">
	<constructor-arg name="source" value="2023-08-27 07:20:00"/>
</bean>

4.4、SqlSessionFactory

配置SqlSessionFactory交由Spring管理
导入Mybatis相关坐标

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.10</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
</dependency>
<!--静态工厂方法-->
<bean id="inputStream" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream">
    <constructor-arg name="resource" value="mybatis-config.xml"></constructor-arg>
</bean>
<!--无参构造实例化-->
<bean id="builder" class="org.apache.ibatis.session.SqlSessionFactoryBuilder"></bean>
<!--实例化工厂-->
<bean id="factory" factory-bean="builder" factory-method="build">
    <constructor-arg name="inputStream" ref="inputStream"></constructor-arg>
</bean>

五、Bean实例化的基本流程

5.1、BeanDefinition

  1. Spring容器在进行初始化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象;
  2. 所有的BeanDefinition存储到一个名为beanDefinitionMapMap集合中去;
  3. Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中;
  4. 当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。

在这里插入图片描述
DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap

public class DefaultListableBeanFactory extends ... implements ... {
	//存储<bean>标签对应的BeanDefinition对象
	//key:是Bean的beanName,value:是Bean定义对象BeanDefinition
	private final Map<String,BeanDefinition> beanDefinitionMap;
}

Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息,反射构造方法或调用指定的工厂方法生成Bean实例对象,所以只要将BeanDefinition注册到beanDefinitionMap这个Map中,Spring就会进行对应的Bean的实例化操作

5.2、单例池和流程总结

Bean实例及单例池singletonObjects,beanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象,存储到单例池singletonObjects中去,在DefaultListableBeanFactory的上四级父类
DefaultSingletonBeanRegistry中,维护着singletonObjects
源码如下:

public class DefaultSingletonBeanRegistry extends ... implements ... {
//存储Bean实例的单例池
key :是Bean的beanName,value:是Bean的实例对象
private final Map<String,object> singleton0bjects = new ConcurrentHashMap(256)

基本流程:

  • 加载xml配置文件,解析获取配置中的每个<bean>的信息,封装成一个个的BeanDefinition对象;
  • BeanDefinition存储在一个名为beanDefinitionMapMap<String,BeanDefinition>中;
  • ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
  • 创建好的Bean实例对象,被存储到一个名为singletonObjectsMap<String,Object>中;
  • 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。

在这里插入图片描述

六、Spring的Bean工厂后处理器

6.1、入门

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

  • BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
  • BeanPostProcessor: Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。

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

BeanFactoryPostProcessor定义如下:

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}

6.2、注册BeanDefinition

  • 使用这种方法,可以不用再spring容器内在创建一个类的<bean>标签
public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("beandefinitionMap填充完毕后回调该方法");
        //1.注册一个beandefinition 创建一个RootBeanDefinition()对象
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClassName("com.Smulll.Dao.Impl.processorImpl");
        //2.将beanFactory强转成DefaultListableBeanFactory类型
        DefaultListableBeanFactory beanFactory1 = (DefaultListableBeanFactory) beanFactory;
        beanFactory1.registerBeanDefinition("processor",rootBeanDefinition);
    }
}
@Test
public void demo2(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Object processor = classPathXmlApplicationContext.getBean("processor");
    System.out.println(processor);
}
<bean class="com.Smulll.processor.MyBeanFactoryProcessor"></bean>

6.3、BeanDefinitionRegistryPostProcessor

Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作

public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        //创建一个RootBeanDefinition()对象
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClassName("com.Smulll.Dao.Impl.processorImpl");
        //不需要强转就可以创建一个Bean
        beanDefinitionRegistry.registerBeanDefinition("personDao",rootBeanDefinition);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }
}

6.4、完善实例化流程图

BeanFactoryPostProcessorSpringBean的实例化过程中的体现

在这里插入图片描述

6.5、自定义@Component

要求:

  • 自定义@MyComponent注解,使用在类上;
  • 使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;
  • 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。

BaseClassScanUtils

public class BaseClassScanUtils {

    //设置资源规则
    private static final String RESOURCE_PATTERN = "/**/*.class";

    public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {

        //创建容器存储使用了指定注解的Bean字节码对象
        Map<String, Class> annotationClassMap = new HashMap<String, Class>();

        //spring工具类,可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = refractory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否属于指定的注解类型
                if(clazz.isAnnotationPresent(MyComponent.class)){
                    //获得注解对象
                    MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                    //获得属value属性值
                    String beanName = annotation.value();
                    //判断是否为""
                    if(beanName!=null&&!beanName.equals("")){
                        //存储到Map中去
                        annotationClassMap.put(beanName,clazz);
                        continue;
                    }

                    //如果没有为"",那就把当前类的类名作为beanName
                    annotationClassMap.put(clazz.getSimpleName(),clazz);

                }
            }
        } catch (Exception exception) {
        }

        return annotationClassMap;
    }

    public static void main(String[] args) {
        Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.Smulll");
        System.out.println(stringClassMap);
    }
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
    String value();
}
@MyComponent("One")
public class OneBean {
}

七、Spring的Bean后处理器

7.1、概念和入门

Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

public class MyBeanPostProcesser implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"postProcessBeforeInitialization");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"postProcessAfterInitialization");
        return null;
    }
}
  • postProcessBeforeInitialization方法和postProcessAfterInitialization方法需要进行手动创建,接口中实现的是null返回值的方法
  • 两者方法在bena创建之后执行

7.2、before和after方法的执行时机

  1. 先执行bean的构造方法
  2. 执行before方法
  3. 执行InitializingBean接口中的afterPropertiesSet()方法
  4. 执行在xml文件中设置的Bean的init-method方法
  5. 执行after方法

在这里插入图片描述

7.3、案例-日志功能增强

在这里插入图片描述
Processor类

package com.Smulll.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Proxy;
import java.util.Date;

public class TimelogBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        //使用动态代理,对目标Bean进行增强,返回proxy对象,进而储存导单例池singletonObjects中
        Object beanproxy = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    //1.输出开始时间
                    System.out.println("方法:" + method.getName() + "开始时间:" + new Date());
                    //2.执行目标方法
                    Object result = method.invoke(bean, args);
                    //3.输出结束时间
                    System.out.println("方法:" + method.getName() + "结束时间:" + new Date());
                    return result;
                });
        return beanproxy;
    }
}

接口实现类

package com.Smulll.Dao.Impl;

import com.Smulll.Dao.UserDao;
import com.Smulll.service.UserService;

public class UserDaoImpl implements UserDao{
    public void show(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("执行了show方法。。。");
    }
    private UserService userService;
    public void setUserService(UserService userService){
        this.userService  = userService;
    }
}

xml文件

<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl" init-method="init"></bean>

<bean class="com.Smulll.processor.TimelogBeanPostProcessor"></bean>

测试方法

@Test
public void demo2(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao)classPathXmlApplicationContext.getBean("userDao");
    userDao.show();
}

在这里插入图片描述

7.4、再次完善实例化基本流程图

BeanPostProcessor在 SpringBean的实例化过程中的体现

在这里插入图片描述

八、SpringBean的生命周期

8.1、概述

Spring Bean的生命周期是从Bean 实例化之后,即通过反射创建出对象之后,导Bean成为一个完整对象,最终储存到单例池中,这个过程被称为SpringBean的生命周期。Spring Bean的生命周期大体上分为三个阶段:

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

8.2、初始化阶段执行步骤

由于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段
Spring Bean的初始化过程涉及如下几个过程:

  • Bean实例的属性填充
  • Aware接口属性注入
  • BeanPostProcessorbefore()方法回调
  • lnitializingBean接口的初始化方法回调
  • 自定义初始化方法init回调
  • BeanPostProcessorafter()方法回调

8.3、初始化阶段注入属性信息封装

BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储

<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>

<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">
    <property name="dao" ref="userDao"></property>
    <property name="username" value="AAA"></property>
</bean>

例如UserService的属性信息:
在这里插入图片描述

8.4、属性注入的三种情况

Spring在进行属性注入时,会分为如下几种情况:

  • 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
  • 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作
  • 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。

8.5、单向对象注入的代码验证

public class UserServiceImpl implements UserService{
    private UserDao dao;
    private String username;

    public UserServiceImpl() {
        System.out.println("实例化UserService");
    }
    public UserServiceImpl(UserDao dao, String username) {
        this.dao = dao;
        this.username = username;
    }
    public void setDao(UserDao dao) {
        System.out.println("执行setDao方法");
        this.dao = dao;
    }
    public void setUsername(String username) {
        this.username = username;
    }
}
public class UserDaoImpl implements UserDao{
    public UserDaoImpl() {
        System.out.println("实例化UserDao");
    }
}

user的<bean>在userService的前面

<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>

<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">
    <property name="dao" ref="userDao"></property>
    <property name="username" value="AAA"></property>
</bean>

执行结果

在这里插入图片描述
user的<bean>在userService的后面

<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">
    <property name="dao" ref="userDao"></property>
    <property name="username" value="AAA"></property>
</bean>

<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>

在这里插入图片描述

8.6、循环依赖概念及解决方案

多个实体之间相互依赖并形成闭环的情况就叫做 “循环依赖”,也叫做 “循环引用”

在这里插入图片描述

<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">
   <property name="dao" ref="userDao"></property>
</bean>

<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl">
   <property name="service" ref="userService"></property>
</bean>
public class UserDaoImpl implements UserDao{
    private UserService service; 
    public void setService(UserService service){
        this.service = service;
    }
}
public class UserServiceImpl implements UserService{
    private UserDao dao;
    public void setDao(UserDao dao) {
        this.dao = dao;
    }
}

在这里插入图片描述

8.7、三级缓存的设计原理

Spring提供了三级缓存存储完整Bean实例半成品Bean实例,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

public class DefaultsingletonBeanRegistry ... {
	//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
	Map<String,Object> singletonObjects = new ConcurrentHashMap(256);
	//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
	Map<String,Object> earlySingletonObjects = new ConcurrentHashMap(16);
	//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
	Map<String,ObjectFactory<?>> singletonFactories = new HashMap(16);
}

8.8、循环依赖源码流程剖析

UserService和UserDao循环依赖的过程结合上述三级缓存描述一下

  • UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
  • UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
  • UserDao 实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
  • UserDao 属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
  • UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
  • UserService注入UserDao;
  • UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

8.9、Aware接口

Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象

Aware接口回调方法作用
ServletContextAwaresetServletContext(ServletContext context)Spring框架回调方法注入ServletContext对象,web环境下才生效
BeanFactoryAwaresetBeanFactory(BeanFactory factory)Spring框架回调方法注入beanFactory对象
BeanNameAwaresetBeanName(String beanName)Spring框架回调方法注入当前Bean在容器中的beanName
ApplicationContextAwaresetApplicationContext(ApplicationContext applicationContext)Spring框架回调方法注入applicationContext对象

九、Spring IoC容器实例化Bean整体流程图

在这里插入图片描述

十、Spring xml方式整和第三方框架

xml整合第三方框架有两种整合方案:

  • 不需要自定义名空间,不需要使用Spring的配置文件配置第三方框架本身内容,例如: MyBatis;
  • 需要引入第三方框架命名空间,需要使用Spring的配置文件配置第三方框架本身内容,例如:Dubbo。

10.1、Mybatis整合Spring实现

Spring整合MyBatis,之前已经在Spring中简单的配置了SqlSessionFactory,但是这不是正规的整合方式,MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。
Spring整合MyBatis的步骤如下:

  • 导入MyBatis整合Spring的相关坐标;
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <!--注意版本,因为版本过低的原因在这里卡了很久!!!-->
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.25.RELEASE</version>
</dependency>
  • 编写Mapper和Mapper.xml;
  • 配置SqlSessionFactoryBeanMapperScannerConfigurer;
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis2"></property>
    <property name="username" value="root"></property>
    <property name="password" value="123456"></property>
</bean>
<!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器-->
<bean  class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.Smulll.mapper"></property>
</bean>

<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">
    <property name="carMapper" ref="carMapper"></property>
</bean>

  • 编写测试代码

10.2、Mybatis整合Spring源码解析

整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象,SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:

  • sqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;
  • MapperScannerConfigurer:"需要进行配置,用于扫描指定mapper注册BeanDefinition;
  • MapperFactoryBean: Mapper的FactoryBean,获得指定Mapper时调用getObject方法;
  • ClassPathMapperScanner: definition.setAutowireMode(2)修改了自动注入状态,所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。

10.3、加载外部properties文件

Spring整合其他组件时就不像MyBatis这么简单了,例如Dubbo框架在于Spring进行整合时,要使用Dubbo提供的命名空间的扩展方式,自定义了一些Dubbo的标签

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

    <!-- 配置Dubbo应用信息 -->
    <dubbo:application name="your-application-name" />

    <!-- 配置注册中心 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

    <!-- 配置服务提供者 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <dubbo:service interface="com.example.YourServiceInterface" ref="yourServiceBean" />

    <!-- 配置其他Bean -->
    <!-- 消费者配置 -->
	<dubbo:consumer check="false" timeout="1000" retries="0"/>
</beans>

通过配置context文件来加载外部properties文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--通过context加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <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>
    <!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器-->
    <bean  class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.Smulll.mapper"></property>
    </bean>

    <bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">
        <property name="carMapper" ref="carMapper"></property>
    </bean>
</beans>

10.4、自定义空间步骤

  • 将自定义标签的约束与物理约束文件与网络约束名称的约束以键值对形式存储到一个spring.schemas文件里,该文件存储在类加载路径的META-INF里,Spring会自动加载到;
  • 将自定义命名空间的名称与自定义命名空间的处理器映射关系以键值对形式存在到一个叫spring.handlers文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到;
  • 准备好NamespaceHandler,如果命名空间只有一个标签,那么直接在parse方法中进行解析即可,一般解析结果就是注册该标签对应的BeanDefinition。如果命名空间里有多个标签,那么可以在init方法中为每个标签都注册一个BeanDefinitionParser,在执行NamespaceHandler的parse方法时在分流给不同的
    BeanDefinitionParser进行解析(重写doParse方法即可)。

10.5、自定义命名空间案例


步骤分析:

  1. 确定命名空间名称、schema虚拟路径、标签名称;
  2. 编写schema约束文件haohao-annotation.xsd
  3. 在类加载路径下创建META目录,编写约束映射文件spring.schemas和处理器映射文件spring.handlers
  4. 编写命名空间处理器HaohaoNamespaceHandler,在init方法中注册HaohaoBeanDefinitionParser
  5. 编写标签的解析器HaohaoBeanDefinitionParser,在parse方法中注册HaohaoBeanPostProcessor
  6. 编写HaohaoBeanPostProcessor
    ============= 以上五步是框架开发者写的,以下是框架使用者写的 ====================
  7. 在applicationContext.xml配置文件中引入命名空间
  8. 在applicationContext.xml配置文件中使用自定义的标签

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

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

相关文章

复习第四章集合+多线程

一、集合 Collection派生 我们在使用集合的时候其实更多的是使用List、Set、Map集合进行操作&#xff0c;List、Set又继承自Collection Collection下的集合为单列集合&#xff0c;可以理解为一个集合当中的数据只代表一个“对象”。 Map派生 Map代表的是存储key-value对的集…

为高性能计算构建,由亚马逊云科技Amazon Graviton3E驱动的最新实例正式可用

亚马逊云科技宣布两款基于最新一代自研芯片Amazon Graviton3E的新实例Amazon Elastic Compute Cloud&#xff08;Amazon EC2&#xff09;Hpc7g和Amazon EC2 C7gn正式可用。 其中&#xff0c;Hpc7g实例专为计算和网络密集型高性能计算&#xff08;HPC&#xff09;工作负载而构建…

K8s安全配置:CIS基准与kube-bench工具

01、概述 K8s集群往往会因为配置不当导致存在入侵风险&#xff0c;如K8S组件的未授权访问、容器逃逸和横向攻击等。为了保护K8s集群的安全&#xff0c;我们必须仔细检查安全配置。 CIS Kubernetes基准提供了集群安全配置的最佳实践&#xff0c;主要聚焦在两个方面&#xff1a;主…

axios 网络应用 axios基本使用

之前学了vue本地应用&#xff0c;通过Vue提供的各种指令&#xff0c;对本地的数据进行操作。 但是现在很少有纯本地的应用了&#xff0c;或多或少都会进行网络数据的交互。这个就想要axios&#xff0c;内部是阿贾克斯&#xff0c;封装之后使用起来更加便捷&#xff0c;并且功能…

【Java练习题汇总】《第一行代码JAVA》综合测试一,汇总Java练习题

Java练习题 综合测试一 1️⃣ 综合测试一 1️⃣ 综合测试一 下面关于Java 的特点不正确的一项是( )。 A. Java 具备跨平台性&#xff0c;可以在任意的操作系统间进行移植 B. Java 编写的程序可以直接解释执行&#xff0c;属于解释型的编程语言类型 C. Java 中具备垃圾收集机制…

Linux安装VScode

从本篇开始&#xff0c;打算有时间就写写在VScode中编写一些ros相关的案例程序用于学习记录。本篇是如何在Linux安装VScode的第一篇。 一、下载VScode 在Linux中打开浏览器输入&#xff1a;https://code.visualstudio.com/Download&#xff0c;选择与你电脑相匹配的版本下载&…

java版本spring cloud 企业工程系统管理 工程项目管理系统源码

&#xfeff; Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&…

浅谈Java转义符\|

浅谈Java转义符\| 看一段程序 String t "a||b||c||d"; String[] temp t.split("\\|\\|"); System.out.println(temp.length); 主要是&#xff1a;"\\|\\|" 代表什么意思&#xff1f; 开始有点蒙&#xff0c;后来仔细一看明白了&#xff0…

易基因:单细胞DNA甲基化与转录组分析揭示猪生发泡卵母细胞成熟的关键调控机制|项目文章

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 在哺乳动物中&#xff0c;窦卵泡内的生发泡(germinal vesicle&#xff0c;GV) 卵母细胞可以保持数月或数年的静止状态。促黄体生成素(luteinizing hormone&#xff0c;LH)激增促进了减数…

740.删除并获得点数

目录 一、题目 二、代码 一、题目 740. 删除并获得点数 - 力扣&#xff08;LeetCode&#xff09; 二、代码 转换成打家劫舍问题 class Solution { public:int deleteAndEarn(vector<int>& nums) {int Max nums[0];//nums中的最大点数for(int i0;i<nums.size(…

iTOP-RK3588开发板Debian系统使用 VNC 远程控制桌面-客户端连接

在 360 软件管家里面下载 VNC Viewer 软件&#xff0c;如下图所示&#xff1a; 然后打开软件&#xff0c;之后如下图所示&#xff1a; 然后我们点击左上角 file-》New connection&#xff0c;如下图所示设置&#xff0c;我们的 VNC Server 设为 192.168.1.14:1&#xff0c;其中…

柴洪峰院士:大模型赋能金融科技思考与展望

本文整理自7月7日世界人工智能大会“AI生成与垂直大语言模型的无限魅力”论坛上中国工程院院士、复旦大学金融科技研究院院长柴洪峰《大模型赋能金融科技思考与展望》的主题分享&#xff0c;从金融垂直模型构建、金融知识图谱与大模型融合以及金融大模型的监管三个方面进行介绍…

大数据Flink(五十五):Flink架构体系

文章目录 Flink架构体系 一、 Flink中的重要角色 二、Flink数据流编程模型 三、Libraries支持

风辞远的科技茶屋:来自未来的信号枪

很久之前&#xff0c;有位朋友问我&#xff0c;现在科技资讯这么发达了&#xff0c;你们还写啊写做什么呢&#xff1f; 我是这么看的。最终能够凝结为资讯的那个新闻点&#xff0c;其实是一系列事情最终得出的结果&#xff0c;而这个结果又会带来更多新的结果。其中这些“得出”…

Ae 效果:CC Kernel

颜色校正/CC Kernel Color Correction/CC Kernel CC Kernel&#xff08;CC 卷积核&#xff09;效果主要用于图像的卷积处理&#xff0c;通过在卷积矩阵中设置不同的权重值&#xff0c;可以实现图像的锐化 Sharpen、模糊 Blur、查找边缘 Find Edges以及浮雕 Emboss等效果。 ◆ …

电脑剪辑视频的软件有哪些?试试这几种视频剪辑工具

视频剪辑可以帮助人们在不同情境下更好地理解和消化视频内容。通过剪辑&#xff0c;可以去除不必要的素材并突出重点&#xff0c;使观看者能够更快地获取信息&#xff0c;并且更容易保持注意力的集中。此外&#xff0c;剪辑可以提高视频质量&#xff0c;例如通过添加音乐、图形…

CAD如何转成JPG图片?学会这招轻松完成转换

当我们使用CAD软件创建设计或模型时&#xff0c;这些文件可能会很大且难以共享。将它们转换为图片可以使它们更易于使用和查看。图片格式通常支持各种软件和平台&#xff0c;这使得它们更容易被打开和共享&#xff0c;而不需要安装CAD软件。此外&#xff0c;图片文件通常比CAD文…

Java多线程面试21题

并行和并发有什么区别&#xff1f; 并行是指两个或者多个事件在同一时刻发生&#xff1b;而并发是指两个或多个事件在同一时间间隔发生。 并行是在不同实体上的多个事件&#xff0c;并发是在同一实体上的多个事件。 在一台处理器上“同时”处理多个任务&#xff0c;在多台处理…

【C++医学影像】支持三维影像后处理PACS系统源码

PACS系统源码是按照DICOM3.0和HL7标准,遵循IHE标准工作流程&#xff0c;以医学影像的采集、传输、存储和诊断为核心&#xff0c;集流程质控、患者信息管理应用和患者关注服务于一体的&#xff0c;覆盖放射、超声、内窥镜和病理等科室的C/S架构的综合医院应用系统。集成三维影像…

sublime配置less的一些坑(1)

仅在sublime的Install Package安装保存less报错 在sublime的Install Package安装less 打开sublime软件,按住CtrlShiftP组合键,弹出的界面中选择Install Package 选中后enter或者回车。等会弹出一个弹窗,大致意思是说你已经成功安装了package control。如果你在此之前已经安装了…