当前位置: 首页 > news >正文

Spring学习笔记——1

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> {@Overridepublic UserDao getObject() throws Exception {return new UserDaoImpl();}@Overridepublic 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;}//注入Propertiesprivate 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定义对象BeanDefinitionprivate 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 {@Overridepublic 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 {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {//创建一个RootBeanDefinition()对象RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();rootBeanDefinition.setBeanClassName("com.Smulll.Dao.Impl.processorImpl");//不需要强转就可以创建一个BeanbeanDefinitionRegistry.registerBeanDefinition("personDao",rootBeanDefinition);}@Overridepublic 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);//扫描到的classString 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;}//如果没有为"",那就把当前类的类名作为beanNameannotationClassMap.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 {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+"postProcessBeforeInitialization");return null;}@Overridepublic 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 {@Overridepublic 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbohttp://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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://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配置文件中使用自定义的标签

相关文章:

Spring学习笔记——1

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与Applicati…...

leetcode 406. 根据身高重建队列

2023.8.2 这题一开始有点让人懵逼的是有两个维度&#xff0c;一个是身高&#xff0c;还一个是前面人高于自己的人数。这种题一般需要先固定一个维度&#xff0c;再去确定另外一个维度&#xff0c;不要想着兼顾。 经过纸上模拟&#xff0c;我的思路是先通过身高进行从大到小排序…...

Matlab实现AGNES算法

在数据分析和机器学习中&#xff0c;聚类是一种常用的无监督学习方法&#xff0c;它可以将数据点按照某种相似度标准进行分组&#xff0c;从而发现数据中的结构和模式。聚类算法有很多种&#xff0c;其中一种比较经典的是AGNES算法&#xff0c;它是一种基于层次的聚类算法&…...

STM32F4_外部SRAM

目录 前言 1. SRAM控制原理 1.1 SRAM功能框图 1.2 SRAM读写时序 2. FSMC简介 2.1 FSMC架构 2.2 FSMC地址映射 2.3 FSMC控制SRAM时序 3. FSMC结构体 4. 库函数配置FSMC 5. 实验程序 5.1 main.c 5.2 SRAM.c 5.3 SRAM.h 前言 STM32F4自带了192K字节的SRAM&#xff1…...

Java的代理模式

java有三种代理模式 静态代理 jdk动态代理 cglib实现动态代理 代理模式的定义&#xff1a; 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xff0c;一个对象不适合或者不能直接引用另一个对象&#xff0c;而代理对象可以在客户端和目标对象之间起到中介的…...

FilterAttributeOnClassMethod

目录 1 BadMethodFilterAttribute 2 FilterAttributeOnClassMethod 2.1 OnMethodExecuted 2.2 OnMethodExecutedAsync 2.3 OnMethodExecuting BadMethodFilterAttribute using System; using System.Threading.Tasks; namespace Flatwhite.Core.Tests.Attributes …...

springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)

配置文件&#xff1a; spring:datasource:primary:jdbc-url: jdbc:mysql://host:3306/数据库?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneAsia/Shanghai&zeroDateTimeBehaviorconvertToNullusername…...

【Golang】Golang进阶系列教程--Go 语言 context 都能做什么?

文章目录 前言核心是 Context 接口&#xff1a;包含四个方法&#xff1a;遵循规则WithCancelWithDeadlineWithTimeoutWithValue 前言 很多 Go 项目的源码&#xff0c;在读的过程中会发现一个很常见的参数 ctx&#xff0c;而且基本都是作为函数的第一个参数。 为什么要这么写呢…...

画图干货!14种uml图类型及示例

1. 什么是 UML UML 是统一建模语言的缩写。UML 图是基于 UML&#xff08;统一建模语言&#xff09;的图表&#xff0c;目的是直观地表示系统及其主要参与者、角色、动作、工件或类&#xff0c;以便更好地理解、更改、维护或记录信息关于系统。简而言之&#xff0c;UML 是一种…...

计算机视觉实验:人脸识别系统设计

实验内容 设计计算机视觉目标识别系统&#xff0c;与实际应用有关&#xff08;建议&#xff1a;最终展示形式为带界面可运行的系统&#xff09;&#xff0c;以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计&#xff08;必做&#xff09;&#xff1a;根据…...

振弦采集仪完整链条的岩土工程隧道安全监测

振弦采集仪完整链条的岩土工程隧道安全监测 隧道工程是一种特殊的地下工程&#xff0c;其建设过程及运行期间&#xff0c;都受到各种内外力的作用&#xff0c;如水压、地震、地质变形、交通荷载等&#xff0c;这些因素都会对隧道的安全性产生影响。因此&#xff0c;对隧道的安…...

NLP实战9:Transformer实战-单词预测

目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] 模型结构图&#xff1a; &a…...

使用Vue.js和Rust构建高性能的物联网应用

物联网(IoT)应用是现代技术的重要组成部分&#xff0c;它们可以在各种场景中&#xff08;例如智能家居&#xff0c;工业自动化等&#xff09;提供无缝的自动化解决方案。在这篇文章中&#xff0c;我们将探讨如何使用Vue.js和Rust构建高性能的物联网应用。 1. 为什么选择Vue.js…...

idea调节文字大小、日志颜色、git改动信息

idea调节菜单栏文字大小&#xff1a; 调节代码文字大小&#xff1a; 按住ctrl滚动滑轮可以调节代码文字大小&#xff1a; 单击文件即可在主窗口上打开显示&#xff1a; idea在控制台对不同级别的日志打印不同颜色 &#xff1a; “grep console”插件 点击某一行的时候&#x…...

避免大龄程序员边缘化:如何在技术行业中保持竞争力

目录 导语持续学习和进修维护专业形象寻找适合自己的领域构建个人品牌和网络拥抱变化和创新实例结语&#xff1a; 导语 导语&#xff1a;随着科技的不断发展&#xff0c;技术行业的竞争日益激烈。对于那些年龄稍长的程序员来说&#xff0c;如何保持竞争力并避免边缘化成为了一…...

Jenkins工具系列 —— 启动 Jenkins 服务报错

错误显示 apt-get 安装 Jenkins 后&#xff0c;自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log&#xff1a;Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明&#xff1a;这里提示的是8080端口号…...

华为数通HCIA-实验环境ensp简介

ensp 路由器&#xff1a;AR系列、NE系列&#xff1b; 模拟器中使用AR2220&#xff1b; 交换机&#xff1a;S系列、CE系列&#xff1b; 模拟器中使用S5700&#xff1b; 线缆&#xff1a;copper——以太网链路&#xff1b; serial——串行链路&#xff0c;在模拟器中用于模…...

SK5代理与IP代理:网络安全中的爬虫利器

一、什么是IP代理与SK5代理&#xff1f; IP代理&#xff1a; IP代理是一种允许用户通过代理服务器进行网络连接的技术。用户请求经由代理服务器中转&#xff0c;从而实现隐藏真实IP地址&#xff0c;保护用户隐私&#xff0c;并在一定程度上突破IP访问限制。常见的IP代理有HTTP…...

实战:Prometheus+Grafana监控Linux服务器及Springboot项目

文章目录 前言知识积累什么是Prometheus什么是Grafana怎样完成数据采集和监控 环境搭建docker与docker-compose安装docker-compose编写 监控配置grafana配置prometheus数据源grafana配置dashboardLinux Host Metrics监控Spring Boot 监控 写在最后 前言 相信大家都知道一个项目…...

[用go实现解释器]笔记1-词法分析

本文是《用go实现解释器》的读书笔记 ​ https://malred-blog​malred.github.io/2023/06/03/ji-suan-ji-li-lun-ji-shu-ji/shi-ti/go-compile/yong-go-yu-yan-shi-xian-jie-shi-qi/go-compiler-1/#toc-heading-6http://个人博客该笔记地址 ​github.com/malred/malanghttp:/…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...