当前位置: 首页 > 编程笔记 >

Spring xml方式整合第三方框架

呼延宪
2023-05-16

我们已经学习了Spring、SpringMVC和MyBatis框架,那么这三个框架如何结合在一块儿使用呢?首先,需要将者三个框架进行整合。那么,就给大家安排上。本节将给大家介绍SSM框架的整合流程,并带大家最终完成Web应用程序的开发。保证,只要你认真看完,肯定会有斩获哦。

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

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

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

Spring整合MyBatis的步骤如下:

⚫ 导入MyBatis整合Spring的相关坐标;(请见资料中的pom.xml)

⚫ 编写Mapper和Mapper.xml;

⚫ 配置SqlSessionFactoryBean和MapperScannerConfigurer;

⚫ 编写测试代码。

配置SqlSessionFactoryBean和MapperScannerConfigurer:

<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property>  <property name="username" value="root"></property>
  <property name="password" value="root"></property>
</bean>
<!--配置SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置Mapper包扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.itheima.dao"></property>
</bean>

编写Mapper和Mapper.xml

public interface UserMapper { 
    List<User> findAll(); 
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.UserMapper">
    <select id="findAll" resulttype="com.itheima.pojo.User">
        select * from tb_user
    </select>
</mapper>

编写测试代码

ClassPathxmlApplicationContext applicationContext =
            new ClassPathxmlApplicationContext("applicationContext.xml");
UserMapper userMapper = applicationContext.getBean(UserMapper.class);
List<User> all = userMapper.findAll();
System.out.println(all);

Spring整合MyBatis的原理剖析

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

• SqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;

• MapperScannerConfigurer:需要进行配置,用于扫描指定mapper注册BeanDefinition;

• MapperFactoryBean:Mapper的FactoryBean,获得指定Mapper时调用getObject方法;

• ClassPathMapperScanner:definition.setAutowireMode(2) 修改了自动注入状态,所以

• MapperFactoryBean中的setSqlSessionFactory会自动注入进去。

配置SqlSessionFactoryBean作用是向容器中提供SqlSessionFactory,SqlSessionFactoryBean实现了FactoryBean和InitializingBean两个接口,所以会自动执行getObject() 和afterPropertiesSet()方法。

SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean{
    public void afterPropertiesSet() throws Exception {
      //创建SqlSessionFactory对象
      this.sqlSessionFactory = this.buildSqlSessionFactory();
    }
    public SqlSessionFactory getObject() throws Exception {
      return this.sqlSessionFactory;
    }
}

配置MapperScannerConfigurer作用是扫描Mapper,向容器中注册Mapper对应的MapperFactoryBean,MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor和InitializingBean两个接口,会在postProcessBeanDefinitionRegistry方法中向容器中注册MapperFactoryBean。

class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean{
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
      scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
}
class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
      Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
        } else {
          this.processBeanDefinitions(beanDefinitions);
        }
      }
      private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        //设置Mapper的beanClass是org.mybatis.spring.mapper.MapperFactoryBean
        definition.setBeanClass(this.mapperFactoryBeanClass);
        definition.setAutowireMode(2); //设置MapperBeanFactory 进行自动注入
      }
}

autowireMode取值:1是根据名称自动装配,2是根据类型自动装配。

class ClassPathBeanDefinitionScanner{
    public int scan(String... basePackages) {
      this.doScan(basePackages);
    }
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
      //将扫描到的类注册到beanDefinitionMap中,此时beanClass是当前类全限定名
      this.registerBeanDefinition(definitionHolder, this.registry);
      return beanDefinitions;
    }
}
UserMapper userMapper = applicationContext.getBean(UserMapper.class);
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    public MapperFactoryBean(Class<T> mapperInterface) {
      this.mapperInterface = mapperInterface;
    }
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
      this.sqlSessionTemplate = this.createSqlSessionTemplate(sqlSessionFactory);
    }

    public T getObject() throws Exception {
      return this.getSqlSession().getMapper(this.mapperInterface);
    }
}

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:application name="dubbo1-consumer"/>
    <!--配置注册中心地址-->
    <dubbo:registry address="zookeeper://localhost:2181"/>
    <!--扫描dubbo的注解-->
    <dubbo:annotation package="com.itheima.controller"/>
    <!--消费者配置-->
    <dubbo:consumer check="false" timeout="1000" retries="0"/>
</beans>

为了降低我们此处的学习成本,不在引入Dubbo第三方框架了,以Spring的 context 命名空间去进行讲解,该方式也是命名空间扩展方式。

需求:加载外部properties文件,将键值对存储在Spring容器中。

jdbc.url=jdbc:mysql://localhost:3306/mybatis 
jdbc.username=root 
jdbc.password=root

引入context命名空间,在使用context命名空间的标签,使用SpEL表达式在xml或注解中根据key获得value。

<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="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
      <property name="url" value="${jdbc.url}"></property>
      <property name="username" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
    </bean>
    
<beans>

其实,加载的properties文件中的属性最终通过Spring解析后会被存储到了Spring容器的environment中去,不仅自己定义的属性会进行存储,Spring也会把环境相关的一些属性进行存储。

1681726493712_企业微信截图_16817264376863.png

原理剖析解析过程,只能从源头ClassPathXmlApplicationContext入手,经历复杂的源码追踪,找到如下两个点:

1)在创建DefaultNamespaceHandlerResolver时,为处理器映射地址handlerMappingsLocation属性赋值,并加载命名空间处理器到MaphandlerMappings 中去。

this.handlerMappingsLocation = "META-INF/spring.handlers";

1681726712753_11.png

第一点完成后,Map集合handlerMappings就被填充了很多XxxNamespaceHandler,继续往下追代码

2)在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中,发现如下逻辑:

//如果是默认命名空间
if (delegate.isDefaultNamespace(ele)) {
  this.parseDefaultElement(ele, delegate);
//否则是自定义命名空间
} else {
  delegate.parseCustomElement(ele);
}

如果是默认命名空间,则执行parseDefaultElement方法

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, "import")) {
      this.importBeanDefinitionResource(ele);
    } else if (delegate.nodeNameEquals(ele, "alias")) {
      this.processAliasRegistration(ele);
    } else if (delegate.nodeNameEquals(ele, "bean")) {
      this.processBeanDefinition(ele, delegate);
    } else if (delegate.nodeNameEquals(ele, "beans")) {
      this.doRegisterBeanDefinitions(ele);
    }
}

如果是自定义命名空间,则执行parseCustomElement方法

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   //解析命名空间
   String namespaceUri = this.getNamespaceURI(ele);
   //获得命名空间解析器
   NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   //解析执行的标签
   return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

在执行resovle方法时,就是从MaphandlerMappings中根据命名空间名称获得对应的处理器对象,此处是ContextNamespaceHandler,最终执行NamespaceHandler的parse方法。

ContextNamespaceHandler源码如下,间接实现了NamespaceHandler接口,初始化方法init会被自动调用。由于context命名空间下有多个标签,所以每个标签又单独注册了对应的解析器,注册到了其父类NamespaceHandlerSupport的Map<String, BeanDefinitionParser>

parsers中去了。

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
    public ContextNamespaceHandler() {
    }

    public void init() {
      this.registerBeanDefinitionParser("property-placeholder", new 
PropertyPlaceholderBeanDefinitionParser());
      this.registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
      this.registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
      this.registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
      this.registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
      this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
      this.registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
      this.registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }
}

通过上述分析,我们清楚的了解了外部命名空间标签的执行流程,如下:

将自定义标签的约束 与 物理约束文件与网络约束名称的约束 以键值对形式存储到一个spring.schemas文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到;

将自定义命名空间的名称 与 自定义命名空间的处理器映射关系 以键值对形式存在到一个叫spring.handlers文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到;

准备好NamespaceHandler,如果命名空间只有一个标签,那么直接在parse方法中进行解析即可,一般解析结果就是注册该标签对应的BeanDefinition。如果命名空间里有多个标签,那么可以在init方法中为每个标签都注册一个BeanDefinitionParser,在执行NamespaceHandler的parse方法时在分流给不同的BeanDefinitionParser进行解析(重写doParse方法即可)。


 类似资料:
  • 整合第三方系统: 该文档正在编写中...

  • 本文向大家介绍Android ImageLoader第三方框架解析,包括了Android ImageLoader第三方框架解析的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了Android ImageLoader框架的使用方法,供大家参考,具体内容如下 1.准备工作 1)导入universal-image-loader-1.9.5.jar到项目中 2)创建MyApplication继

  • 云联壹云平台支持对接第三方工单系统。 目前仅支持对接JIRA工单系统,且只有技术支持工单支持对接第三方工单系统。 第三方工单使用流程如下: 在系统配置-工单-第三方工单中对接第三方工单系统。 在工单配置中-配置技术支持工单,支持对接第三方工单系统。 用户提交的技术支持工单都需要第三方工单系统进行审批处理等。 入口:在云管平台单击左上角导航菜单,在弹出的左侧菜单栏中单击 “系统配置/工单/第三方”

  • 我添加了respone头(“cross-origin-opener-policy”,“same-origin”)来使用SharedArrayBuffer(以支持webassembly)。但是现在我在我的页面中添加第三方JS时遇到了麻烦,这将被跨源隔离阻止。有什么方法可以在webassembly中使用第三方JS吗?

  • 第三方库 ThinkCMF 内置了以下第三方库,如果你项目中需要增加其它第三库,也可以通过 composer自己安装,但以后升级应注意不要覆盖 vendor目录,而是使用 composer update去更新第三方库 "topthink/think-orm": "^2.0", "topthink/think-captcha": "^3.0",

  • 你们要爱惜光阴,用智慧与外人交往。你们的言语要常常带着和气,好像用盐调和,就可知道怎样回答各人。(COLOSSIANS 4:5-6) 第三方库 标准库的内容已经非常多了,前面仅仅列举几个,但是Python给编程者的支持不仅仅在于标准库,它还有不可胜数的第三方库。因此,如果作为一个Pythoner,即使你达到了master的水平,在做某个事情之前最好在网上搜一下是否有标准库或者第三方库替你完成。因为

  • 第三方包 从一开始,如果要做一些实际Python开发,你一定会用到一些第三方包。 在Linux系统上至少有3种安装第三方包的方法。 使用系统自带的包管理系统(deb,rpm,等) 通过社区开发的各种工具,例如pip,easy_install等 从源文件安装 这三个方面,几乎完成同样的事情。即:安装依赖,编译代码(如果需要的话),将一个包含模块的包复制的标准软件包搜索位置。 第二步和第三步在所有的操

  • 问题内容: 如何将第三部分框架导入Xcode Playground? 斯威夫特游乐场显然有一个框架,导入机制,因为我们可以导入,以及在OSX游乐场,(似乎来自iOS的缺失,奇怪) 我真正想做的是从操场上隐藏代码,并能够显示一个最小的示例。关于如何将框架加载到Swift游乐场的任何想法? (此问题有所不同,因为请求是在Playground中使用 框架 而不是简单的常规文件) 问题答案: 编辑:从Be