1

从Spring中学到的【2】--容器类 - 大华dijkstra

 1 year ago
source link: https://www.cnblogs.com/dahua-dijkstra/p/17013286.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我们在实际编码中,常常会遇到各种容器类,他们有时叫做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功能看,一目了然。

image

打断点到对应位置

image

模板方法refresh

image

举例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),随便拿出一个类,基本都可以看做容器类,后续我会结合一些代码再进行详细分析,敬请关注。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK