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

绪论

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

从一个简单的例子提及

咱们在使用shiro获取登陆用户的时候,好比使用ini文件配置用户角色权限,咱们能够这样写:session

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

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

 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源码,咱们对建造这模式有了一个初步的感知,目前你们能够理解成建立复杂对象由一个建造类来完成。那么,咱们就来学习一下建造者模式的一些理论知识,相信你们结合上面的例子分析,不会感到吃力乏味的。ui

什么是建造者模式

建造者模式又称为生成器模式。
建造者模式:它能够将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不一样实现方法能够构造出不一样表现(属性)的对象。 参考维基百度百科:生成器模式
可能有些读者看了这句话迷迷糊糊的,那么这句话是什么意思呢?结合上面的例子,咱们能够知道Subject对象有不少属性,经过SubjectContext对象来保存的,最后建立Subject对象的时候,传递SubjectContext就能够了。这样好比,咱们建立两个有不一样属性值的Subject:this

        Subject user_1 = new Subject.Builder().authenticated(false).buildSubject();
        Subject user_2 = new Subject.Builder().authenticated(true).buildSubject();
复制代码

咱们建立Subject的不一样实现方法,好比authenticated()一个设置true,一个设置false,咱们就构造出来了有不一样表现(属性)的Subject对象了,一个是受权,一个是未受权。编码

为何使用建造者模式

使用建造者模式能够屏蔽复杂的对象建立过程,对象的建立对调用者来讲是透明的,这样就使得程序耦合度下降,程序可读性和维护性提升了。spa

结束语

对于设计模式,你们不要机械地学习,我的以为主要理解其设计思想和好处就能够了。在编码过程当中你们不要刻意去使用它,而是去思考如何运用它更好地设计代码。这样,通过时间的推磨,你们就能够真正作到把设计模式融会贯通,手到拈来。设计

相关文章
相关标签/搜索