Spring 中9 种设计模式 spring中的设计模式有哪些

目录

  • 1.简单工厂(非23种设计模式中的一种)
  • 2.工厂方法
  • 3.单例模式
  • 4.适配器模式
  • 5.装饰器模式
  • 6.代理模式
  • 7.观察者模式
  • 8.策略模式
  • 9.模版方法模式

1.简单工厂(非23种设计模式中的一种)

实现方式:

BeanFactory。Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。

实质:

由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

实现原理:

bean容器的启动阶段:

  • 读取bean的xml配置文件,将bean元素分别转换成一个BeanDefinition对象。
  • 然后通过BeanDefinitionRegistry将这些bean注册到beanFactory中,保存在它的一个ConcurrentHashMap中。
  • 将BeanDefinition注册到了beanFactory之后,在这里Spring为我们提供了一个扩展的切口,允许我们通过实现接口BeanFactoryPostProcessor 在此处来插入我们定义的代码。典型的例子就是:PropertyPlaceholderConfigurer,我们一般在配置数据库的dataSource时使用到的占位符的值,就是它注入进去的。

容器中bean的实例化阶段:

实例化阶段主要是通过反射或者CGLIB对bean进行实例化,在这个阶段Spring又给我们暴露了很多的扩展点:

  • 各种的Aware接口 ,比如 BeanFactoryAware,对于实现了这些Aware接口的bean,在实例化bean时Spring会帮我们注入对应的BeanFactory的实例。
  • BeanPostProcessor接口 ,实现了BeanPostProcessor接口的bean,在实例化bean时Spring会帮我们调用接口中的方法。
  • InitializingBean接口 ,实现了InitializingBean接口的bean,在实例化bean时Spring会帮我们调用接口中的方法。
  • DisposableBean接口 ,实现了BeanPostProcessor接口的bean,在该bean死亡时Spring会帮我们调用接口中的方法。

设计意义:

松耦合。 可以将原来硬编码的依赖,通过Spring这个beanFactory这个工厂来注入依赖,也就是说原来只有依赖方和被依赖方,现在我们引入了第三方——spring这个beanFactory,由它来解决bean之间的依赖问题,达到了松耦合的效果.

bean的额外处理。 通过Spring接口的暴露,在实例化bean的阶段我们可以进行一些额外的处理,这些额外的处理只需要让bean实现对应的接口即可,那么spring就会在bean的生命周期调用我们实现的接口来处理该bean。[非常重要]

2.工厂方法

实现方式:

FactoryBean接口。

实现原理:

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

例子:

典型的例子有spring与mybatis的结合。

代码示例:

Spring 中9 种设计模式 spring中的设计模式有哪些

说明:

我们看上面该bean,因为实现了FactoryBean接口,所以返回的不是 SqlSessionFactoryBean 的实例,而是它的 SqlSessionFactoryBean.getObject() 的返回值。

3.单例模式

  • Spring依赖注入Bean实例默认是单例的。
  • Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行bean的创建。
  • 分析getSingleton()方法
public Object getSingleton(String beanName){
    //参数true设置标识允许早期依赖
    return getSingleton(beanName,true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //检查缓存中是否存在实例
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //如果为空,则锁定全局变量并进行处理。
        synchronized (this.singletonObjects) {
            //如果此bean正在加载,则不处理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //当某些方法需要提前初始化的时候则会调用addSingleFactory 方法将对应的ObjectFactory初始化策略存储在singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //调用预先设定的getObject方法
                    singletonObject = singletonFactory.getObject();
                    //记录在缓存中,earlysingletonObjects和singletonFactories互斥
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  • getSingleton()过程图

ps:spring依赖注入时,使用了 双重判断加锁 的单例模式

Spring 中9 种设计模式 spring中的设计模式有哪些

总结

单例模式定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

spring对单例的实现: spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是任意的java对象。

4.适配器模式

实现方式:

SpringMVC中的适配器HandlerAdatper。

实现原理:

HandlerAdatper根据Handler规则执行不同的Handler。

实现过程:

DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求,处理Handler。

HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。

实现意义:

HandlerAdatper使得Handler的扩展变得容易,只需要增加一个新的Handler和一个对应的HandlerAdapter即可。

因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

5.装饰器模式

实现方式:

Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。

实质:

动态地给一个对象添加一些额外的职责。

就增加功能来说,Decorator模式相比生成子类更为灵活。

6.代理模式

实现方式:

AOP底层,就是动态代理模式的实现。

  • 动态代理:在内存中构建的,不需要手动编写代理类
  • 静态代理:需要手工编写代理类,代理类引用被代理对象。

实现原理:

切面在应用运行的时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建动态的创建一个代理对象。SpringAOP就是以这种方式织入切面的。

织入:把切面应用到目标对象并创建新的代理对象的过程。

7.观察者模式

实现方式:

spring的事件驱动模型使用的是 观察者模式 ,Spring中Observer模式常用的地方是listener的实现。

具体实现:

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

(1)ApplicationEvent抽象类[事件]

  • 继承自jdk的EventObject,所有的事件都需要继承ApplicationEvent,并且通过构造器参数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接口[事件监听器]

  • 继承自jdk的EventListener,所有的监听器都要实现这个接口。
  • 这个接口只有一个onApplicationEvent()方法,该方法接受一个ApplicationEvent或其子类对象作为参数,在方法体中,可以通过不同对Event类的判断来进行相应的处理。
  • 当事件触发时所有的监听器都会收到消息。
  • 代码:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
     void onApplicationEvent(E event);
}

(3)ApplicationContext接口[事件源]

  • ApplicationContext是spring中的全局容器,翻译过来是”应用上下文”。
  • 实现了ApplicationEventPublisher接口。
  • 职责:负责读取bean的配置文档,管理bean的加载,维护bean之间的依赖关系,可以说是负责bean的整个生命周期,再通俗一点就是我们平时所说的IOC容器。
  • 代码:
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);
    }
}

(4)ApplicationEventMulticaster抽象类[事件源中publishEvent方法需要调用其方法getApplicationEventMulticaster]

  • 属于事件广播器,它的作用是把Applicationcontext发布的Event广播给所有的监听器.
  • 代码:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
    implements ConfigurableApplicationContext, DisposableBean {
    private ApplicationEventMulticaster applicationEventMulticaster;
    protected void registerListeners() {
    // Register statically specified listeners first.
    for (ApplicationListener<?> listener : getApplicationListeners()) {
    getApplicationEventMulticaster().addApplicationListener(listener);
    }
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String lisName : listenerBeanNames) {
    getApplicationEventMulticaster().addApplicationListenerBean(lisName);
    }
  }
}

8.策略模式

实现方式:

Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。

Resource 接口介绍

source 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。

Resource 接口主要提供了如下几个方法:

  • getInputStream(): 定位并打开资源,返回资源对应的输入流。每次调用都返回新的输入流。调用者必须负责关闭输入流。
  • exists(): 返回 Resource 所指向的资源是否存在。
  • isOpen(): 返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束应该显式关闭,以防止资源泄漏。
  • getDescription(): 返回资源的描述信息,通常用于资源处理出错时输出该信息,通常是全限定文件名或实际 URL。
  • getFile: 返回资源对应的 File 对象。
  • getURL: 返回资源对应的 URL 对象。

最后两个方法通常无须使用,仅在通过简单方式访问无法实现时,Resource 提供传统的资源访问的功能。

Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。

Spring 为 Resource 接口提供了如下实现类:

  • UrlResource: 访问网络资源的实现类。
  • ClassPathResource: 访问类加载路径里资源的实现类。
  • FileSystemResource: 访问文件系统里资源的实现类。
  • ServletContextResource: 访问相对于 ServletContext 路径里的资源的实现类.
  • InputStreamResource: 访问输入流资源的实现类。
  • ByteArrayResource: 访问字节数组资源的实现类。

这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。

9.模版方法模式

经典模板方法定义:

父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现。

最大的好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好了。

所以父类模板方法中有两类方法:

共同的方法: 所有子类都会用到的代码

不同的方法: 子类要覆盖的方法,分为两种:

  • 抽象方法:父类中的是抽象方法,子类必须覆盖
  • 钩子方法:父类中是一个空方法,子类继承了默认也是空的

注:为什么叫钩子,子类可以通过这个钩子(方法),控制父类,因为这个钩子实际是父类的方法(空方法)!

Spring模板方法模式实质:

是模板方法模式和回调模式的结合,是Template Method不需要继承的另一种实现方式。Spring几乎所有的外接扩展都采用这种模式。

具体实现:

JDBC的抽象和对Hibernate的集成,都采用了一种理念或者处理方式,那就是模板方法模式与相应的Callback接口相结合。

采用模板方法模式是为了以一种统一而集中的方式来处理资源的获取和释放,以JdbcTempalte为例:

public abstract class JdbcTemplate {
     public final Object execute(String sql){
        Connection con=null;
        Statement stmt=null;
        try{
            con=getConnection();
            stmt=con.createStatement();
            Object retValue=executeWithStatement(stmt,sql);
            return retValue;
        }catch(SQLException e){
             ...
        }finally{
            closeStatement(stmt);
            releaseConnection(con);
        }
    }
    protected abstract Object executeWithStatement(Statement   stmt, String sql);
}

引入回调原因:

JdbcTemplate是抽象类,不能够独立使用,我们每次进行数据访问的时候都要给出一个相应的子类实现,这样肯定不方便,所以就引入了回调。

回调代码

public interface StatementCallback{
    Object doWithStatement(Statement stmt);
}

利用回调方法重写JdbcTemplate方法

public class JdbcTemplate {
    public final Object execute(StatementCallback callback){
        Connection con=null;
        Statement stmt=null;
        try{
            con=getConnection();
            stmt=con.createStatement();
            Object retValue=callback.doWithStatement(stmt);
            return retValue;
        }catch(SQLException e){
            ...
        }finally{
            closeStatement(stmt);
            releaseConnection(con);
        }
    }

    ...//其它方法定义
}

Jdbc使用方法如下:

JdbcTemplate jdbcTemplate=...;
    final String sql=...;
    StatementCallback callback=new StatementCallback(){
    public Object=doWithStatement(Statement stmt){
        return ...;
    }
}
jdbcTemplate.execute(callback);

为什么JdbcTemplate没有使用继承?

因为这个类的方法太多,但是我们还是想用到JdbcTemplate已有的稳定的、公用的数据库连接,那么我们怎么办呢?

我们可以把变化的东西抽出来作为一个参数传入JdbcTemplate的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate中的变量。怎么办?

那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到JdbcTemplate,从而完成了调用。

本文链接:https://www.dzdvip.com/45097.html 版权声明:本文内容均来源于互联网。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 395045033@qq.com,一经查实,本站将立刻删除。
(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年2月15日 21:52
下一篇 2023年2月15日 21:54

相关推荐

  • win7电脑显卡驱动怎么更新

    1.右键单击此计算机,然后选择属性 2.单击左侧的设备管理器 3.单击显示适配器 4.右键单击需要更新的图形卡,然后选择“更新驱动程序” 5.我们选择自动搜索驱动程序 6.在线查找驱动程序,然后自己更新图形驱动程序

    2021年12月22日
    68
  • SEM进阶必杀技:高效进行关键词否词

    搜索广告相比于信息流广告线索质量好,除了有主动搜索的因素外,还因为搜索广告可以通过关键词匹配和否词控制流量。
    如果你想要宽泛流量,则匹配可以宽一些,否词可以松一些。

    2021年5月26日
    60
  • 阿里巴巴p8p9什么意思(阿里巴巴p级别划分薪资)

    阿里技术高P那么多,你能喊得出名字技术大牛也就那几个,能占多少比例?而且,有的还都是包装出来的,他们团队之间相互之间还会喷对方水,全靠包装呢。 阿里,或者说大公司升职几大因素怎么排序? 第一,赶风口!大多数人赶风口得靠命,比如赶上昨天快手上市,前年拼多多上市。 在阿里的话,就是去一个风口上的事业部,搞新业务、重点业务。比如前些年的支付宝、盒马(非技术)。阿里升P8,P9难不难?难!很多人应该都听说过6升7通过率,7升8通过率,于是根据金字塔结构,推导出8升9应该难爆了。 事实是:最近几年,支付宝P8升P9的成功率大概在5/8,这个通过率可还行? 第二,造风!也就是通常说的:搞事情。搞大了就是行业变革,搞小了也是升职项目。阿里内部绝大多数高P升职应该靠的就是这种,领导们争夺地盘也要靠下面小弟”搞事情“,所以社招面试P7就得考察这点能力。这种例子在阿里太多了。比如海外短视频vmate,靠花钱买量,最后搞得稀烂关门大吉,还闹出毁约应届生的闹剧,结果负责人照样P9升P10,讽刺不?再比如天猫精灵,当初立下flag目标销售百万,结果3万台都没达到。最后靠天猫双十一99元半卖半送完成目标,要知道99元大概是成本价的1/3 。现在也是一滩烂泥,结果负责人升了VP跑了,下面一大帮P8升了P9然后转岗跑路了。 第三,硬实力!活还是需要人做的,你的硬实力(技术 or 项目管理)够强,升到P8问题不大。造成外面小白觉得阿里P8,P9技术很屌,也是因为这帮人存在。!幸存者偏差嘛,你们在网上看到的阿里技术大牛只占阿里所有P8,P9中的1%,剩下99%都是”平凡无奇“的技术普通人。由于我自己所在的BU不强势,所以从来没见过硬实力P9。 第四,领导关照!也就是裙带关系、向上管理。这么不多数,懂得都懂。百度哈工大帮,网易浙大老白兔帮等等。阿里一样,我以前BU的老板把他实验一大帮师弟师妹都拉过来,要知道他实验室可是偏机械的。师弟还好,师妹这么多年代码都写的磕磕绊绊,照样升职。 第五,熬!这也是阿里佛系躺平员工的正常路子。现在熬起来挺慢的,个人能力不强,遇到的业务又渣渣的话,硕士毕业熬3-4年升P6,再熬3-4年升P7。以前要简单得多,你要知道阿里招聘强势也就是从2013年开始的,之前只能捡百度、腾讯剩下的渣。2011,2012阿里在名校招聘都没有笔试,直接绿色通道发Offer。2015年之前,还有专…

    2022年2月26日
    231
  • 自媒体平台有哪些(做自媒体哪个平台最好)

    我们做自媒体,平台选择也是至关重要的 选择不对,努力白费! 选对赛道比具体怎么做更加重要,避免我们在错误的道路上越走越远 几十个自媒体平台,每个平台属性不一样、流量群体不一样、甚至发布作品的审核机制都完全不一样的。 废话不多说,我们来看看最新整理出来的自媒体平台分类 最新分类 一、长视频平台 西瓜视频、好看和视频、爱奇艺、优酷、腾讯视频、B站 二、短视频平台 抖音、快手、视频号、秒拍、全民小视频 三、资讯类 头条号、百家号、大鱼号、企鹅号、知乎、豆瓣、简书、新浪博客 四、直播平台 抖音直播、西瓜直播、腾讯直播、淘直播、一直播、花椒、映客、虎牙、斗鱼 五、音频平台 喜马拉雅、荔枝微课、懒人听书 六、垂类平台 懂车帝、闲鱼、美团、大众点评、腾讯课堂、网易云课堂 七、社交类平台 微博、小红书 八、海外平台 Youtube、Tiktok、FB 新手刚开始做自媒体的建议: 1、明确你能做什么,你擅长什么,给自己一个清晰明了的定位 比如你是家庭主妇,喜欢做饭,对各种美食深有研究,那么你可以把自己定位成美食类型的自媒体,在入住平台的时候,我们先选择八大类型中的一个类型来操作运营,可以是视频形式也可以是图文形式 2、选择一个自媒体平台 比如头条号,前期没找到自媒体内容创作的灵感,我们可以从写文章开始,这个就非常的简单了 3、入驻自媒体平台 喜欢美食,擅长美食,给自己的定位就是做美食类型的自媒体账号,那么在入驻自媒体平台的时候,就要在自己的名称、简介、头像上下点功夫了 取名的模板:领域+称呼 简介的模板:领域+你的自媒体运营宣言 4、坚持创作优质内容 一名合格的自媒体人一定是一名优质内容创作者 千万不要幻想着搬运内容来赚钱 这完全就是在触碰平台的底线 每个自媒体平台都喜欢原创的内容 记得一定要坚持创作内容 99%的自媒体人为什么没有赚到钱? 根本原因就是三天打鱼两天晒网 5、听话守规矩,顺着平台规则做更重要 选对了平台后,下面这一条也很重要,那就是你守规矩! 你在人家地盘,不听话,不按人家的规则走,人家凭什么给你推荐量,凭什么带你玩! 好了,今天就分享到此,主要是整理了当下自媒体平台的8中类型,根据你自己的定位来选择对的平台

    2021年12月22日
    78
  • 快速提高企业网站SEO优化排名的方法有哪些

    为了让自己的网站排名更上一层,现在很多企业网站都在做SEO优化,对于很多网站来说,想通过搜索引擎制作自己的网站,让更多的用户访问自己的网站,那么如何能迅速提高网站的SEO排名呢?这是我国很多站长都关心的问题 …

    2021年5月31日
    29
  • 男生喜欢压女生身上是什么意思呢?

    有些人睡觉时会有一些小习惯,比如说畏光或是打呼,这无疑会给另一半造成影响。而有的丈夫睡觉时,习惯用腿压着妻子。可能起初妻子会不舒服,渐渐地也适应了这样的动作。其实,用腿压着对方,是有深层心理原因的。一个人睡觉时是最放松的状态,而他表现出来的也是内心最本真的想法。妻子千万不要嫌弃,这正是丈夫爱你的表现。 丈夫的内心需要安全感男人的压力很大,社会赋予他们太多的角色,要求他们不能出错,去对抗生活的各种风浪。所以白天的时候,男人是不能放松的,即使缺少安全感,也不能明显地表现出来。但男人内心的这种情绪不会消失,只是暂时被隐藏。在熟睡时,这种情感就会释放出来。他们用腿压着妻子,是感受到了另一半的存在,就会心里踏实。 如果妻子理解对方的话,就应该知道,自己已经被对方当成了非常信任的人。丈夫暴露出来依赖感,恰恰是因为从心底认可妻子。看这些睡眠小习惯,是可以看出夫妻感情怎么样的。真正有爱的妻子,反而应该心疼丈夫没有安全感的状态。 而一些同床异梦的夫妻,就会本能地有距离感,可能最终分床睡。丈夫更不会伸腿过去,这甚至会引起妻子的大吵,嫌弃对方妨碍了自己睡眠。 丈夫需要另一半的爱 当你看到男人成熟、睿智的一面时,他未必真的爱你。而当你看到他单纯地像孩子那一面时,才说明你走进了他心里。在你面前,他不需要伪装,可以像小孩子一样撒娇缠着你。甚至睡眠的时候,也把腿压在你身上。他就是想对你说,需要你多一点的爱。而你的爱对他很重要,你明白了这一点,就该好好呵护内心缺爱的他。 对于有些丈夫来说,如果不把腿压在另一半身上,自己就睡不踏实。只有另一半让自己抱着、压着的时候,他才能感觉到心灵相通,也感觉到对方爱意的回馈。不过有些妻子却觉得,丈夫这样就像抱着玩具睡觉的小孩,显得有些幼稚。她们会认为,一直索要爱意的丈夫不够有魅力。如果妻子真是这样的想法,那丈夫也要调整自己的睡眠习惯了 丈夫想表现对妻子的呵护 有些丈夫白天的时候忙于工作,几乎没什么时间和另一半交流。甚至因为一些鸡毛蒜皮的事情,和妻子产生了口角。那些忘了缴费的无奈,给孩子辅导功课的烦躁,都在悄悄地影响夫妻感情。而在睡眠的时候,白天的浮躁都沉淀下来,不应再为各种矛盾而牵扯。这时丈夫的举动,其实是表达对妻子的爱,也是借由此拉近两人的距离。 曾经有一位妻子反映,在和丈夫一起的20年里,虽然纷纷扰扰经历了许多,但丈夫每晚都会用腿压着自己入睡。每当这时她就知道…

    2022年6月11日
    146