Spring的@Primary注释在起作用

Spring是一个永不止息的框架。 这是因为它提供了许多不同的解决方案,使我们(开发人员)无需编写数百万行代码即可完成我们的任务。 取而代之的是,我们能够以更具可读性,更标准化的方式进行操作。 在这篇文章中,我将尝试描述最有可能为大家所熟知的功能之一,但我认为其重要性被低估了。 我将要讨论的功能是@Primary批注。

问题

在我从事的几个项目中,我们遇到了一个常见的业务问题–我们有一个进入更复杂逻辑的入口–一些容器,该容器会将其他几个处理器的结果收集到一个输出中(例如map-filter-reduce函数编程中的函数)。 在某种程度上,它类似于Composite模式。 综上所述,我们的方法如下:

  1. 我们有一个容器,其中包含自动实现共同接口的处理器列表
  2. 我们的容器实现了与自动装配列表元素相同的接口
  3. 我们希望使用该容器的客户端类使整个处理工作透明化-他只对结果感兴趣
  4. 处理器具有一些逻辑(谓词),处理器可将其应用于当前输入数据集
  5. 然后将处理结果合并到一个列表中,然后缩减为单个输出

有很多方法可以解决此问题-我将介绍一种使用Spring和@Primary批注的方法。

解决方案

让我们从定义用例如何适应上述前提开始。 我们的数据集是一个Person类,如下所示:

人.java

package com.blogspot.toomuchcoding.person.domain;public final class Person {private final String name;private final int age;private final boolean stupid;public Person(String name, int age, boolean stupid) {this.name = name;this.age = age;this.stupid = stupid;}public String getName() {return name;}public int getAge() {return age;}public boolean isStupid() {return stupid;}
}

没有什么不寻常的。 现在让我们定义合同:

PersonProcessingService.java

package com.blogspot.toomuchcoding.person.service;import com.blogspot.toomuchcoding.person.domain.Person;public interface PersonProcessingService {boolean isApplicableFor(Person person);String process(Person person);
}

如前提条件所述,PersonProcessingService的每个实现都必须定义合同的两点:

  1. 是否适用于当前人员
  2. 它如何处理一个人。

现在,让我们看一下我们拥有的一些处理器-由于它毫无意义,所以我不会在此处发布代码-您可以稍后在Github或Bitbucket上查看代码。 我们有以下@Component注释的PersonProcessingService实现:

  • AgePersonProcessingService
    • 如果某人的年龄大于或等于18,则适用
  • IntelligencePersonProcessingService
    • 适用于某人是愚蠢的人
  • NamePersonProcessingService
    • 如果某人有名字,则适用

逻辑很简单。 现在,我们的PersonProcessingServices容器将要针对处理器上的给定Person进行迭代,检查当前处理器是否适用(过滤器),如果是这种情况,则将响应处理Person的结果字符串添加到响应列表中(映射-将Person转换为String的函数),并最终以逗号将这些响应合并(减少)。 让我们检查一下它是如何完成的:

PersonProcessingServiceContainer.java

package com.blogspot.toomuchcoding.person.service;import java.util.ArrayList;
import java.util.List;import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;import com.blogspot.toomuchcoding.person.domain.Person;@Component
@Primary
class PersonProcessingServiceContainer implements PersonProcessingService {private static final Logger LOGGER = LoggerFactory.getLogger(PersonProcessingServiceContainer.class);@Autowiredprivate List<PersonProcessingService> personProcessingServices = new ArrayList<PersonProcessingService>();@Overridepublic boolean isApplicableFor(Person person) {return person != null;}@Overridepublic String process(Person person) {List<String> output = new ArrayList<String>();for(PersonProcessingService personProcessingService : personProcessingServices){if(personProcessingService.isApplicableFor(person)){output.add(personProcessingService.process(person));}}String result = StringUtils.join(output, ",");LOGGER.info(result);return result;}public List<PersonProcessingService> getPersonProcessingServices() {return personProcessingServices;}
}

如您所见,我们有一个用@Primary注释的容器,这意味着如果必须注入PersonProcessingService的实现,则Spring将选择要注入的PersonProcessingServiceContainer。 很棒的事情是,我们有一个自动连接的PersonProcessingServices列表,这意味着该接口的所有其他实现都将在那里自动连接(容器不会自动将其自身连接到该列表!)。

现在,让我们检查一下Spock测试 ,这些测试证明我没有在说谎。 如果您尚未在项目中使用Spock,则应立即将其移动。

PersonProcessingServiceContainerIntegrationSpec.groovy

package com.blogspot.toomuchcoding.person.service
import com.blogspot.toomuchcoding.configuration.SpringConfiguration
import com.blogspot.toomuchcoding.person.domain.Person
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
import spock.lang.Unrollimport static org.hamcrest.CoreMatchers.notNullValue@ContextConfiguration(classes = [SpringConfiguration])
class PersonProcessingServiceContainerIntegrationSpec extends Specification {@AutowiredPersonProcessingService personProcessingServicedef "should autowire container even though there are many implementations of service"(){       expect: personProcessingService instanceof PersonProcessingServiceContainer}def "the autowired container should not have itself in the list of autowired services"(){       expect: personProcessingService instanceof PersonProcessingServiceContainerand:!(personProcessingService as PersonProcessingServiceContainer).personProcessingServices.findResult {it instanceof PersonProcessingServiceContainer}}def "should not be applicable for processing if a person doesn't exist"(){given:Person person = nullexpect:!personProcessingService.isApplicableFor(person)}def "should return an empty result for a person not applicable for anything"(){given:Person person = new Person("", 17, false)when:def result = personProcessingService.process(person)then:result notNullValue()result.isEmpty()}@Unroll("For name [#name], age [#age] and being stupid [#stupid] the result should contain keywords #keywords")def "should perform different processing depending on input"(){given:Person person = new Person(name, age, stupid)when:def result = personProcessingService.process(person)        then:keywords.every {result.contains(it)    }where:name  | age | stupid || keywords"jan" | 20  | true   || ['NAME', 'AGE', 'STUPID']""    | 20  | true   || ['AGE', 'STUPID']""    | 20  | false  || ['AGE']null  | 17  | true   || ['STUPID']"jan" | 17  | true   || ['NAME']}
}

测试非常简单:

  1. 我们证明自动装配字段实际上是我们的容器– PersonProcessingServiceContainer。
  2. 然后,我们证明在PersonProcessingService的自动装配实现的集合中找不到对象,该对象属于PersonProcessingServiceContainer类型
  3. 在接下来的两个测试中,我们证明处理器背后的逻辑正在运行
  4. 最后但并非最不重要的一点是Spock最出色的– where子句,它使我们能够创建漂亮的参数化测试。

每个模块的功能

想象一下您在核心模块中定义了接口的实现的情况。

@Component
class CoreModuleClass implements SomeInterface {
...
}

如果您在与核心模块有依赖性的其他模块中决定不想使用此CoreModuleClass并希望在SomeInterface自动连线的任何地方都具有一些自定义逻辑该怎么办? 好吧–使用@Primary!

@Component
@Primary
class CountryModuleClass implements SomeInterface {
...
}

通过这种方式,您可以确保必须自动装配SomeInterface的位置将是您的CountryModuleClass,将其插入到该字段中。

结论

在这篇文章中,您可以看到如何

  • 使用@Primary批注创建类似接口实现的复合容器
  • 使用@Primary批注提供接口的每个模块实现,在自动装配方面,该实现将优先于其他@Components
  • 编写出色的Spock测试:)

编码

您可以在Too Much Coding的Github存储库或Too Much Coding的Bitbucket存储库中找到此处提供的代码。

参考:来自我们的JCG合作伙伴 Marcin Grzejszczak(位于Blog上)的 Spring @Primary注释 适用于编码成瘾者博客。

翻译自: https://www.javacodegeeks.com/2013/12/springs-primary-annotation-in-action.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366087.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JS对全角与半角的验证,相互转化以及介绍

1.什么是全角和半角? 全角&#xff1a;是一种电脑字符&#xff0c;是指一个全角字符占用两个标准字符(或两个半角字符)的位置。全角占两个字节。 汉字字符和规定了全角的英文字符及国标GB2312-80中的图形符号和特殊字符都是全角字符。在全角中&#xff0c;字母和数字等与汉字…

oracle 安装ora 27102,ORA-27102 解决办法

因为在32位操作系统上执行了如下的操作alter system set sga_max_size2G scopespfile;之后再重启数据库就会失败报出错误:ORA-27102Out of Memory解决的方法也很简单:新建一个文本文件 如initaaa.ora内容&#xff1a;SPFILE../product/10.2.0&#xff0f;db_1/dbs/spfileorcl.o…

Codechef August Challenge 2018 : Modular GCD

传送门 一开始还手动拓欧找规律&#xff0c;发现好像玩不了。 然后想了想&#xff0c;A-B这个数比较小&#xff0c;枚举它的因子判断合不合法就行了。 需要特判AB的情况。 #include<cstdio> #include<algorithm> #define ll long long #define ld long double usin…

bootstrap源码之滚动监听组件scrollspy.js详解

其实滚动监听使用的情况还是很多的&#xff0c;比如导航居于右侧&#xff0c;当主题内容滚动某一块的时候&#xff0c;右侧导航对应的要高亮。 实现功能 1、当滚动区域内设置的hashkey距离顶点到有效位置时&#xff0c;就关联设置其导航上的指定项 2、导航必须是 .nav > l…

c语言 指针_C语言——指针

学习阶段&#xff1a;高中信竞、大学编程。前置知识&#xff1a;二进制与十六进制&#xff0c;C语言基础&#xff0c;数组。指针初学可能比较难理解&#xff0c;我这篇文章尽量用通俗易懂的方式来讲解。1. 指针概述为什么有指针这个东西&#xff1f;因为指针很贴近计算机内部的…

将jOOQ与Spring结合使用:配置

我遇到了由ORM引起的性能问题。 尽管我不得不承认大多数这些问题确实是由您造成的&#xff0c;但是我开始认为在只读操作中使用ORM是不值得的。 我开始寻找实现这些操作的替代方法。 这就是我遇到jOOQ的方式 &#xff0c;它指出&#xff1a; jOOQ从您的数据库生成Java代码&a…

FFT实现高精度乘法

你应该知道$FFT$是用来处理多项式乘法的吧。 那么高精度乘法和多项式乘法有什么关系呢&#xff1f; 观察这样一个$20$位高精度整数$11111111111111111111$ 我们可以把它处理成这样的形式&#xff1a;$\sum_{i0}^{19}1\times10^i$ 这样就变成了一个多项式了&#xff01; 直接上代…

oracle精确匹配时间,Oracle时间精确到时、分、秒处理方法

Oracle时间精确到时、分、秒处理方法一般数据库中时间的格式为DATE类型&#xff0c;而我们从页面中获取的时间往往为String类型&#xff0c;这个就需要类型的转换。一般我们会通过调用 Java.text.SimpleDateFormat JAVA类来对其进行转换。这个JAVA类中我们经常用到的方法有两个…

BZOJ 2141 排队(分块+树状数组)

题意 第一行为一个正整数n&#xff0c;表示小朋友的数量&#xff1b;第二行包含n个由空格分隔的正整数h1,h2,…,hn&#xff0c;依次表示初始队列中小朋友的身高&#xff1b;第三行为一个正整数m&#xff0c;表示交换操作的次数&#xff1b;以下m行每行包含两个正整数ai和bi&…

骆驼路线的主/从故障转移

一种实现主/从故障转移模式的方法是拥有一个应用程序实例的集群&#xff0c;其中一个实例&#xff08;主实例&#xff09;当前处于活动状态&#xff0c;而其他实例&#xff08;从属实例&#xff09;处于待机状态&#xff0c;随时可以在主实例发生故障时接管。 一些项目提供了开…

vant input框禁止调用手机键盘_【案例分享】适应网银等密码键盘的解决方案

在各种技术不断进步的大趋势下&#xff0c;对业务造成了极大影响&#xff0c;从大数据到云计算&#xff0c;再到人工智能&#xff0c;众多企业都开始步入技术改革&#xff0c;从而实现企业的创新。但与此同时&#xff0c;更多技术的引用&#xff0c;意味着更多的业务系统上线&a…

JS无法获取display为none的隐藏元素的宽度和高度的解决方案

在实际开发中会遇到确实需要获取隐藏元素的宽高&#xff0c;这儿所说的隐藏元素是display为none的元素。 可使用jQuery Actual Plugin插件来完成&#xff0c;其源码如下&#xff1a; ;( function ( $ ){$.fn.addBack $.fn.addBack || $.fn.andSelf;$.fn.extend({actual : fun…

删除kafka topic

1、因为项目原因&#xff0c;kakfa通道中经常造成数据阻塞&#xff0c;导致kafka通道中数据量过大&#xff0c;因此我需要将kakfa通道中数据清除&#xff08;个人项目原因&#xff0c;一直使用一个消费者&#xff0c;只要保证当前消费者不在消费之前很久的数据就可以。因数量过…

启动oracle数据库工具,Oracle数据库常用工具

SQL*Plus:SQL*Plus 是Oracle 数据库的一个基本工具&#xff0c;它允许用户使用SQL 命令交互式的访问数据库&#xff0c;也允许用户使用SQL*Plus 命令格式化输出参数。 通过SQL*Plus &#xff0c;可以完成数据库的启动和停止、创建和运行查询、更新数据、格式化输出数据报表、运…

可持久化并查集小结

https://www.zybuluo.com/ysner/note/1253722 定义 允许恢复历史状态的并查集。 建立 建\(Q\)棵主席树&#xff0c;每个主席树上维护当前状态并查集各个节点的父亲。 &#xff08;实际上就是并查集和主席树强行捆绑在一起&#xff09; 操作 每次操作前自动继承上次操作后的状态…

Java 获取linux根目录下的文件夹_Windows支持直接访问Linux子系统文件:你的下一台Linux何必是Linux...

2020年第一波薅当当网羊毛的机会&#xff0c;别错过&#xff01;晓查 发自 凹非寺 量子位 报道 | 公众号 QbitAI 微软&#xff0c;致力于做最好的Linux发行版。今天&#xff0c;安装Windows 10测试版本号19603的用户发现&#xff0c;系统里WSL (Windows中的Linux子系统…

HTML5效果:实现树叶飘落

实现如图所示的东西效果&#xff08;落叶下落&#xff09;&#xff1a; html代码&#xff1a; <!DOCTYPE html><html><head><title>HTML5树叶飘落动画</title><meta charset"utf-8"><meta name"viewport" content&…

Java 8 Friday Goodies:Lambda和XML

在Data Geekery &#xff0c;我们喜欢Java。 而且&#xff0c;由于我们真的很喜欢jOOQ的流畅的API和查询DSL &#xff0c;我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 我们已经写了一些关于Java 8好东西的博客 &#xff0c;现在我们觉得是时候开始一个新的博客系列了…

洛谷2619/bzoj2654 Tree(凸优化+MST)

bzoj的数据是真的水。。 qwq 由于本人还有很多东西不是很理解 qwq 所以这里只写一个正确的做法。 首先&#xff0c;我们会发现&#xff0c;对于你选择白色边的数目&#xff0c;随着数目的上涨&#xff0c;斜率是单调升高的。 那么这时候我们就可以考虑凸优化&#xff0c;也就是…

oracle 创交表,创建交叉报表(oracle)_oracle

创建交叉报表create table t1(goodid number(10) not null,saledate date not null,salesum number(10));要求生成本年度每个月的产品销售状况表m1 m2 m3 ... m12g1g2...gn下面是生成报表的sqlSELECT goodid,SUM(decode(to_char(saledate,mm),01,salesum)) "01"…