几个直击灵魂的Spring拷问(五)

这一篇主要来讲讲Spring的架构设计理念和优秀的设计模式吧。

一、体系框架


Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core Container 和 Test。

Spring 总共有十几个组件,Spring 框架中的核心组件只有三个:Core、Context 和 Beans。它们构建起了整个 Spring 的骨骼架构。Bean 包装的是 Object,而 Object 必然有数据,如何给这些数据提供生存环境就是 Context 要解决的问题,所以 Context 就是一个 Bean 关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可以为你工作了。而Core 就是发现、建立和维护每个 Bean 之间的关系所需要的一些列的工具。

Bean组件

Bean 组件在 Spring 的 org.springframework.beans 包下。这个包下的所有类主要解决了三件事:Bean 的定义、Bean 的创建以及对 Bean 的解析。对 Spring 的使用者来说唯一需要关心的就是 Bean 的创建,其他两个由 Spring 在内部完成,对开发透明。

Spring Bean 的创建时典型的工厂模式,它的顶级接口是 BeanFactory,下图是这个工厂的继承层次关系:

Bean 工厂的继承关系

Bean 的定义就是完整的描述了在 Spring 的配置文件或者Spring注解中定义的bean所有的信息,包括各种子节点。当 Spring 成功解析你定义的一个 节点后,在 Spring 的内部就被转化成 BeanDefinition 对象。

context组件

Context 在 Spring 的 org.springframework.context 包下,前面已经讲解了 Context 组件在 Spring 中的作用,他实际上就是给 Spring 提供一个运行时的环境,用以保存各个对象的状态。

  • 标识一个应用环境
  • 利用 BeanFactory 创建 Bean 对象
  • 保存对象关系表
  • 能够捕获各种事件

core组件

Core 组件作为 Spring 的核心组件,他其中包含了很多的关键类,其中一个重要组成部分就是定义了资源的访问方式。 Resource 接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。Context 和 Resource 是如何建立关系的?

Context 是把资源的加载、解析和描述工作委托给了 ResourcePatternResolver 类来完成,他相当于一个接头人,他把资源的加载、解析和资源的定义整合在一起便于其他组件使用。Core 组件中还有很多类似的方式。

参考引用:https://developer.ibm.com/zh/articles/j-lo-spring-principle/

Spring中优秀的设计模式


1、静态工厂模式

简单工厂模式,又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。 spring中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。

自己来实现一个简单工厂还是挺easy的,比如按照这个文章的<工厂模式--简单工厂模式>

1、抽象产品,负责描述所有实例共有的公共接口

public interface Computer {
    /**
     * 产品的抽象方法,由具体的产品类去实现
     */
    public abstract void start();
}

2、具体产品,是简单工厂模式的创建目标。

public class LenovoComputer implements Computer{
    @Override
    public void start() {
        System.out.println("联想电脑启动");
    }

3、简单工厂,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。

public class ComputerFactory {
    public static Computer createComputer(String type){
        Computer mComputer=null;
        switch (type) {
            case "lenovo":
                mComputer=new LenovoComputer();
               break;
            case "hp":
                mComputer=new HpComputer();
                break;
            case "asus":
                mComputer=new AsusComputer();
                break;

        }
        return mComputer;
    }
}

好处:使用户根据参数获得对应的类实例,避免了直接实例化类,降低了耦合性。
坏处:简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。

Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 Bean 对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。在容器启动过程中,Spring读取 bean 的 xml 配置文件或者注解了Bean的组件,将 bean 元素分别转换成一个 BeanDefinition 对象。然后通过 BeanDefinitionRegistry 将这些 bean 注册到 beanFactory 中,保存在它的一个 ConcurrentHashMap 中。取的时候通过这个方法来取:

Object getBean(String name)
<T> T getBean(Class<T> requiredType) 
<T> T getBean(String name, Class<T> requiredType)

2、工厂方法模式

工厂方法模式是对简单工厂模式进一步的解耦,一个产品要抽象成一个专门的工厂来生产,原来的简单工厂要进一步划分为抽象工厂和具体工厂。新增一种产品类型的时候,无需修改原来的“简单工厂”,只需要按照接口约定,实现抽象工厂,扩展出这个产品对应的具体工厂来即可!

1.抽象工厂,提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。

public interface ComputerFactory {
    public Computer createComputer() ;
}

2.具体工厂,主要是实现抽象工厂中的抽象方法,完成具体产品的创建。

// Lenove类工厂
public class LenovoComputerFactory implements ComputerFactory {
    @Override
    public Computer createComputer() {
        return new LenovoComputer();
    }
}

3、产品还是维持原先的抽象产品和具体产品不变,那么需要产品的时候,只需要先拿一个工厂,然后再创建产品即可使用。

public class Client {

    public static void main(String[] args) {
        ComputerFactory cf = new LenovoComputerFactory();
        Computer computer = cf.createComputer();
        computer.start();
    }
}

在Spring中用到的静态工厂模式的是FactoryBean接口,具体包含的接口如下所以:

T getObject() throws Exception;    //返回此工厂管理的对象的实例
Class<?> getObjectType();    //返回此FactoryBean创建的对象的类型
//这个工厂返回的对象是否是单例,默认返回true;若是单例则由getObject返回的//是相同的对象,是可以缓存的引用
default boolean isSingleton() {     
   return true;
}

所以对照着来看的,Factorybean本身就是一个抽象工厂,每个开发自己实现Factorybean接口的实现类就是一个具体工厂,这个工厂通过 getObject() 方法可以拿到一个特定的对象。
在Spring工程中使用Factorybean也很简单,案例如下:

//FactoryBean接口的实现类
@Component
public class FactoryBeanLearn implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        //这个Bean是我们自己new的,这里我们就可以控制Bean的创建过程了
        return new FactoryBeanServiceImpl();
    }

    @Override
    public Class<?> getObjectType() {
        return FactoryBeanService.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
//接口
public interface FactoryBeanService {

    /**
     * 测试FactoryBean
     */
    void testFactoryBean();
}
//实现类
public class FactoryBeanServiceImpl implements FactoryBeanService {
    /**
     * 测试FactoryBean
     */
    @Override
    public void testFactoryBean() {
        System.out.println("我是FactoryBean的一个测试类。。。。");
    }
}
//单测
@Test
public void test() {
        ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("classpath:com/applicationContext.xml");
        FactoryBeanService beanService = cac.getBean(FactoryBeanService.class);
        beanService.testFactoryBean();
    }

实现原理:实现了 FactoryBean 接口的 bean 是一类叫做 factory 的 bean。其特点是,spring 会在使用 getBean()调用获得该 bean 时,会自动调用该 bean 的 getObject()方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()方法的返回值。

3、观察者模式

也叫发布-订阅模式,Spring 的事件驱动模型使用的是 观察者模式 ,Spring 中 Observer 模式常用的地方是 listener 的实现。具体的使用方式在基于JDK与Spring中的观察者模式
已经提供了,其原理简单概括下:

事件机制的实现需要三个部分:事件源,事件,事件监听器

1、事件,所有的事件都需要继承 ApplicationEvent,ApplicationEvent 抽象类[事件] 继承自 JDK 的 EventObject,并且通过构造器参数 source 得到事件源,ApplicationContextEvent 表示 ApplicaitonContext 的容器事件。

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;
    }
}

2、事件监听器,所有的监听器都要实现 ApplicationListener 接口,这个接口只有一个 onApplicationEvent()方法,该方法接受一个 ApplicationEvent 或其子类对象作为参数,在方法体中,可以通过不同对 Event 类的判断来进行相应的处理。当事件触发时所有的监听器都会收到消息。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}

3、事件源,ApplicationContext 是 spring 中的全局”应用上下文”,它负责实现了 ApplicationEventPublisher 接口。Applicationcontext 通过publishEvent() 方法发布 Event 广播给所有的监听器。

public interface ApplicationEventPublisher {
        void publishEvent(ApplicationEvent event);
}

public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
         logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }
    getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
       this.parent.publishEvent(event);
    }
}

Spring中的设计模式很多,前面已经讲过了动态代理部分,还有一个单例模式,十分重要单独抽取成章节继续讲解。

1、微信文章.Spring中涉及的设计模式总结
2、软件设计模式概述

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352