spring整合框架
循环引用
BeanCurrentlyInCreationException
当A对象需要引用B对象,而B对象也需要A对象的时候就是双向,当spring属性填充为双向注入的时候叫做循环依赖,也叫做循环引用
spring提供了三级缓存存储完整的bean和半成品bean,用于解决循环引用的问题:
例如:userDao与userService双向注入
userService实例化对象,但尚未初始化,将半成品存储到三级缓存singletonObjects
userService属性注入,需要userDao,从缓存中获取,没有userDao
userDao实例化对象,尚未初始化,将userDao存储到三级缓存singletonFactories
userDao属性注入,需要userService,从三级缓存获取userService,userService从三级缓存移入二级缓存earlySingletonObjects
userDao执行其他生命周期,最终为完整bean,存储到一级缓存singletonObjects,删除二三级缓存
userService注入userDao
userSerive执行其他生命周期,成为完整bean,存储到一级缓存singletonObjects,删除二三级缓存
常用的aware接口
接口名称 | 回调方法 | 作用 |
ServletContextAware | getServletContext() | 注入servletContext对象 |
BeanFactoryAware | setBeanFactory() | 注入beanFactory |
BeanNameAware | setBeanName() | 注入beanName |
ApplicationContextAware | setApplicationContext() | 注入applicationContext |
spring整合mybatis
在bean的配置文件中添加bean:
<!--数据源对象-->
<bean id="dataSources" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/4031"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--配置SqlSessionFactoryBean,将SqlSessionFactory存储到spring容器-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--为SqlSessionFactory注入数据源-->
<property name="dataSource" ref="dataSources"></property>
</bean>
<!--扫描指定包,产生Mapper对象存储到spring容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注入需要扫描的包-->
<property name="basePackage" value="com.liku.mapper"></property>
</bean>
由于是service层调用数据持久层,因此在service层我们添加一个属性,并调用mapper文件里面查询所有的方法:
public class UserServiceImpl implements UserService {
private SysuserMapper mapper;
public void setMapper(SysuserMapper mapper) {
this.mapper = mapper;
}
@Override
public void show() {
mapper.selectAll().forEach(System.out::println);
}
}
在bean中我们添加实例化service层的bean:
bean id="service" class="com.liku.service.impl.UserServiceImpl">
<!--引用名称为类名的小写,不然会报没有这个bean异常-->
<property name="mapper" ref="sysuserMapper"></property>
</bean>
上面要注意引用的需要是类名的小写,不然会报没有这个bean的异常
接下来获取service对象,并且调用show方法就可以对进行数据库的查询。
上面的操作相关类有四个:
SqlSessionFactoryBean:配置创建SqlSessionFactory的bean工厂,用于产生SqlSessionFactory
MapperScannerConfigurer:配置MapperScannerConfigurer用于扫描指定包下面的mapper,并且注册BeanDefinition
MapperFactoryBean:Mapper的FactoryBean,调用getObject()获得指定的Mapper
ClassPathMapperScanner:修改自动注入状态 definition.setAutowireMode(2),MapperFactoryBean中的setSqlSessionFactory会自动注入进去
读取数据库配置文件连接数据库
新建配置文件【jdbc.properties】
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/4031
user=root
pwd=123456
在bean的配置文件beans属性中添加:
xmlns:context="http://www.springframework.org/schema/context"
并且添加上对应的xsi:schemaLocation
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
加载配置文件:
<context:property-placeholder location="classpath:jdbc.properties"/>
通过el表达式为数据源对象注入值
<bean id="dataSources" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${pwd}"></property>
</bean>
完整beans的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源对象-->
<bean id="dataSources" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${pwd}"></property>
</bean>
<!--配置SqlSessionFactoryBean,将SqlSessionFactory存储到spring容器-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--为SqlSessionFactory注入数据源-->
<property name="dataSource" ref="dataSources"></property>
</bean>
<!--扫描指定包,产生Mapper对象存储到spring容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注入需要扫描的包-->
<property name="basePackage" value="com.liku.mapper"></property>
</bean>
<bean id="service" class="com.liku.service.impl.UserServiceImpl">
<!--引用名称为类名的小写,不然会报没有这个bean异常-->
<property name="mapper" ref="sysuserMapper"></property>
</bean>
</beans>
实例化service层调用方法,查询数据库成功
外部命名空间标签的执行流程
将自定义标签的约束与物理约束文件、网络约束名称的约束 以键值对形式存储到一个spring.schemas文件里,将该文件存储在类加载器路径的META-INF里,spring会自动加载;
将自定义命名空间的名称 与 自定义命名空间的处理器映射关系 以键值对形式存到一个叫spring.handler文件中;
准备好NameSpaceHandler,如果命名空间只有一个标签,那么直接在parse方法中进行解析即可,一般结果就是注册该标签对应的BeanDefinition;如果有多个标签,那么可以在init方法中为每个标签注册一个BeanDefinitionParser,在执行parse方法的时候根据不同的标签,解析成不同的BeanDefinitionParser
通过自定义标签,向spring容器中自动注入一个BeanPostProcessor
分析:
确定命名空间名称、schema虚拟路径、标签名称
编写schema约束文件xx.xsd
在类加载路径下创建META-INF目录,编写约束映射文件spring.schemas和处理器映射文件spring.handlers
编写命名空间处理器xxNameSpaceHandler,在init方法中注册xxBeanDefinitionParser
编写标签的解析器xxBeanDefinitionParser,在parse方法中注册xxBeanPostProcessor
编写xxBeanPostProcessor
在applicationContext.xml配置文件中引入命名空间
在applicationcontext.xml中使用自定义标签
实现上面的步骤:
第一步:创建xsd文件【liku.xsd】,里面设置标签和命名空间
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.liku.com/lulu">
<xs:element name="myElement-liku"></xs:element>
</xs:schema>
第二部:创建META-INF目录,并创建约束映射文件:
spring.schemas:
http\://www.liku.com/lulu/lulu.xsd=com/liku/config/liku.xsd
第三步:创建命名空间处理器LuNameSpaceHandler类,继承NamespaceHandlerSupport,重写init方法:
public class LuNamspaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
//初始化标签解析器
this.registerBeanDefinitionParser("myElement-liku",new LuBeanDefinitionParser());
}
}
第四步:创建标签解析器LuBeanDefinitionParser实现BeanDefinitionParser,重写里面的方法:
public class LuBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
System.out.println("我是自定义");
BeanDefinition definition=new RootBeanDefinition();
definition.setBeanClassName("com.liku.entity.MyBeanFactoryPostProcessor");
parserContext.getRegistry().registerBeanDefinition("beanPostProcessor",definition);
return definition;
}
}
第五步:在META-INF下创建spring.handlers:
http\://www.liku.com/lulu=com.liku.handler.LuNamspaceHandler
在ApplicationContext.xml中添加自定义标签:
xmlns:lulu="http://www.liku.com/lulu"
http://www.liku.com/lulu
http://www.liku.com/lulu/lulu.xsd
引入标签:
<lulu:myElement-liku></lulu:myElement-liku>
xml引入之后上半部分结构如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:lulu="http://www.liku.com/lulu"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.liku.com/lulu
http://www.liku.com/lulu/lulu.xsd">
<lulu:myElement-liku></lulu:myElement-liku>
bingo!案例完成!😀