从shiro源码角度学习建造者设计模式

栏目: Java · 发布时间: 8年前

内容简介:从shiro源码角度学习建造者设计模式

今天就和大家分享一下shiro源码里面使用到的建造者模式。在介绍建造者模式相关知识之前,我们先来看一段例子分析。

从一个简单的例子说起

我们在使用shiro获取登录用户的时候,比如使用ini文件配置用户角色权限,我们可以这样写:

        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:demo01_getstarted.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject currentUser = SecurityUtils.getSubject();

SecurityUtils.getSubject() 是获取当前用户,我们跳进去看一下,是怎么获取的

 public static Subject getSubject() {
        Subject subject = ThreadContext.getSubject();
        if (subject == null) {
            subject = (new Subject.Builder()).buildSubject();
            ThreadContext.bind(subject);
        }
        return subject;
    }

上面代码,首先从ThreadContext里面获取Subject,获取不到就创建,我们看看创建过程,跳进去看看 (new Subject.Builder()).buildSubject() 干了什么。 这里不贴过长代码了,Builder是Subject的静态内部类,用来完成Subject的复杂创建过程,使得调用就无需关注Subject怎么创建的,只要知道怎么获取Subject就可以了。 我们看一下,Builder类里面干了什么事情

 public static class Builder {

        private final SubjectContext subjectContext;

        private final SecurityManager securityManager;

        public Builder() {
            this(SecurityUtils.getSecurityManager());
        }

        public Builder(SecurityManager securityManager) {
            if (securityManager == null) {
                throw new NullPointerException("SecurityManager method argument cannot be null.");
            }
            this.securityManager = securityManager;
            this.subjectContext = newSubjectContextInstance();
            if (this.subjectContext == null) {
                throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
                        "cannot be null.");
            }
            this.subjectContext.setSecurityManager(securityManager);
        }

        protected SubjectContext newSubjectContextInstance() {
            return new DefaultSubjectContext();
        }

        protected SubjectContext getSubjectContext() {
            return this.subjectContext;
        }

        public Builder sessionId(Serializable sessionId) {
            if (sessionId != null) {
                this.subjectContext.setSessionId(sessionId);
            }
            return this;
        }

        public Builder host(String host) {
            if (StringUtils.hasText(host)) {
                this.subjectContext.setHost(host);
            }
            return this;
        }

        public Builder session(Session session) {
            if (session != null) {
                this.subjectContext.setSession(session);
            }
            return this;
        }

        public Builder principals(PrincipalCollection principals) {
            if (principals != null && !principals.isEmpty()) {
                this.subjectContext.setPrincipals(principals);
            }
            return this;
        }

        public Builder sessionCreationEnabled(boolean enabled) {
            this.subjectContext.setSessionCreationEnabled(enabled);
            return this;
        }

        public Builder authenticated(boolean authenticated) {
            this.subjectContext.setAuthenticated(authenticated);
            return this;
        }

        public Builder contextAttribute(String attributeKey, Object attributeValue) {
            if (attributeKey == null) {
                String msg = "Subject context map key cannot be null.";
                throw new IllegalArgumentException(msg);
            }
            if (attributeValue == null) {
                this.subjectContext.remove(attributeKey);
            } else {
                this.subjectContext.put(attributeKey, attributeValue);
            }
            return this;
        }
        // 创建Subject
        public Subject buildSubject() {
            return this.securityManager.createSubject(this.subjectContext);
        }
    }

我们可以看到,Builder完成了Subject一些列的复杂创建过程,包括sesson、sessionid等的设置等过程。最后会调用 buildSubject() 方法来创建Subject。 我们就不继续深入看 this.securityManager.createSubject(this.subjectContext) 里面干了什么事了。 回到前面,我们可以看到Subject对象会包含许多的当前用户信息,但是这些信息框架底层都替我们设置好了,我们只要获取Subject对象即可,调用 Subject currentUser = SecurityUtils.getSubject(); 就可以了。 这样看来,是不是特别方便。这些都归功于建造这模式的使用。

建造这模式基本概念

通过分析shiro源码,我们对建造这模式有了一个初步的感知,目前大家可以理解成创建复杂对象由一个建造类来完成。那么,我们就来学习一下建造者模式的一些理论知识,相信大家结合上面的例子分析,不会感到吃力乏味的。

什么是建造者模式

建造者模式又称为生成器模式。

建造者模式:它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 参考维基百度百科:生成器模式

可能有些读者看了这句话迷迷糊糊的,那么这句话是什么意思呢?结合上面的例子,我们可以知道Subject对象有很多属性,通过SubjectContext对象来保存的,最后创建Subject对象的时候,传递SubjectContext就可以了。这样比如,我们创建两个有不同属性值的Subject:

        Subject user_1 = new Subject.Builder().authenticated(false).buildSubject();
        Subject user_2 = new Subject.Builder().authenticated(true).buildSubject();

我们创建Subject的不同实现方法,比如authenticated()一个设置true,一个设置false,我们就构造出来了有不同表现(属性)的Subject对象了,一个是授权,一个是未授权。

为什么使用建造者模式

使用建造者模式可以屏蔽复杂的对象创建过程,对象的创建对调用者来说是透明的,这样就使得程序耦合度降低,程序可读性和维护性提高了。

结束语

对于设计模式,大家不要机械地学习,个人觉得主要理解其设计思想和好处就可以了。在编码过程中大家不要刻意去使用它,而是去思考如何运用它更好地设计代码。这样,经过时间的推磨,大家就可以真正做到把 设计模式 融会贯通,手到拈来。


以上所述就是小编给大家介绍的《从shiro源码角度学习建造者设计模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Web Application Hacker's Handbook

The Web Application Hacker's Handbook

Dafydd Stuttard、Marcus Pinto / Wiley / 2011-9-27 / USD 50.00

The highly successful security book returns with a new edition, completely updated Web applications are the front door to most organizations, exposing them to attacks that may disclose personal infor......一起来看看 《The Web Application Hacker's Handbook》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换