从shiro源码角度学习工厂方法设计模式

绪论

shiro是一个简单易用,功能强大的Java安全框架,学习其源码设计思想对我们的编码水平的提高大有裨益。现在,就从源码角度带大家学习一下shiro里面的工厂方法模式。 这里的前提是读者有过使用shiro的基础,没有也行,关键理解思想就可以了。

从一个简单例子说起

首先,我们先从一个简单的例子说起。我们在使用ini文件来作为用户角色权限配置的时候,我们获取SecurityManager的方法是:

 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:demo01_getstarted.ini");
 
 SecurityManager securityManager = factory.getInstance();
复制代码

上面两行代码看似简单,其实框架底层就使用了工厂方法模式。
关于上面的例子就不多讲了。这里是侧重分析工厂方法设计模式。

工厂方法模式

在工厂方法模式中,我们对模式角色划分为四种:
1、抽象产品(产品接口):比如上面shiro实例中的SecurityManager
2、具体产品:比如实现了SecurityManager的类
3、抽象工厂(工厂接口):比如上面shiro实例中的Factory、AbstractFactory
4、具体工厂:比如上面shiro实例中的IniSecurityManagerFactory

我们先来看看shiro里面工厂方法模式实现的源码:

一、顶级工厂抽象接口Factory,所有抽象工厂类都继承此接口

public interface Factory<T> {
    T getInstance();
}
复制代码

二、抽象工厂类,这个抽象工厂类负责获取工厂实例,具体创建过程由其子类来实现

public abstract class AbstractFactory<T> implements Factory<T> {

    private boolean singleton;
    private T singletonInstance;

    public AbstractFactory() {
        this.singleton = true;
    }

    public boolean isSingleton() {
        return singleton;
    }

    public void setSingleton(boolean singleton) {
        this.singleton = singleton;
    }
   // 获取工厂实例,可以以单例形式获取,也可以每一次获取都创建一个实例
    public T getInstance() {
        T instance;
        if (isSingleton()) {
            if (this.singletonInstance == null) {
                this.singletonInstance = createInstance();
            }
            instance = this.singletonInstance;
        } else {
            instance = createInstance();
        }
        if (instance == null) {
            String msg = "Factory 'createInstance' implementation returned a null object.";
            throw new IllegalStateException(msg);
        }
        return instance;
    }

    protected abstract T createInstance();
}

复制代码

上面两个工厂类抽象了最基本的工厂接口--创建工厂、获取工厂。如果我们需要对工厂类进行扩展的话,只需要继承AbstractFactory来实现即可,非常方便。现在看一下AbstractFactory的一个子类。
IniFactorySupport是一个特定的抽象工厂类,是根据ini文件来创建工厂实例的工厂抽象类。我们不需要细究IniFactorySupport代码干了什么。只需要明白,它是对根据ini文件创建工厂做了一些逻辑处理就好了。
我们可以看到,继承AbstractFactory,我们可以随便扩展定制我们工厂类的行为。

public abstract class IniFactorySupport<T> extends AbstractFactory<T> {

    public static final String DEFAULT_INI_RESOURCE_PATH = "classpath:shiro.ini";

    private static transient final Logger log = LoggerFactory.getLogger(IniFactorySupport.class);

    private Ini ini;

    private Map<String, ?> defaultBeans;

    protected IniFactorySupport() {
    }

    protected IniFactorySupport(Ini ini) {
        this.ini = ini;
    }

    public Ini getIni() {
        return ini;
    }

    public void setIni(Ini ini) {
        this.ini = ini;
    }

    protected Map<String, ?> getDefaults() {
        return defaultBeans;
    }

    public void setDefaults(Map<String, ?> defaultBeans) {
        this.defaultBeans = defaultBeans;
    }

    public static Ini loadDefaultClassPathIni() {
        Ini ini = null;
        if (ResourceUtils.resourceExists(DEFAULT_INI_RESOURCE_PATH)) {
            log.debug("Found shiro.ini at the root of the classpath.");
            ini = new Ini();
            ini.loadFromPath(DEFAULT_INI_RESOURCE_PATH);
            if (CollectionUtils.isEmpty(ini)) {
                log.warn("shiro.ini found at the root of the classpath, but it did not contain any data.");
            }
        }
        return ini;
    }
    
    protected Ini resolveIni() {
        Ini ini = getIni();
        if (CollectionUtils.isEmpty(ini)) {
            log.debug("Null or empty Ini instance.  Falling back to the default {} file.", DEFAULT_INI_RESOURCE_PATH);
            ini = loadDefaultClassPathIni();
        }
        return ini;
    }
   
    public T createInstance() {
        Ini ini = resolveIni();

        T instance;

        if (CollectionUtils.isEmpty(ini)) {
            log.debug("No populated Ini available.  Creating a default instance.");
            instance = createDefaultInstance();
            if (instance == null) {
                String msg = getClass().getName() + " implementation did not return a default instance in " +
                        "the event of a null/empty Ini configuration.  This is required to support the " +
                        "Factory interface.  Please check your implementation.";
                throw new IllegalStateException(msg);
            }
        } else {
            log.debug("Creating instance from Ini [" + ini + "]");
            instance = createInstance(ini);
            if (instance == null) {
                String msg = getClass().getName() + " implementation did not return a constructed instance from " +
                        "the createInstance(Ini) method implementation.";
                throw new IllegalStateException(msg);
            }
        }

        return instance;
    }

    protected abstract T createInstance(Ini ini);

    protected abstract T createDefaultInstance();
}

复制代码

通过看类关系图,IniSecurityManagerFactory继承IniFactorySupport,在IniFactorySupport基础上面进一步封装创建工厂过程。
IniSecurityManagerFactory的源码就不贴出来了,明白设计思想就可以了。
通过源码分析,我们可以看到,首先抽象出最基本的工厂接口,具体的工厂类由其子类去实现。一个具体工厂类对应这一类产品。当需要新增产品类的时候,我们只需要新加工厂类,并且新增对应的产品类即可,不需要修改原有工厂类代码,符合了设计模式中的开闭原则、单一职责原则。

demo实现

一、创建工厂接口:IFactory IFactory.java

/**
 * 泛型代表的是产品类型
 *
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public interface IFactory<T> {

    /**
     * 获取产品
     *
     * @return 产品实例
     */
    T getInstance();

}

复制代码

进一步抽象工厂接口 AbstractFactory.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public abstract class AbstractFactory<T> implements IFactory<T> {

   /**
        * 创建产品,具体创建过程由其子类实现
        *
        * @return 创建的产品实例
        */
       protected abstract T createInstance();
   
       @Override
       public T getInstance() {
           return createInstance();
       }
}


复制代码

二、创建产品接口 IProduct.java


/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public interface IProduct {

}

复制代码

进一步分类产品,创建一类产品接口 Car.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public abstract class Car implements IProduct {
    /**
     * 创建汽车产品
     *
     * @return 创建的汽车产品
     */
    protected abstract Car createCar();

    /**
     * 驾驶汽车
     */
    public abstract void drive();
}

复制代码

具体产品类 Taxi.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public class Taxi extends Car {

    private Taxi taxi;

    @Override
    protected Car createCar() {
        this.taxi = new Taxi();
        return this.taxi;
    }

    @Override
    public void drive() {
        System.out.println("我是接送客的车");
    }
}
复制代码

三、创建具体产品的工厂类 TaxtFactory.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public class TaxiFactory extends AbstractFactory<Car> {

    @Override
    protected Car createInstance() {
        return new Taxi();
    }
}
复制代码

四、客户端代码 Client.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public class Clent {
    public static void main(String[] args) {
        IFactory<Car> factory = new TaxiFactory();
        Car taxi = factory.getInstance();
        taxi.drive();
    }
}
复制代码

通过例子,我们知道,在工厂方法模式中,有一个顶级的产品接口,对产品作出做基本的抽象,然后产品下面还有不同产品的分类,在同一类产品中又有不同的具体产品,比如car类产品下面又会有多种汽车产品。每一个具体的产品都有对应一个具体的工厂类。 如果想再新加一个新的产品,不论是car类产品,还是非car类产品,我们都可以通过新加工厂类和产品类来实现,比如新增一个船类产品

Ship.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public abstract class Ship implements IProduct {
    /**
     * 造船
     *
     * @return
     */
    protected abstract IProduct createShip();

    public abstract void doSomething();
}
复制代码

创建渔船 FishShip.java

/**
 * 渔船
 *
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public class FishShip extends Ship {

    private FishShip ship;

    @Override
    public IProduct createShip() {
        this.ship = new FishShip();
        return this.ship;
    }

    @Override
    public void doSomething() {
        System.out.println("我在打鱼呀");
    }
}

复制代码

创建渔船工厂类 FishShipFactory.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public class FishShipFactory extends AbstractFactory<Ship> {
    @Override
    protected Ship createInstance() {
        return new FishShip();
    }
复制代码

添加一个产品,我们就得添加产品类和工厂类。对于系统的扩展来说,工厂方法模式有优势,但是会增加系统的复杂度以及类的数量。

结束语

对于设计模式,大家重点是理解这样设计的原理与优缺点,不要机械的背诵条条框框。实际我们在开发真实系统时,会糅合多种设计模式在一起。只有我们对设计模式有本质性的认识和掌握,才是真正掌握了设计模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈微信小程序对于房地产行业的影响

前几日&#xff0c;我们曾经整理过一篇文章是关于微信小程序对于在线旅游业的影响的一些反思&#xff08;浅谈微信小程序对OTA在线旅游市场的影响&#xff09;&#xff0c;近日由于生活工作的需要走访了一些房地产的住宅商品房&#xff0c;突然想到微信小程序对于房地产行业会有…

新型基础测绘与实景三维中国建设技术文件【1】名词解释

文章目录一、新型基础测绘 new fundamental surveying and mapping**二、实景三维 3D real scene**三、时空大数据平台 spatio-temporal big data platform**四、地理实体 geo-entity**五、基础地理实体 fundamental geo-entity**六、组合地理实体数据 combined geo-entity dat…

Android.bp 和 Android.mk 的对应关系

参考 Soong 构建系统 Android.mk 转为 Android.bp 没有分支、循环等流程控制的简单的 Android.mk &#xff0c;可以通过 androidmk 命令转化为 Android.bp source 、lunch 之后执行即可。 androidmk Android.mk > Android.bp对应关系 Android 13 &#xff0c;build/soon…

深入理解javascript原型和闭包

原文链接http://www.cnblogs.com/wangfupeng1988/p/3977924.html 对象是属性的集合。 function show(x) {console.log(typeof(x)); // undefinedconsole.log(typeof(10)); // numberconsole.log(typeof(abc)); // stringconsole.log(typeof(true)); // booleanconsole.lo…

薪资高压线

阅读本文大概需要5分钟。最近一名读者咨询一个问题&#xff1a;洋哥&#xff0c;最近公司有一名同事因为打探其他人薪资被开除了&#xff0c;为啥我们公司要把薪资设置为高压线。这是个好问题&#xff0c;解答完他的疑惑后想起了一年多前写过一篇&#xff0c;彼时读者还比较少&…

达摩院年终预测出炉:2022 十大科技趋势,AI for Science 高居榜首

作为“一所探索科技未知的研究院”&#xff0c;阿里巴巴达摩院成立至今已经四年了。 这四年来&#xff0c;达摩院秉持着“探索科技位置&#xff0c;以人类愿景为驱动力&#xff0c;开展基础科学和颠覆式技术创新研究”的原则与使命&#xff0c;在基础科研和硬科技发展上“遍地生…

chrome调试工具高级不完整使用指南(基础篇)

一、前言 本文记录的是作者在工作上面对chrome的一些使用和情况的分析分享&#xff0c;内容仅代表个人的观点。转发请注明出处(http://www.cnblogs.com/st-leslie/),谢谢合作 二、浏览器模块介绍 由于chrome浏览器一直在不断的进行更新迭代&#xff0c;会不断的新增功能&#x…

新型基础测绘与实景三维中国建设技术文件【2】基础地理实体分类、粒度及精度基本要求

《新型基础测绘体系建设试点技术大纲》指出&#xff0c;新型基础测绘将以“基础地理实体”为核心的成果模式创新为切入点&#xff0c;带动技术体系、生产组织体系和政策标准体系的全面创新&#xff0c;从而实现基础测绘高质量发展。 基础地理实体作为新型基础测绘产品体系的核心…

构建和实现单点登录解决方案(转载于IBMdeveloperWorks)

将一个开放源码的基于 Java 的身份验证组件集成进 Web 门户中 在现有的应用程序中实现单点登录解决方案&#xff08;single sign-on&#xff0c;SSO&#xff0c;即登录一次&#xff0c;就可以向所有网络资源验证用户的身份&#xff09;是非常困难的&#xff0c;但是在构建复杂的…

分享一个基于Abp 和Yarp 开发的API网关项目

这个项目起源于去年公司相要尝试用微服务构建项目,在网关的技术选型中,我们原本确认了ApiSix 网关,如果需要写网关插件需要基于Lua脚本去写,我和另外一个同事当时基于这个写了一个简单的插件,但是开发测试以及发布都很麻烦,而且使用Lua脚本作为插件的开发语言本身也不是我们强项…

罗振宇2022“时间的朋友”跨年演讲全文稿(pdf)

2021年12月31日20:30&#xff0c;五粮液成都金融城演艺中心&#xff0c;罗振宇“时间的朋友”跨年演讲如约而至。 罗胖曾发下大愿望&#xff1a;跨年演讲要连办二十年。今年是第七场&#xff0c;也是最特殊的一场&#xff0c;罗胖面对12000个空座位&#xff0c;用53个好故事&am…

08.LoT.UI 前后台通用框架分解系列之——多样的Tag选择器

LOT.UI分解系列汇总&#xff1a;http://www.cnblogs.com/dunitian/p/4822808.html#lotui LoT.UI开源地址如下&#xff1a;https://github.com/dunitian/LoTCodeBase/tree/master/LoTUI 这个必须说下&#xff0c;本来是用Bootstrap-Select做的&#xff0c;很漂亮&#xff0c;正好…

jquery文档加载完毕后执行的几种写法

2019独角兽企业重金招聘Python工程师标准>>> 1.js文档加载完毕 标签内 οnlοad"test()"window.οnlοadfunction(){}2.jquery文档加载完毕 //方式1 $(document).ready(function(){//TODO }); //方式2 $(function(){//TODO }) //方式3 $(function($){//TO…

新型基础测绘与实景三维中国建设技术文件【3】基础地理实体空间身份编码规则

基础地理实体是新型基础测绘产品体系中的核心成果&#xff0c;是推动基础测绘工作转型升级的关键。与现有的测绘地理信息数据不同&#xff0c;基础地理实体具有多粒度、多模态、多层次&#xff0c;以及搭载结构化、半结构化和非结构化多样化信息的鲜明特点。 基础地理实体空间…

oracle 表 视图 存储过程 序列 job

table 表--delete tabledrop table Test1;-- Create tablecreate table TEST1(ID NUMBER,T_NAME VARCHAR2(100),DT DATE);-- 添加注释comment on column TEST1.T_NAME is 名称;--添加age字段alter table Test1 add (age NUMBER(8));--删除字段alter table TABLE_NAME …

[转]Docker 大势已去,Podman 即将崛起

Podman Podman 什么是Podman&#xff1f;Podman和Docker的主要区别是什么&#xff1f;Podman的使用与docker有什么区别&#xff1f;Podman 常用命令 容器镜像部署 PodmanPodman 加速器使用 Podman 运行一个容器列出运行的容器检查正在运行的容器查看一个运行中容器的日志查看一…

基于Kubernetes v1.24.0的集群搭建(一)

一、写在前面 K8S 1.24作为一个很重要的版本更新&#xff0c;它为我们提供了很多重要功能。该版本涉及46项增强功能&#xff1a;其中14项已升级为稳定版&#xff0c;15项进入beta阶段&#xff0c;13项则刚刚进入alpha阶段。此外&#xff0c;另有2项功能被弃用、2项功能被删除。…

mvc设计模式和mvc框架的区别

一组概念需要先理解&#xff0c;因为后面需要用&#xff1a; 架构&#xff1a;简单的说架构就是一个蓝图&#xff0c;是一种设计方案&#xff0c;将客户的不同需求抽象成为抽象组件&#xff0c;并且能够描述这些抽象组件之间的通信和调用。 框架&#xff1a;软件框架是项目软件…

新型基础测绘与实景三维中国建设技术文件【4】基础地理实体数据元数据

基础地理实体数据是新型基础测绘产品体系中的核心成果&#xff0c;为实现该数据的规范化管理和使用&#xff0c;编制基础地理实体数据元数据技术文件。本文件规定了基础地理实体数据元数据的基本要求和元数据内容&#xff0c;适用于二维表达形式、三维表达形式基础地理实体数据…

思科三层交换机充当路由器实现全网互通

转载于:https://blog.51cto.com/13568840/2059797