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接口

接口名称回调方法作用
ServletContextAwaregetServletContext()注入servletContext对象
BeanFactoryAwaresetBeanFactory()注入beanFactory
BeanNameAwaresetBeanName()注入beanName
ApplicationContextAwaresetApplicationContext()注入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!案例完成!😀

posted @ 2023-02-23 23:22  Liku007  阅读(27)  评论(0编辑  收藏  举报