`

unitils中spring module初始化源码解读

    博客分类:
  • Test
阅读更多
spring module的初始化还算简单, 但是熟悉里面的原理有利于搭建一个项目的测试脚手架.

spring module 通过ApplicationContextManager来管理所有测试的ctx

而获取指定测试类的ctx, 使用了下面的方法:
org.unitils.core.util.AnnotatedInstanceManager.getInstanceImpl(Object testObject, Class<?> testClass)


ApplicationContextManager会用一个map来存储所有test类的ctx
protected Map<Class<?>, T> instances = new HashMap<Class<?>, T>();


分别在class, field, method上面找SpringApplicationContext注解(这个找的实现逻辑是:
在class上只找本类的, 在field会在继承结构上继续往上找所有的注解, 而method上只找本类), 但是三者只允许存在一个注解:
org.unitils.core.util.AnnotatedInstanceManager.getAnnotationValues(Class<?> testClass)


从所有注解上拿到spring bean配置文件列表
org.unitils.spring.util.ApplicationContextManager.getAnnotationValues(SpringApplicationContext annotation)


然后在方法上找是否加了SpringApplicationContext注解(只在本类方法上找)
org.unitils.core.util.AnnotatedInstanceManager.getCustomCreateMethod(Class<?> testClass, boolean searchSuperClasses)


在所有的使用了SpringApplicationContext注解的方法上过滤掉没有返回值(void)的方法

所有的最终使用SpringApplicationContext注解方法只能有一个, 否则抛异常

如果本类没有找到的话, 再到父类上去找(如果可以的话).

这些方法必须满足条件
引用
(T createMethodName() or T createMethodName(List<String> values))
这里的T就是ctx类型

如果本类中使用了在方法(这个方法称之为customCreateMethod)上使用SpringApplicationContext注解, 那么通过反射拿到ctx, 因为前面在类(方法, field)上拿到了一个配置文件列表, 这里会传这个列表给方法作为参数(如果需要的话)

如果在本类的class(field, method)上指定了spring配置文件, 但是本类没有在方法上使用SpringApplicationContext注解, 则到父类上面去找方法, 而且这个过程会一直追踪到最上面的基类, 直到找到这样的方法. 因此这里需要特别注意, 在整个继承结构中只允许一个在方法上使用SpringApplicationContext注解, 因为一旦找到了这个方法, 将不再继续往上查找. 找到方法, 完成ctx的创建过程.

如果在整个测试类的继承结构中都没有找到所需要的方法, 则将使用ApplicationContextFactory创建一个ctx. spring module 默认是ClassPathXmlApplicationContextFactory, new的ClassPathXmlApplicationContext. 如果你需要定制的话, 可以通过unitils.properties替换掉unitils的实现.

如果本类没有使用SpringApplicationContext注解, 那么在测试父类上继续找, 直到找到为止.

完成之后调用下面的方法来做一些后续的操作.
unitils.core.util.AnnotatedInstanceManager.afterInstanceCreate(T instance, Object testObject, Class<?> testClass)


最后是以testclass为key, ctx为value进行缓存以便下次重用.另外还有一个需要注意的是如果出现继承层次太多, 而在不同的测试层次上加上SpringApplicationContext注解, 可能会导致找不到基类的配置的问题(这个主要是因为对配置文件的查找, 在class, field上只能查找本类, 在method可以在父类上找, 但是找到之后就不在继续往上找, 加上三者只能使用一个注解).

通过对spring module初始化源码解读, 可以了解到使用配置文件的一个最佳实践(在unitils上也有说明), 在基类上这样写:
    private static ApplicationContext parent;

    @SpringApplicationContext
    public ApplicationContext createApplicationContext(List<String> locations) {
        ApplicationContext parent = createParentApplicationContext(locations);

        ctx = new FileSystemXmlApplicationContext(locations.toArray(new String[locations.size()]), parent);

        return ctx;
    }

    private static ApplicationContext createParentApplicationContext(List<String> locations) {
        if (parent == null) {
            synchronized (parent) {
                String[] parentLocations = new String[] {
                        "classpath:config.xml",
                        "classpath:jdbc.xml",
                        "classpath:mock.xml"
                };

                parent = new FileSystemXmlApplicationContext(locations.toArray(parentLocations)) ;
            }
        }
        return parent;
    }

然后可以在子类的class上打上SpringApplicationContext注解, 给出当前测试类所需要的配置文件.

这样配置的一个好处是, 如果一起同时跑多个测试类的话, 每个测试的spring配置都会单独的在manager的map中缓存一份儿, 对我们目前成百个测试类的应用有可能会导致内存溢出, 这的确是个严重的问题.解决办法就是所有的测试都配置在基类, 从而共享同一份spring bean xml files, 但是这样对跑单个测试来说, 又会出现初始化耗时太长的问题(根据我们的测试发现初始化spring配置文件几乎占了整个测试时间的90%多), 因此需要作出权衡.
0
0
分享到:
评论

相关推荐

    27. Spring Boot Junit单元测试【从零开始学Spring Boot】

    此外,`@Before`和`@After`可以用来定义在每个测试方法执行前后的初始化和清理操作。 为了提高测试的覆盖率,我们还可以利用Mockito等库来模拟对象的行为,避免实际依赖的影响。例如,我们可以为`HelloService`的...

    jeecms源码

    5. **配置(Configuration)**:Jeecms的配置文件(如`spring-context.xml`和`servlet-context.xml`)定义了Spring容器如何初始化和装配组件,以及SpringMVC的配置,如拦截器、视图解析器等。 6. **模块(Module)*...

    springside-3.3.4 源码(无JAR)

    1. **Core 模块**:这是 SpringSide 的核心模块,包含了一系列基础配置,如 Spring Boot、Spring MVC、Spring Data、AOP 等的初始化和配置。通过这些配置,SpringSide 提供了一个统一的入口点,使得开发者可以快速...

    Spring Boot集成dubbo,mybatis构建maven工程

    首先,Spring Boot简化了传统的Spring应用初始化和配置过程,通过自动配置和起步依赖使得创建独立的、生产级别的基于Spring的应用变得容易。在本案例中,Spring Boot将作为整个应用的核心,负责管理和启动所有组件。...

    springside3-core-3.0.4 源码

    4. **IoC容器**:IoCModule展示了如何配置和使用Spring的IoC容器,包括bean的定义、依赖注入以及容器的初始化流程。通过源码,我们可以深入了解IoC容器的内部机制。 5. **工具类集**:springside3-core-3.0.4提供了...

    基于java开发的跨境电商平台ECO-源码

    3. **数据库脚本(Database Scripts)**:包括SQL文件,用于创建和初始化数据库表结构。 4. **配置文件(Configuration Files)**:如application.properties或application.yml,设置项目运行环境的参数。 5. **测试...

    基于springboot的医疗挂号管理系统源码数据库.zip

    SpringBoot是Spring框架的扩展,旨在简化Spring应用的初始搭建以及开发过程。它内置了Tomcat服务器,提供了自动配置功能,使得开发者可以快速构建可运行的应用程序。在医疗挂号管理系统中,SpringBoot扮演着服务启动...

    dubbo核心原理解析.pdf

    parse方法负责解析标签属性,并将解析后的配置保存到相应的BeanConfig实例中,从而实现对标签对应配置类的初始化。 6. Dubbo服务暴露过程解析: 服务暴露过程大致可以分为以下几个步骤: a) 容器启动,解析配置...

    基于Eclipse开发OSGI的简单实例

    4. **启动与测试**:在OSGi框架中启动Bundle,SpringDM会自动初始化配置的服务并管理其生命周期。通过OSGi控制台或自定义的管理接口,可以查看和操作这些服务。 ### 纯OSGi实现源码 除了SpringDM,Eclipse也支持纯...

    SSIFamework

    在学习和使用SSIFramework时,开发者需要了解其基本架构,掌握如何配置和初始化框架,以及如何创建和配置控制器和服务。此外,熟悉框架提供的API和工具,能有效地提高开发效率。通过深入源码,开发者还可以学习到更...

    mall商城前后端分离IDEA编写.zip

    Spring Boot 是一个简化了 Spring 应用初始搭建以及开发过程的框架,它集成了大量常用的第三方库配置,如 JDBC、MongoDB、JPA、RabbitMQ、Quartz 等等,大大提高了开发效率。在本文中,我们将探讨如何使用 IntelliJ ...

    JAVA-JDK多模块示例代码

    3. **模块路径与模块化应用**:在模块化系统中,有别于传统的类路径,引入了模块路径(Module Path)。开发者需要指定模块路径,以便JVM能够找到并加载模块。 4. **模块间的依赖管理**:在多模块项目中,理解如何...

    maven搭建模块工程

    1. **初始化父模块**:使用`mvn archetype:create-from-project`命令或者在IDEA等集成环境中,从现有项目创建父模块。在pom.xml中,设置`&lt;packaging&gt;`为`pom`,并添加子模块的`&lt;modules&gt;`标签,例如: ```xml .....

    基于ssm+mysql的高校在线请假与审批系统源码数据库论文.docx

    SpringBoot则简化了Spring应用的初始搭建以及开发过程,通过自动配置、起步依赖等特性,使得开发者能快速构建微服务应用。 在这个基于SSM+MySQL的高校在线请假与审批系统中,学生可以在线提交请假申请,包括请假...

    taco-cloud:JDK 11中的Spring Boot刷新器项目

    3. **变量推断(var关键字)**:在局部范围内,可以使用`var`关键字声明并初始化变量,简化代码,提高可读性。 4. **文本块(Text Blocks)**:用于表示多行字符串,避免了繁琐的转义字符和字符串连接操作。 5. **...

    初试Guice(转)

    5. **提供者(Provider)**:对于有特殊初始化需求或需要延迟创建的对象,可以通过自定义`Provider`来控制其创建逻辑。 6. **超型绑定**:Guice允许绑定接口到其实现类,甚至可以绑定到接口的超类型,这使得更换...

    Java轻量级CMS-天梯 v1.0

    2、数据库使用mysql,初始化脚本位于tianti-module-admin中的src/main/webapp/scripts/tianti_stage.sql。3、后台的登陆路径为http端口/login,用户名为admin,初始密码为123456。4、后台中所用到的图标是从font...

    appfuse学习笔记(二)新建模块

    1. **初始化Maven项目**:AppFuse基于Maven构建,所以我们首先需要创建一个新的Maven项目。这可以通过运行`appfuse generate-module`命令完成,该命令会根据选定的技术栈(如Spring或Struts)生成一个基本的Maven...

    Sitemesh例子分享

    设置过滤器类`com.opensymphony.module.sitemesh.filter.PageFilter`,并指定其初始化参数,如装饰器目录和不参与装饰的URL模式。 3. **创建装饰模板**:创建一个HTML模板文件,例如`decorator.html`,其中定义了...

    jhipster angular2 lazyload 权限&国际化整体总结

    它结合了Spring Boot和Angular(在此处指Angular2)的优势,提供了强大的自动化工具,用于创建、构建和部署微服务架构的应用。本文将深入探讨JHipster在实现Angular2懒加载、权限控制以及国际化方面的最佳实践。 一...

Global site tag (gtag.js) - Google Analytics