绪论

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();    }复制代码

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

结束语

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

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

  1. 从框架源码中学习创建型设计模式

    文章目录 从框架源码中解读创建型设计模式 工厂模式 案例一:RocketMQ源码-创建Producer生产者 案例二:RocketMQ源码-创建过滤器工厂 抽象工厂 案例一:Dubbo源码-创建缓存的 ...

  2. 【设计模式-手写源码-2】-工厂方法模式-基于魔兽争霸冰封王座

    1:主题拆解 ① 对比简单工厂,建立工厂方法(FactoryMethod) ②工厂方法的优缺点和应用 ③对修改关闭对扩展开放 ④面向切面编程 2:基本介绍 定义一个用于创建对象的接口,让子类决定实例化 ...

  3. 从源码角度学习Volley框架

    转载请标明出处:http://blog.csdn.net/newhope1106/article/details/53615398 虽然网上有很多文章是分析Volley框架的,不过博客的作用,一个是让 ...

  4. 从框架源码中学习结构型设计模式

    文章目录 从框架源码学习结构型设计模式 适配器模式 应用实例 案例一:dubbo框架日志适配器 Logger接口 日志实现类 Logger适配器接口 LoggerAdapter实现类 Logger日志 ...

  5. Mybatis底层原理学习(二):从源码角度分析一次查询操作过程

    在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...

  6. Shiro源码学习之二

    接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...

  7. Shiro源码学习之一

    一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ...

  8. 从另一个角度去解读Blinker,剖析精简源码,学习开源精神,菜鸟哥还是忍不住对它下手了

    文章目录 1.解读起因 2.解读点 2.1 解读硬性要求 3.解读过程 3.1 解读理念 3.1.1 官方说明 3.2 解读组合方式 3.2.1 绿色 -- 必选宏 BLINKER_BLE -- bl ...

  9. 从源码角度看Android系统Launcher在开机时的启动过程

    Launcher是Android所有应用的入口,用来显示系统中已经安装的应用程序图标. Launcher本身也是一个App,一个提供桌面显示的App,但它与普通App有如下不同: Launcher是所 ...

最新文章

  1. vue 输入框限制3位小数_vue+element 中 el-input框 限制 只能输入数字及几位小数(自定义)和输入框之键盘...
  2. 显卡测试软件3d mark,显卡评测工具3DMark跑分结果变了:可以直接PK同等配置
  3. 数据库开发——MySQL——索引原理
  4. linux之nl命令
  5. IBM MQ 使用指南
  6. ADO.NET常用对象详解之:Command对象
  7. linux 分区个数限制,Linux分区个数限制[转载]
  8. HDU4619--Warm up 2
  9. 目前常用的开源服务器端技术
  10. html 行级元素和块级元素标签列表分别有哪些
  11. java 建立缓存_java动态缓存——创建一个简单的缓存
  12. 数据结构与计算机网络参考书,2019计算机考研:数据结构参考书及复习重点
  13. HTTP的报文(详解)
  14. CAD图纸中图块无法打开编辑?
  15. ROS2.9.27架设网吧软路由实战篇之端口映射与回流
  16. 如何创作属于自己的NFT?
  17. java汽车租赁系统(java租赁系统java共享汽车java汽车租借系统)java汽车租赁管理系统java自行车租赁系统共
  18. 银行双活数据中心建设项目实践
  19. 六度分离(Floyd算法)
  20. balsamiq原型工具

热门文章

  1. mysql端口被占用_MySQL重启端口被占用处理
  2. 外国经典儿童读物合集pdf_帮助父母在线购买儿童读物–用户体验案例研究
  3. 软件设计师09-面向对象-用例图
  4. Bash Cookbook 学习笔记 【中级】
  5. HAProxy的日志配置以及ACL规则实现负载均衡
  6. [javaEE] JDBC快速入门
  7. linux下查看mysql的当前连接情况
  8. Docker最全教程——从理论到实战(七)
  9. 公钥,私钥和数字签名
  10. 洛谷P2587 [ZJOI2008] 泡泡堂