容器类

我们在实际编码中,常常会遇到各种容器类,他们有时叫做POJO,有时又叫做DTO,VO, DO等,这些类只具有容器的作用,具有完全的get,set方法,作为信息载体,作数据传输用。

其实,很多地方都可以看做将对象看做容器。比如,一些起到标签作用的接口,Serializable, **Aware接口等;注解元信息,比如@Order提供排序信息,@Value提供值注入,@Autowired提供属性注入等。

这种容器类一般很少具有面向对象的方法,又叫做贫血模型。与之对应的是充血模型,适合用于复杂系统,Spring源码显然就是充血模型占绝大多数,基于了面向对象的思想:对象=数据+行为。但是Spring中也经常见到许多容器类,有些类的一部分也可以看做容器。

函数式模型

函数式编程思想下,一个对象通常值为不变的。这样做的好处有很多,仅列举一些:便于支持并发,是线程安全的;对象是无状态的,极大地简化代码的理解,因为每一次值的变化都是显性的;便于编译器优化性能,比如延迟计算,实现对象复用;便于测试,因为没有副作用,每一次执行方法返回的结果都唯一。

这种对象称为值对象,Lombok中使用@Value标注,JDK14开始引入的record关键字实现了相同功能。比如Map容器对Key的要求就是值对象。以后有时间我再写一下函数式的一些基本思想。

举例1:MVC模型

MVC模型中,Model, View, Controller中我们使用的数据传输对象一般就使用POJO。一般情况下,一个请求过来,SpringMvc 可以将Json格式的字符串反序列化为POJO,一般为**Request。进行一系列计算或数据库交互后,返回可序列化的POJO对象,SpringMvc 将其序列化为Json。

举例2:各种配置的属性

我们经常使用的@Value、@ConfigurationProperties注解可以快速配置对象的属性,配合@RefreshScope可以实现动态刷新配置。

@Value注解的中的值还支持EL表达式,供我们灵活取用。

举例3:Spring 事件

观察者设计模式中的事件都是值对象。一个事件可以被多个listener监听,如果有一个listener改变了事件的某些属性,其他listener无法确定事件是否被更改过,系统运行的结果就不稳定。

我们简单地看下refresh模板方法,在容器刷下完毕之后,会调用finishRefresh方法,这个方法会发布事件ContextRefreshedEvent。我们看一下ContextRefreshedEvent的源码就会发现,其值只在构造器创建对象时确定,不包含set方法。

public class ContextRefreshedEvent extends ApplicationContextEvent {
public ContextRefreshedEvent(ApplicationContext source) {
super(source);
}
} public abstract class ApplicationContextEvent extends ApplicationEvent {
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
} public class EventObject implements java.io.Serializable {
private static final long serialVersionUID = 5516075349620653480L;
protected transient Object source;
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source"); this.source = source;
}
public Object getSource() {
return source;
}
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}

也可以用IDEA提供的Structure功能看,一目了然。

打断点到对应位置

模板方法refresh

举例4:Monad

前置知识:我们知道Java Stream支持以下操作:filter, map, flatMap, collect, reduce。以上这些操作就是我们经常使用的,入参通常为函数。

monad是一个很复杂的概念,也很难解释。我们可以从使用的角度来简化理解,其就是一个容器,这个容器包装了其他的对象,这个容器包装了对象的行为,可以使用map、flatMap等方法。

不严格地说,如果没有Null,Optional就是一个Monad,数据库查询的返回值应该是Optional。

java8 引入Stream流后,集合类型也可以看做Monad,比如members.stream().filter(validPredicate).map(Member::getSalary).mapToInt(x → x).sum()

monad中的 flatMap 可以避免套娃。

在Java中常见的套娃就是Try Catch,使用vavr库中的Try可以解决,Try.of(逻辑1).faltMap(逻辑2),代码运行后,Try包装的对象要么是正常业务执行的返回值,要么是遇到的Exception。

比如我想查询数据库,查询公司职员A的主管。

member = repo.getByName(a);

manager = member.map(Member::getManager).flatMap(repo::getByName);

总结

  1. 阅读源码的过程中,我们更应该关注的是类的行为,或者说是对于数据的处理。而对于数据类的存放位置,可以放在次要的位置。
  2. 数据的表示可以是POJO类、值类型、数组、基本数据类型以及他们之间的组合,我们在编码过程中更应该注意其结构的可读性和是否选取 Immutable(不变)来表示。
  3. 关注数据的传递,可以说数据的传递表达了系统的架构。有时候虽然我们并不知道数据的传递过程,但是我们更在意数据传递的结果。

关于第 3 点举一个例子,使用@Order 注解可以为一组类进行排序,再阅读源码之前,我们虽然不知道是哪个具体的类去处理的,但是其处理的过程必然包括了以下过程:获取注解值,对原有类与排序号进行关联,一个通用的排序方法排序。

SpringBoot自动配置了许多类,我们虽然不知道具体的配置是如何映射到我们在容器中使用的Bean,但是可以确定数据的传递必然经历了 配置 → 解析 → 注册Bean的过程。从实用的角度出发,多数情况下,我们不必知道一个Bean的创建的具体逻辑,我们只是拿来就用,按需配置,我们需要了解的是这个Bean可以做什么。

事实上,Spring项目中,除了少数的方法类(比如各种**Strategy),随便拿出一个类,基本都可以看做容器类,后续我会结合一些代码再进行详细分析,敬请关注。

从Spring中学到的【2】--容器类的更多相关文章

  1. 从Spring中学到的【1】--读懂继承链

    最近看了一些 Spring 源码,发现源码分析的文章很多,而底层思想分析的文章比较少,这个系列文章准备总结一下Spring中给我的启示,包括设计模式思想.SOLID设计原则等,涉及一些编程的基本原则, ...

  2. spring不依赖注入得到实体bean

    如题,我们一般用spring的ioc,通过配置注入接口得到这个实现类,现在通过研究公司平台框架发现还有一种方法得到spring文件配置的bean方法,举个例子(注:这个ApplicationConte ...

  3. Spring Boot 学习笔记一(SpringBoot启动过程)

    SpringBoot启动 Spring Boot通常有一个名为*Application的入口类,在入口类里有一个main方法,这个main方法其实就是一个标准的java应用的入口方法. 在main方法 ...

  4. spring源码分析系列3:BeanFactory核心容器的研究

    目录 @(spring源码分析系列3:核心容器的研究) 在讲容器之前,再明确一下知识点. BeanDefinition是Bean在容器的描述.BeanDefinition与Bean不是一个东西. Be ...

  5. Spring Boot中Tomcat是怎么启动的

    Spring Boot一个非常突出的优点就是不需要我们额外再部署Servlet容器,它内置了多种容器的支持.我们可以通过配置来指定我们需要的容器. 本文以我们平时最常使用的容器Tomcat为列来介绍以 ...

  6. spring IoC容器类接口关系梳理

    整理了下spring中容器类的接口,用UML画了张图(并不十分严格按照UML标准,省略了些方法).

  7. 普通线程类获取service,controller等spring容器类

    package com.zihexin.application.strategy; import org.springframework.beans.BeansException; import or ...

  8. test 测试spring容器类

  9. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

  10. Spring中bean的作用域scope详解

    参考文献:http://blog.csdn.net/jacklearntech/article/details/40157861 http://www.cnblogs.com/qq78292959/p ...

随机推荐

  1. 第 46 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(沈阳)

    有时候,很简单的模板题,可能有人没有做出来,(特指 I ),到时候一定要把所有的题目全部看一遍 目录 B 题解 E F 题解 H I 题解&代码 J B 输入样例 3 2 1 2 1 2 3 ...

  2. composer 报错 The "https://mirrors.aliyun.com/composer/p....json" file could not be downloaded (HTTP/1.1 404 Not Found)

    [Composer\Downloader\TransportException] The "https://mirrors.aliyun.com/composer/p/provider-20 ...

  3. Redis 常见问题-缓存穿透

    问题描述: * 针对 DB 中不存在的数据源,每次请求缓存和数据库都不存在 造成后果: * 应用服务器压力变大 * Redis 命中率大幅度降低 * `数据库压力巨增甚至 down 掉`* 该现象对于 ...

  4. wpf 手指触摸图片放大缩小 设置放大缩小值

    xaml代码: <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/w ...

  5. RAID5部署

    软RAID与备份盘 1.此处我们还用刚才的4块盘做演示,三块盘做raid,一块盘做备份盘,防止磁盘故障 我们以raid 5 来配置三块磁盘 加上一块备份盘 [root@local-pyyu tmp]# ...

  6. JMETER与BeanShell

    变量 Beanshell应用自定义变量有两种方法: #第一种方法,使用${key}格式,但是需要注意这是用应用的变量是没有定义数据类型的 #log.info(String Key)只能打印字符串,所以 ...

  7. HTTP(s) API 经验总结

    目录 参考文章 First URL HTTP(s)-Header 请求方式 参考文章 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Overvie ...

  8. webpack4--按需加载

    在做单页面应用的过程中,通常利用webpack打包文件,将依赖的外部问价单独打一个vendor.js.这样就会有个问题,这个文件会随着你引用的包的增多,体积会越来越大.在路由中利用import 引用文 ...

  9. for in 和 for of 的区别和v-for指令的三种使用方法

    for...in 循环:只能获得对象的键名,不能获得键值 for...of 循环:允许遍历获得键值 var arr = ['red', 'green', 'blue'] for(let item in ...

  10. 【题解】P7860 [COCI2015-2016#2] ARTUR

    题面传送门 好题. 主要思路和另一位巨佬差不多,详细讲一下判断的部分. 解决思路: 首先考虑本题与拓扑排序有和关系.可以想到,某些棍子的先后移动顺序是有限制的.比如: 这里红色的必须比蓝色的先移动,因 ...