代理模式和动态代理模式

  • 代表:被选中或当选为他人投票或代理的人– Merriam-Webster 。
  • 委托模式:在软件工程中,委托模式是面向对象编程中的一种设计模式,其中,一个对象而不是执行其陈述的任务之一,而是将该任务委托给一个关联的辅助对象Wikipedia 。
  • 使事情尽可能简单,但不要简单- 爱因斯坦 Albert Einstein)释义

Spring Batch是Enterprise Java工具箱中的重要工具。 它提供了开箱即用的强大功能,尤其是从不同来源读取和写入数据时。 我们在此博客中提供了几篇介绍Spring Batch的文章。 如果您不熟悉Spring Batch和Reader,Processor,Writer Tasklet,请花点时间回顾一下。

我上面使用的措辞对我来说很重要。 我尝试做的一件事就是保持我提供的代码尽可能可维护。 我希望它能正常工作,但是我今天签入的代码将在以后某个日期由某些人维护。 保持代码尽可能简单是确保代码易于维护的一种方法。

那么,当您必须处理复杂的数据源时会发生什么呢?

我们发现,经常需要处理的输入文件并不像每行一个记录那么简单。 通常,文件中有多行描述一个记录。

例如:

HKaren Traviss
LAB00KW3VG2G
LI0345478274
LI0345511131
F00000003
HJim Butcher
LI0451457811
F00000001
HDave Duncan
LI0380791277
LI0345352912
F00000002
HRik Scarborough
LI9999999999
F00000001

在这里,我们有一个文件,其中包含十五行中的四个记录。 每条记录均以页眉行开头,包含一个或多个正文行,并以页脚结尾。 标头包含线型(标头为H)和名称。 该行还包含线型(L),查找类型(在此示例中为ISBN或Amazon代码)以及查找书本的键。 页脚再次包含线型和此块中的记录数。

使用标准的读取器,将读取每一行,然后传递给处理器,然后处理器必须确定处理的是哪种类型的行。 然后,处理器在处理每个正文行时必须保留每个标头中的信息,直到处理了页脚。 然后,编写者将必须知道处理器发送的每一行,以及是否应将其写入。 这在某种程度上很复杂,因为多个对象必须知道如何读取文件,而不是处理器只关心单个对象,而编写器只关心编写给定的对象。

相反,让我们将Delegate模式引入Reader并让其处理创建整个记录的过程。 由于我们具有来自多行的信息以及用于创建每条记录的页眉和页脚,因此我们将必须向处理者传递记录列表。 你们当中的观察者会注意到,每个记录都包含一个ISBN或Amazon图书符号,并且可以用来查找作者(也包含在标题中)。 在现实生活中,这种冗余可能也不会发生。

让我们将输出包装在另一个对象中,以使其更易于使用。

public class OrderReaderStep implements ItemReader<OrderList> {private static final Logger logger = LoggerFactory.getLogger(OrderReaderStep.class);private FlatFileItemReader
<FieldSet> delegate;private static final String FOOTER = "F*";private static final String BODY = "L*";private static final String HEADER = "H*";@BeforeSteppublic void beforeStep(StepExecution stepExecution) {delegate = new FlatFileItemReader<>();delegate.setResource(new ClassPathResource("orders.txt"));final DefaultLineMapper
<FieldSet> defaultLineMapper = new DefaultLineMapper<>();final PatternMatchingCompositeLineTokenizer orderFileTokenizer = new PatternMatchingCompositeLineTokenizer();final Map<String, LineTokenizer> tokenizers = new HashMap<>();tokenizers.put(HEADER, buildHeaderTokenizer());tokenizers.put(BODY, buildBodyTokenizer());tokenizers.put(FOOTER, buildFooterTokenizer());orderFileTokenizer.setTokenizers(tokenizers);defaultLineMapper.setLineTokenizer(orderFileTokenizer);defaultLineMapper.setFieldSetMapper(new PassThroughFieldSetMapper());delegate.setLineMapper(defaultLineMapper);delegate.open(stepExecution.getExecutionContext());}@AfterSteppublic void afterStep(StepExecution stepExecution) {delegate.close();}@Overridepublic OrderList read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {logger.info("start read");OrderList record = null;FieldSet line;List<Order> bodyList = new ArrayList<>();while ((line = delegate.read()) != null) {String prefix = line.readString("lineType");if (prefix.equals("H")) {record = new OrderList();record.setName(line.readString("name"));} else if (prefix.equals("L")) {Order order = new Order();order.setLookup(line.readString("lookupKey"));order.setLookupType(line.readString("keyType"));bodyList.add(order);} else if (prefix.equals("F")) {if (record != null) {if (line.readLong("count") != bodyList.size()) {throw new ValidationException("Size does not match file count");}record.setOrders(bodyList);}break;}}logger.info("end read");return record;}private LineTokenizer buildBodyTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 2), // keyTypenew Range(3, 12) // lookup key});tokenizer.setNames(new String[]{ //"lineType","keyType","lookupKey"}); //tokenizer.setStrict(false);return tokenizer;}private LineTokenizer buildFooterTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 9) // count});tokenizer.setNames(new String[]{ //"lineType","count"}); //tokenizer.setStrict(false);return tokenizer;}private LineTokenizer buildHeaderTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 20), // name});tokenizer.setNames(new String[]{ //"lineType","name"}); //tokenizer.setStrict(false);return tokenizer;}}

此Reader实现ItemReader接口。 这为我们提供了一个由作业调用的read方法,直到它返回null或发生错误时引发异常。 在我们的Reader中,我们声明另一个Reader,这是一个FlatFileItemReader。 这是我们的代表,即为我们执行功能所选择的对象。 我们的read方法将以委托的读取为循环,直到读取Footer。 然后它将整个记录捆绑到其包装器中,并将其传递给处理器。

必须先打开委托阅读器,然后才完成使用。 我必须在此处将它初始化并在此处进行设置,因此在BeforeStep中在此处打开它。 我也可以将包含的阅读器实现为ItemStreamReader,并使用Interface给我们的open,close以及update方法。

将简化的对象返回给Processor可以使我们大大简化Processor:

@Override
public List<BookList> process(OrderList orderList) throws Exception {logger.info("process");List<BookList> books = new ArrayList<>();for (Order order : orderList.getOrders()) {BookList bl = doProcessing(orderList.getName(), order);books.add(bl);}return books;
}

doProcessing方法可以包含此Job的业务逻辑,并且需要创建一个有效的BookList对象。 由于我们正在处理多个记录,因此该过程将创建多个可返回的BookList,并将其传递给Writer。 我将留给您填写该对象的其余部分,但这只是一个标准的ItemProcessor。 处理器不必在调用之间保留记录信息,因此程序员可以专注于业务逻辑。

我们的编写器实现ItemStreamWriter。 这给我们提供了比ItemWriter更多的方法,但是,如果您希望像使用Reader一样使用ItemWriter,请确保在BeforeStep中打开Delegate,在AfterStep中将其关闭。

在Writer中使用委托使我们能够遍历Writer从Reader和Process收到的List。

public class ListWriter implements ItemStreamWriter<List<BookList>> {private static final Logger logger = LoggerFactory.getLogger(ListWriter.class);private FlatFileItemWriter<BookList> delegate;@BeforeSteppublic void beforeStep(StepExecution stepExecution) {delegate = new FlatFileItemWriter<>();delegate.setResource(new FileSystemResource("booklist.csv"));delegate.setShouldDeleteIfEmpty(true);delegate.setAppendAllowed(true);DelimitedLineAggregator<BookList> dla = new DelimitedLineAggregator<>();dla.setDelimiter(",");BeanWrapperFieldExtractor<BookList> fieldExtractor = new BeanWrapperFieldExtractor<>();fieldExtractor.setNames(new String[]{"bookName", "author"});dla.setFieldExtractor(fieldExtractor);delegate.setLineAggregator(dla);}@Overridepublic void close() throws ItemStreamException {delegate.close();}@Overridepublic void open(ExecutionContext ec) throws ItemStreamException {delegate.open(ec);}@Overridepublic void update(ExecutionContext ec) throws ItemStreamException {delegate.update(ec);}@Overridepublic void write(List<? extends List<BookList>> list) throws Exception {logger.info("write");for (List<BookList> bookList : list) {delegate.write(bookList);}}}

这为我们提供了以下输出:

Going Grey,Karen Traviss
Hard Contact,Karen Traviss
501st,Karen Traviss
Storm Front,Jim Butcher
Lord of the Fire Lands,Dave Duncan
The Reluctant Swordsman,Dave Duncan
Wolfbrander Series Unpublished,Rik Scarborough

那么,如果稍微复杂一点并且输入文件不包含页脚,会发生什么呢?

逻辑记录仍然从标题行开始,但在下一个标题之前的行结束。 在我们之前的示例中,系统必须先读取下一行,然后才能知道该行已完成,然后具有一些复杂的逻辑来保留该信息以用于下一轮。

HKaren Traviss
LAB00KW3VG2G
LI0345478274
LI0345511131
HJim Butcher
LI0451457811
HDave Duncan
LI0380791277
LI0345352912
HRik Scarborough
LI9999999999

让我们当前的作者提前阅读并在下一次通话时保留该记录是不必要的复杂操作,这会导致维护麻烦。 但是,我们可以使用PeekableItemReader简化此过程:

class OrderReaderStep2 implements ItemStreamReader<OrderList> {private static final String BODY = "L*";private static final String HEADER = "H*";private static final Logger logger = LoggerFactory.getLogger(OrderReaderStep2.class);private SingleItemPeekableItemReader
<FieldSet> delegate;@BeforeSteppublic void beforeStep(StepExecution stepExecution) {FlatFileItemReader fileReader = new FlatFileItemReader<>();fileReader.setResource(new ClassPathResource("orders2.txt"));final DefaultLineMapper
<FieldSet> defaultLineMapper = new DefaultLineMapper<>();final PatternMatchingCompositeLineTokenizer orderFileTokenizer = new PatternMatchingCompositeLineTokenizer();final Map<String, LineTokenizer> tokenizers = new HashMap<>();tokenizers.put(HEADER, buildHeaderTokenizer());tokenizers.put(BODY, buildBodyTokenizer());orderFileTokenizer.setTokenizers(tokenizers);defaultLineMapper.setLineTokenizer(orderFileTokenizer);defaultLineMapper.setFieldSetMapper(new PassThroughFieldSetMapper());fileReader.setLineMapper(defaultLineMapper);delegate = new SingleItemPeekableItemReader<>();delegate.setDelegate(fileReader);}@Overridepublic void close() throws ItemStreamException {delegate.close();}@Overridepublic void open(ExecutionContext ec) throws ItemStreamException {delegate.open(ec);}@Overridepublic OrderList read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {logger.info("start read");OrderList record = null;FieldSet line;List<Order> bodyList = new ArrayList<>();while ((line = delegate.read()) != null) {String prefix = line.readString("lineType");if (prefix.equals("H")) {record = new OrderList();record.setName(line.readString("name"));} else if (prefix.equals("L")) {Order order = new Order();order.setLookup(line.readString("lookupKey"));order.setLookupType(line.readString("keyType"));bodyList.add(order);}FieldSet nextLine = delegate.peek();if (nextLine == null || nextLine.readString("lineType").equals("H")) {record.setOrders(bodyList);break;}}logger.info("end read");return record;}@Overridepublic void update(ExecutionContext ec) throws ItemStreamException {delegate.update(ec);}private LineTokenizer buildBodyTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 2), // keyTypenew Range(3, 12) // lookup key});tokenizer.setNames(new String[]{ //"lineType","keyType","lookupKey"}); //tokenizer.setStrict(false);return tokenizer;}private LineTokenizer buildHeaderTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 20), // name});tokenizer.setNames(new String[]{ //"lineType","name"}); //tokenizer.setStrict(false);return tokenizer;}}

这次,我确实将包含的Reader实现为ItemStreamReader,以向您展示它们之间的区别。 可以像上一个一样将其实现为ItemReader。

PeekableItemReader允许我们向前查看下一条记录,以查看是否到达记录的末尾或文件的末尾。 然后可以使用相同的Processor和Writer来产生与以前相同的输出。

最后的想法

乍一看,委托模式似乎不像使用单个读取器或写入器那么简单。 这两个对象都有更多的配置。 但是我最喜欢的释义是说要尽可能简单,而且再简单不过。 稍微复杂一点的Reader和Writer将使您的Processor更加简单,并有助于进行后续维护。

代码很好,我的朋友。

翻译自: https://www.javacodegeeks.com/2016/03/introducing-delegate-pattern.html

代理模式和动态代理模式

代理模式和动态代理模式_代理模式介绍相关推荐

  1. 动态代理:JDK动态代理和CGLIB代理的区别

    代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...

  2. 静态代理,JDK动态代理和CGLIB代理入门学习

    之前面试时面试官问我:"你知道spring中有哪几种代理吗?" 啊?代理?啥子代理?VPN代理吗?嘿嘿,面试官你要种子直说啊......被刷下来了.好吧,入门学习下代理. 为什么需 ...

  3. python中代理模式分为几种类型_代理模式

    JDK 自带的动态代理 java.lang.reflect.Proxy:生成动态代理类和对象: java.lang.reflect.InvocationHandler(处理器接口):可以通过invok ...

  4. 用jsp的mvc模式的新闻发布系统_海鸥模式:创新文旅融合 促进消费升级

    人民网济南11月24日电(谈媛)11月18日,"海鸥模式战略合作发布会"在山东新闻大厦新闻大会堂成功举办.活动现场,百家企业签约"海鸥模式"战略合作单位,共同发 ...

  5. 23种设计模式之代理模式(动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式的组成 抽 ...

  6. 第六周 Java语法总结_设计原则_工厂模式_单例模式_代理模式(静态代理_动态代理)_递归_IO流_网络编程(UDP_TCP)_反射_数据库

    文章目录 20.设计原则 1.工厂模式 2.单例模式 1)饿汉式 2)懒汉式 3.Runtime类 4.代理模式 1)静态代理 2)动态代理 动态代理模板 21.递归 22.IO流 1.File 2. ...

  7. cglib动态代理jar包_代理模式详解:静态代理+JDK/CGLIB 动态代理实战

    1. 代理模式 代理模式是一种比较好的理解的设计模式.简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标 ...

  8. 利用代码分别实现jdk动态代理和cglib动态代理_代理模式实现方式及优缺点对比...

    作者:爱宝贝丶来源:https://my.oschina.net/zhangxufeng/blog/1633187 代理模式最典型的应用就是AOP,本文结合主要讲解了代理模式的几种实现方式:静态代理和 ...

  9. 设计模式之代理模式(上) 静态代理与JDK动态代理

    2019独角兽企业重金招聘Python工程师标准>>> 代理模式 给某一个对象提供一个代理,并由代理对象控制对原对象的引用. 静态代理 静态代理是由我们编写好的类,在程序运行之前就已 ...

最新文章

  1. 从强制卸载Office到强制安装WPS
  2. 编译-C++支持iOS静态库的脚本学习
  3. 马斯克现场直播介绍他的脑机接口公司Neuralink最新进展
  4. MacOS系统升级后,IDEA的SVN不好用的问题
  5. ios开发闹钟源代码_开源源码让短视频的开发变得更加便捷
  6. vim编程 插入 保存不退出 保存退出 退出不保存 另存为其他文件名 保存覆盖现有文件...
  7. 关键词热度分析工具_阿里国际站外贸独立站关键词的收集
  8. 今天很高兴,据说微软的长春的什么要设立在我们公司
  9. CenOS6 nginx+pxe+tftpd+samba/nfs+dhcpd 无盘安装windows  linux
  10. Android Studio API 文档_下载与使用
  11. AC日记——单词替换 1.7 21
  12. 小米盒子刷成无线打印服务器,小米盒子刷windows系统教程
  13. java毕业生设计医疗机构药房管理系统软件开发计算机源码+系统+mysql+调试部署+lw
  14. Android11 亮度自动调节
  15. 如何用PS把照片变成红/白/蓝底
  16. 【PHP+微信开发】实现微信对账单处理
  17. 风口起落的背后,是6271家创业公司的消亡
  18. txt文本去重复 亲测50G文本高效去重复
  19. tomcat启动bat文件闪退解决方法
  20. [数据挖掘笔记] 聚类算法KMeans

热门文章

  1. AT2390-[AGC016F]Games on DAG【状压dp,SG函数】
  2. P3975-[TJOI2015]弦论【SAM】
  3. jzoj6312-Lottery【dp,前缀和】
  4. P3629-[APIO2010]巡逻【树的直径】
  5. 在洛谷开了一个邀请赛
  6. Codeforces Round #674 (Div. 3)
  7. 牛客练习赛 65 (待补E-网络流)
  8. [集训队作业2018] 复读机(生成函数,单位根反演)
  9. 2017西安交大ACM小学期 敏感词汇[AC自动机]
  10. SpringCloud Greenwich(二)注册中心之consul、Zuul和 gateway网关配置