【设计模式】- 结合Spring理解设计模式

一、简介

1、是什么

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结,为不断重复发生的问题,提供解决方案。

2、为什么

①、提高代码可重用性
②、提高代码的可读性
③、保障代码的可靠性

3、怎么用

  • 使用场景:
    ①、在程序软件架构设计上使用(架构)
    ②、在软件架构设计上会使用(代码)
  • 优良系统设计特点:
    ①、可扩展性
    ②、灵活性(用和不用,不用太多增删代码)
    ③、组件化可插拔式(jar包依赖)
  • 设计原则:
名称简介例子重要性
单一职责原则类职责单一,不要太多职责放到同一个类。(高内聚,低耦合)userService 只写用户业务增删改,定时任务放别类★★★★☆
开闭原则软件实体对拓展开发,对修改关闭基类不可修改★★★★★
里氏代换原则软件系统中,能接受父类,也能接受子类通过new子类创建父类★★★★☆
依赖倒转原则抽象不依赖实现,实现依赖抽象用到接口时,用依赖将其实现对象注入★★★★★
接口隔离原则多个专用接口替代一个统一的接口业务接口,由一个个CRUD接口组成★★☆☆☆
合成复用原则多实用组合和聚合关联(注入),少继承spring 容器注入★★★★☆
迪米特原则软件实体使用第三者减低与现有对象的耦合度A依赖BCD——>A依赖E依赖BCD★★★☆☆

二、创建型模式

用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”,单例、原型、工厂方法、抽
象工厂、建造者5种设计模式属于创建型模式。

1、单例(Singleton)模式

  • 定义
    1、单例类只能有一个实例。
    2、单例类必须自己创建自己的唯一实例。
    3、单例类必须给所有其他对象提供这一实例。
  • 优点
    1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
    2、避免对资源的多重占用(比如写文件操作)。

单例设计案例(饿汉式、懒汉式、双重验证)

public class SingleModel {

    //1、单例类只能有一个实例。                  A a = new A()
    private static SingleModel singleModel = new SingleModel();

    //2、单例类必须自己创建自己的唯一实例。
    private SingleModel() {
    }

    //3、单例类必须给所有其他对象提供这一实例。
    public static SingleModel getInstance(){
        return singleModel;
    }
}

案例一为基础的单例模式,但是存在问题——每次初始化时候会创建对象,不能达到使用才创建,不使用不创建。(饿汉式)

public class SingleModel2 {

    //整个应用程序中只有一个自己的实例
    private static SingleModel2 singleModel;

    //只能自己创建自己
    private SingleModel2() {
    }

    //需要提供一个方法让外界调用获取实例
    public static synchronized SingleModel1 getInstance(){
        if(singleModel==null){
            singleModel = new SingleModel2();
        }
        return singleModel;
    }
}

案例二,把new SingleModel下放到方法体内,但是由于出现问题,如果并发线程进来,那么会创建多个对象;于是上了方法锁,但是随之而来的是如果并发,不管有没有创建该对象,都会排队等待,效率低下。(懒汉式比这个少synchronized ,线程安全问题)

public class SingleModel3 {

    //1、单例类只能有一个实例。
    private static SingleModel3 singleModel;

    //2、单例类必须自己创建自己的唯一实例。
    private SingleModel3() {
    }

    //3、单例类必须给所有其他对象提供这一实例。
    public static SingleModel3 getInstance(){
        if(singleModel==null){
            //控制多线程安全
            synchronized (SingleModel3.class){
                if(singleModel==null){
                    //对象为空,创建对应实例
                    singleModel = new SingleModel3();
                }
            }
        }
        return singleModel;
    }
}

案例三,把方法锁改成对象锁+类锁,提高调用效率;但是引发问题如果第一个判断并发量大,都进去了,仍然会创建多个对象,于是加上了双重校验。(双重校验模式)

2、工厂设计(Factory Method)模式

  • 定义

提供了一种 创建对象的最佳方式。它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。

(例 A类里面需创建BCD实例,而一般BCD上会有接口实现,所以是AI a = new A(); BI b = new B(); CI c = new C();于是A类就依赖了6个类,那么我们把创建的事交给工厂F去创建,那么A只会依赖AI、BI、CI、F四个类)

  • 优点
    ①、使用对象,只需要知道名称即可
    ②、屏蔽产品的具体实现,调用者只需专注接口
    ③、降低了耦合性

工厂模式案例(产品线生产)

//产品父类
public interface Product {

    //查看产品详情
    void show();
}
//汽车子类
public class Car implements Product {
    @Override
    public void show() {
        System.out.println("汽车:比亚迪!");
    }
}
//手机子类
public class Mobile implements Product {
    //产品详情
    @Override
    public void show() {
        System.out.println("手机:HUAWEI P40 Pro +");
    }
}
public class ProductFactory {
    /***
     * 根据用户的需求创建不同的产品
     * @return
     */
    public static Product getBean(String name){
        if(name.equals("mobile")){
            return new Mobile();
        }else if(name.equals("car")){
            return new Car();
        }
        return null;
    }
}
public class ProductFactoryTest {

    public static void main(String[] args) {
        Product mobile = ProductFactory.getBean("mobile");
        Product car = ProductFactory.getBean("car");

        mobile.show();
        car.show();
    }
}

输出结果:
手机:HUAWEI P40 Pro +
汽车:比亚迪!

Spring 中的 BeanFactory工厂模式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置文件中配置 定义bean-->
    <bean id="car" class="com.xxx.factory.Car"/>
    <bean id="mobile" class="com.xxx.factory.Mobile"/>
</beans>
public class BeanFactoryTest {

    public static void main(String[] args) {
        Resource resource = new ClassPathResource("spring-factory.xml");
        BeanFactory beanFactory = new XmlBeanFactory(resource);

        Product car1 = (Product) beanFactory.getBean("car");
        Product car2 = (Product) beanFactory.getBean("car");
        car1.show();

        System.out.println(car1 == car2);
    }
}

输出结果:
汽车:比亚迪!
true
此处可以看出,beanFactory帮我们创建了对象,并且这个对象是单例的,那么在Spring里面是怎么做到的呢?

public class AopTest {

    public static void main(String[] args) {
        ApplicationContext act = new ClassPathXmlApplicationContext("spring-aop.xml");
        //此处getBean就是工厂模式中取对象的方法
        UserServiceImpl userService = (UserServiceImpl) act.getBean("userService");
        userService.add();
    }
}
//进入getBean方法,因为getBeanFactory与getBean都是抽象方法,所以我们打断点进去
public Object getBean(String name) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(name);
    }
//debug进入getBean 
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        //发现此处会获取实例对象,我们点击方法进去
        Object sharedInstance = this.getSingleton(beanName);
        Object bean;
        if (sharedInstance != null && args == null) {
            if (this.logger.isTraceEnabled()) {
                if (this.isSingletonCurrentlyInCreation(beanName)) {
                    this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
                } else {
                    this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }

            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
        } 
 @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //此处就是获取bean的方法,那么singletonObjects这个对象又是什么东西呢?
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
    //搜索发现,原来这个对象为map ,它把bean名称作为key,实例作为value存储起来了
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    .......

小结:上面Spring初始化时候,把实例都存储到一个map上面了,要取实例的时候,使用bean 的id 到map 获取。

三、结构性型模式

1、代理(Proxy)模式

  • 定义

给某对象提供一个代理对象,通过代理对象可以访问该对象的功能。主要解决通过代理去访问[不能直接访问的对象],(如: 租房中介,你可以直接通过中介去了解房东的房源信息,此时中介就可以称为代理。)

  • 优点
    ①、职责清晰。 (房东收租,代理人处理房务)
    ②、高扩展性。 (房东可以增加处理人,也可以替换处理人)
    ③、智能化。(代理人更专业)

  • 缺点
    ①、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。(先交钱给中介,中介再给钱房东)
    ②、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。(找好的中介需要费时费力)

  • 案例

    JDK动态代理

    1、被代理的类必须实现一个接口
    2、创建代理对象的时候,用JDK代理需要实现InvocationHandler
    3、代理过程在invoke中实现

//1、被代理类必须实现一个接口,此处可以看做找中介的招工启事
public interface LandlordService {
    //交房租
    void pay(String username);
}
//此处是房东类,收房租是房东原来要做的事情
public class Landlord implements LandlordService{

    //收房租
    @Override
    public void pay(String name){
        System.out.println(name);
    }
}
//此处为代理对象——中介
//2、创建代理对象的时候,用JDK代理需要实现InvocationHandler
public class QFang implements InvocationHandler {

    //被代理的对象
    private Object instance;

    public QFang(Object instance) {
        this.instance = instance;
    }

    /****
     * 代理过程
     * proxy:创建的代理的字节码->存在内存中
     * method:用户调用的方法 (pay)
     * args:被调用的方法参数
     */
     //3、代理过程在invoke中实现
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        args[0]="QFang网好房源!带客户"+args[0];
        //执行调用
        Object result = method.invoke(instance,args);
        return result;
    }
}
//被代理的对象的类
public class LandlordServiceImpl implements LandlordService {

    //被代理的方法
    @Override
    public void pay(String username) {
        System.out.println(username+"来交租!");
    }
}
public class QFangTest {

    public static void main(String[] args) {
        //1.指定被创建代理的对象
        LandlordService landlordService = new LandlordServiceImpl();

        //2.创建代理
        LandlordService landlordServiceProxy =
                (LandlordService) Proxy.newProxyInstance(
                        LandlordServiceImpl.class.getClassLoader(),
                        //被创建代理的对象实现的所有接口
                        LandlordServiceImpl.class.getInterfaces(),
                        new QFang(landlordService)
                );

        //3.调用代理中指定的方法
        landlordServiceProxy.pay("王五");
    }
}

处理结果:QFang网好房源!带客户王五来交租!
此处中介的处理方法已经代房东的处理方法进行处理了。

CGLib动态代理

1、代理过程可以实现MethodInterceptor(Callback)接口中的invoke来实现
2、通过Enhancer来创建代理对象

//房东
public class Landlord {

    //收房租
    public void pay(String name){
        System.out.println(name);
    }
}
//代理过程
//1、代理过程可以实现MethodInterceptor(Callback)接口中的invoke来实现 
public class SFang implements MethodInterceptor{

    //被代理的对象
    private Object instance;

    public SFang(Object instance) {
        this.instance = instance;
    }

    /***
     * 代理过程
     * @param o
     * @param method
     * @param objects
     * @param methodProxy : 方法的代理方法 $
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        objects[0] = "SFang房网,好房源,比Qfang好!又带客户"+objects[0];

        //调用
        Object result = method.invoke(instance, objects);
        return result;
    }
}
//被代理的对象的类,注意,此时不在实现接口方法,仅仅提供一个被代理方法
public class LandlordServiceImpl  {

    //被代理的方法
    public void pay(String username) {
        System.out.println(username+"来交租!");
    }
}
public class SFangTest {

    public static void main(String[] args) {
        //1.被代理的对象
        LandlordServiceImpl landlordService = new LandlordServiceImpl();

        //2.创建代理对象
        //1)被代理对象的字节码
        //2)Callback代理过程对象
        LandlordServiceImpl landlordServiceProxy = (LandlordServiceImpl) Enhancer.create(LandlordServiceImpl.class,new SFang(landlordService));

        //3.调用代理对象方法
        landlordServiceProxy.pay("赵六");
    }
}

运行结果:SFang房网,好房源,比Qfang好!又带客户赵六来交租!
此处cglib比jdk动态代理少了抽象接口,是对jdk动态代理的封装。

Spring AOP-动态代理

代码目的:在调用新增方法前,执行日志记录

public interface UserService {
    void add();
}

public class UserServiceImpl {
    //@Override
    public void add(){
        System.out.println("增加用户!");
    }
}
public class Log {

    public void before(JoinPoint jp){
        System.out.println("执行对象:"+jp.getSignature().getName());
        System.out.println("前置通知记录日志!");
    }
}
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--userService-->
    <bean id="userService" class="com.xxx.aop.service.UserServiceImpl"/>
    <!--注册增强(切面)-->
    <bean id="log" class="com.xxx.aop.Log"/>

    <!--AOP配置-->
    <aop:config proxy-target-class="false">
        <!--配置切面(切入点和通知的结合)-->
        <aop:aspect ref="log">
            <!--前置通知-->
            <aop:before method="before" pointcut="execution(* com.xxx.aop.service.*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>
public class AopTest {

    public static void main(String[] args) {
        ApplicationContext act = new ClassPathXmlApplicationContext("spring-aop.xml");
        UserServiceImpl userService = (UserServiceImpl) act.getBean("userService");
        userService.add();
    }
}

执行结果:
执行对象:add
前置通知记录日志!
增加用户!
那么为什么配置切面后,会通知到log方法先执行日志记录呢

  • 原理
  • AOP动态代理源码集中在 DefaultAopProxyFactory 类中、
  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  //此处,<aop:config proxy-target-class="false">跟config.isProxyTargetClass()关联
        if(!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
        //如果配置文件配false,那么此处会执行JDK动态代理
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
            //此处,如果目标(userServiceImpl是否实现接口,实现了用JDK动态代理,没实现即用CGlib代理)
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

小结:从源码看出Spring动态代理是由JDK动态代理及CGlib代理实现。在执行add方法前,使用log类进行动态代理,打印出日志信息。

2、适配器(Adapter)模式

  • 定义

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作

  • 优点
    ①、可以让任何两个没有关联的类一起运行。
    ②、提高了类的复用。
    ③、灵活性好。
  • 缺点
    过多地使用适配器,会让系统非常零乱,不易整体进行把握。(比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接 口的实现,一个系统如果太多出现这种情况,无异于一场灾难。)

适配器案例(打卡前身份验证)

public interface UserCardService {
    void card(String name);
}
public class UserCard implements UserCardService {

    @Override
    public void card(String name){
        System.out.println(name+"打卡成功!");
    }
}
public class UserInfoBefore implements MethodBeforeAdvice {

    //MethodBeforeAdvice:前置通知
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(args[0]+"身份识别通过!");
    }
}
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置文件中配置-->
    <bean id="userCard" class="com.xxx.adapter.UserCard"/>
    <bean id="userInfoBefore" class="com.xxx.adapter.UserInfoBefore"/>

    <!--创建代理(也是一种适配模式)-->
    <bean id="proxyBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定给谁创建代理-->
        <property name="target" ref="userCard" />
        <!--被代理的对象实现了哪些接口-->
        <property name="interfaces" value="com.xxx.adapter.UserCardService" />
        <!--指定拦截器-->
        <property name="interceptorNames">
            <list>
                <value>userInfoBefore</value>
            </list>
        </property>
    </bean>
public class AdapterTest {

    public static void main(String[] args) {
        ApplicationContext act = new ClassPathXmlApplicationContext("spring-adapter.xml");
        UserCardService userCardService = (UserCardService) act.getBean("proxyBean");
        userCardService.card("王五");
    }
}

输出结果:
王五身份识别通过!
王五打卡成功!
此处,proxyBean代理UserCard ,但是没有覆盖原先方法,所以打印出“王五打卡成功!”,而使用UserInfoBefore 作为拦截器,before方法先于代理方法执行,所以首先打印“王五身份识别通过!”。注意代理也是适配模式的一种,前置通知也是是适配器模式之一。那么Spring中是怎么通过前置通知的拦截器执行代码的呢?

此处打断点发现,这个代理对象属于JdkDynamicAopProxy对象,我们直接进该类中寻找invo方法(代理执行的方法)

@Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        Class var8;
        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                Boolean var18 = this.equals(args[0]);
                return var18;
            }

            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                Integer var17 = this.hashCode();
                return var17;
            }

            if (method.getDeclaringClass() != DecoratingProxy.class) {
                Object retVal;
                if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                    return retVal;
                }

                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
                //此处进行拦截器获取,打断点可知此处会获取前置通知的拦截器
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                if (chain.isEmpty()) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                } else {
                    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    //但是为什么会先处理before呢?进去执行方法
                    retVal = invocation.proceed();
                }
   @Nullable
    public Object proceed() throws Throwable {
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.invokeJoinpoint();
        } else {
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
                return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            } else {
            //此处拦截器拦截,invoke
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    }
public Object invoke(MethodInvocation mi) throws Throwable {
//此处先调用beforce方法
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //真实方法调用
        return mi.proceed();
    }

小结:spring代理对象代理执行的时候,获取拦截器,进入拦截器的方法,先执行before方法,再执行真实方法。

3、享元(Flyweight)模式

  • 定义:运用共享技术来有效地支持大量细粒度对象的复用。
  • 享元模式和单利的区别:
    单例是对象只能自己创建自己,整个应用中只有1个对象; 享元模式根据需要共享,不限制被谁创建(有可能有多个对象实例)
  • 优点:特定环境下,相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。(如:同线程某个对象只用相同的实例)
  • 缺点:为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。

享元模式案例(用户下单)

案例业务描述
享元模式下业务处理

tips:此处ThreadLocal为线程对象,每个用户访问对应的线程均不一样,访问的ThreadLocal也不一样;且每次调用完毕后会进行销毁

//建立一个抽象对象,让共享对象继承(提高拓展性)
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public abstract class Session {
    private String username;
    private String name;
    private String sex;
    private String role;
    private Integer level;

    //额外操作
    public abstract void handler();
}
//共享对象
public class SessionShare extends Session {

    //方便创建实例
    public SessionShare(String username, String name, String sex, String role, Integer level) {
        super(username, name, sex, role, level);
    }
    @Override
    public void handler() {
        System.out.println("共享信息!");
    }
}
//存储用户信息(存储共享对象)
@Component
public class SessionThreadLocal {

    //1.创建一个ThreadLocal实现存储线程下共享的对象,此处可当做一个map,key为当前线程
    private static ThreadLocal<Session> sessions = new ThreadLocal<Session>();
    //2.添加共享对象
    public void add(Session session){
        sessions.set(session);
    }
    //3.获取共享对象
    public Session get(){
        return sessions.get();
    }
    //4.移除共享对象
    public void remove(){
        sessions.remove();
    }
}
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

    @Autowired
    private ThreadSession threadSession;

    /****
     * 在拦截器中,将用户会话存储到ThreadLocal中
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            //获取令牌
            String authorization = request.getHeader("token");
            //解析令牌
            if(!StringUtils.isEmpty(authorization)){
                Map<String, Object> tokenMap = JwtTokenUtil.parseToken(authorization);
                //封装用户身份信息,存储到ThreadLocal中,供当前线程共享使用
                //1.封装需要共享的信息[Session]
                //2.创建一个对象继承封装信息,每次共享该对象 (不需要共享,则可以创建另外一个对象继承它)
                //3.创建共享管理对象,实现共享信息的增加、获取、移除功能
                threadSession.add(new SessionShare(
                      tokenMap.get("username").toString(),
                      tokenMap.get("name").toString(),
                      tokenMap.get("sex").toString(),
                      tokenMap.get("role").toString(),
                      Integer.valueOf(tokenMap.get("level").toString())
                ));
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //输出令牌校验失败
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().print("身份校验失败!");
        response.getWriter().close();
        return false;
    }

    /**
     * 移除会话信息
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //移除用户信息
        threadSession.remove();
    }
}
/***
     * 添加订单
     * @param order
     */
    @Override
    public int add(Order order) {
        //order.setUsername("wangwu");
        order.setPaymoney(100); //结算价格
        order.setMoney(100);  //订单价格

        //获取用户信息  此处把共享对象赋值出来
        order.setUsername(threadSession.get().getUsername());
        //装饰者模式嵌套运算计算价格
        fullMoneySum.setMoneySum(orderMoneySum);
        vipMoneySum.setMoneySum(fullMoneySum);
        //价格计算
        vipMoneySum.sum(order);

        //修改库存
        int mCount = itemService.modify(order.getNum(), order.getItemId());
        //添加订单
        int addCount = orderDao.add(order);
        return addCount;
    }

4、装饰者(Decorator)模式

  • 定义:动态的向一个现有的对象添加新的功能,同时又不改变其结构
  • 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
  • 缺点:多层装饰比较复杂

装饰者案例(奶茶制作)

//1.首先新建接口
public interface Tea {
    //制作奶茶
    void making();
}
//2.实现接口
public class IceTea implements Tea {
    @Override
    public void making() {
        System.out.println("①添加冰块!制作成了一杯冰水!");
    }
}
//3.指定被修饰对象,指定被修饰方法
public abstract class DecoratorTea implements Tea {

    //被装饰的对象【IceTea】 在指定对象上进行功能扩展
    private Tea tea;

    public void setTea(Tea tea) {
        this.tea = tea;
    }

    //扩展方法
    @Override
    public void making(){
        //调用被装饰的对象的指定方法
        tea.making();
    }
}
//4.装饰类1继承实现类DecoratorTea ,为什么不是继承iceTea呢,因为DecoratorTea为基类,iceTea是被装饰者;如果直接继承iceTea就会是死代码,只能装饰iceTea
public class PeachTea extends DecoratorTea {
    @Override
    public void making() {
        super.making();
        //扩展
        addPeach();
    }
    //扩展
    public void addPeach(){
        System.out.println("②制作桃子奶茶!");
    }
}
//5.装饰类2继承实现类DecoratorTea
public class CocoTea extends DecoratorTea {

    @Override
    public void making() {
        //执行被装饰的对象指定方法
        super.making();
        //调用扩展
        addCoco();
    }

    //扩展
    public void addCoco(){
        System.out.println("③添加椰汁奶茶!");
    }
}
//5.把iceTea 装到peachTea 里面,再把peachTea 装到cocoTea ,然后执行cocoTea ,就像套娃一样,执行也会一个套一个执行
public class DecoratorTest {

    public static void main(String[] args) {
        //创建被装饰的对象
        Tea iceTea = new IceTea();

        //创建装饰对象
        DecoratorTea peachTea = new PeachTea();
        //设置被装饰的对象
        peachTea.setTea(iceTea);
        //执行操作
        //peachTea.making();

        //创建一个装饰对象
        DecoratorTea cocoTea = new CocoTea();
        //设置被装饰的对象   peachTea--->Tea???
        cocoTea.setTea(peachTea);

        //执行
        cocoTea.making();
    }
}

四、行为型模式

1、观察者(Observer)模式

  • 定义
    对象之间存在一对多或者一对一依赖,每当一个对象改变状态,依赖他的对象就会收到通知,做出反应。(mq就是一种观察者模式,发布者发布,订阅者获取信息,订阅就能收到。)
  • 优点
    ①、观察者与被观察这都是抽象耦合
    ②、建立一套触发机制
  • 缺点
    ①、如果被观察者有很多直接、间接的观察者,通知所有的观察者是很耗时间
    ②、如果观察者与观察目标有依赖循环,观察目标会触发他们之间进行循环

观察者案例 (Spring观察者模式)

ApplicationContext 事件机制是观察者设计模式的实现,通过 ApplicationEvent 类(ContextRefreshedEvent容器刷新事件)和ApplicationListener 接口,可以实现 ApplicationContext 事件处理。

public class ApplicationContextListener implements ApplicationListener<ContextRefreshedEvent> {
 	@Override 
 	public void onApplicationEvent(ContextRefreshedEvent event) { 	 
 		System.out.println("ContextRefreshedEvent事件,容器内对象发生变更"); 
	 } 
 }

首先,我们实现ApplicationListener对ContextRefreshedEvent事件进行监听

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置文件中配置-->
    <bean class="com.itheima.spring.ApplicationContextListener"></bean>

</beans>

在配置文件中配置监听器bean

public class SpringApplicationTest {

    /*****
     * 1.创建ApplicationContext
     * 2.执行相关事件ApplicationEvent:容器初始化完成
     * 3.监听事件执行信息ApplicationListener
     */
    public static void main(String[] args) {
        //1.创建ApplicationContext,会立即加载Bean,加载完毕就相当于执行了事件
        ApplicationContext act = new ClassPathXmlApplicationContext("spring-event.xml");
    }
}

执行main方法,加载容器发现控制台上打印"ContextRefreshedEvent事件,容器内对象发生变更"

  • 原理
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
    public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
    protected final Log logger;
    private String id;
    private String displayName;
    @Nullable
    private ApplicationContext parent;
    @Nullable
    private ConfigurableEnvironment environment;
    private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors;
    private long startupDate;
    private final AtomicBoolean active;
    private final AtomicBoolean closed;
    private final Object startupShutdownMonitor;
    @Nullable
    private Thread shutdownHook;
    private ResourcePatternResolver resourcePatternResolver;
    @Nullable
    private LifecycleProcessor lifecycleProcessor;
    @Nullable
    private MessageSource messageSource;
    @Nullable
    private ApplicationEventMulticaster applicationEventMulticaster;
    //此处会初始化监听器
    private final Set<ApplicationListener<?>> applicationListeners;

追踪源码ClassPathXmlApplicationContext,发现在其父类AbstractApplicationContext 中会初始化一个ApplicationListener的集合,那么容器对象是怎么触发ApplicationEvent呢?


    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");
        Object applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent)event;
        } else {
            applicationEvent = new PayloadApplicationEvent(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
            }
        }

        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        } else {
		//注意:此处事件为空则新增ApplicationEvent
            this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
        }

        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
            } else {
                this.parent.publishEvent(event);
            }
        }

    }
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
        Iterator var5 = this.getApplicationListeners(event, type).iterator();

        while(var5.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var5.next();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
            //点击方法进来,发现事件对监听器进行调用唤醒
                this.invokeListener(listener, event);
            }
        }
    }
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = this.getErrorHandler();
        if (errorHandler != null) {
            try {
                this.doInvokeListener(listener, event);
            } catch (Throwable var5) {
                errorHandler.handleError(var5);
            }
        } else {
        //此时,把ApplicationListener、ApplicationEvent扔进去进行执行
            this.doInvokeListener(listener, event);
        }
    }
 private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
        //执行了ApplicationListener的onApplicationEvent方法
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
                throw var6;
            }

            Log logger = LogFactory.getLog(this.getClass());
            if (logger.isTraceEnabled()) {
                logger.trace("Non-matching event type for listener: " + listener, var6);
            }
        }
    }

小结如图

2、策略(strategy)模式

  • 定义:策略模式是对算法的包装,把使用算法的责任和算法本身分隔开,委派给不同的对象管理。策略模式通常把一系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。(被调用者+工厂+策略执行类)
  • 优点:
    1、算法可以自由切换。
    2、避免使用多重条件判断。
    3、扩展性良好。
  • 缺点:
    1、策略类会增多。
    2、所有策略类都需要对外暴露。

策略模式案例(商品vip等级价格计算)

购买商品时候,会有不同等级的vip,他们所享受的优惠力度是不一样的,所以需要多个策略针对不同的vip进行价格运算

//1.创建策略接口
public interface Strategy {

    /***
     * 金额计算
     */
    Integer money(Integer money);
}
//2.创建vip1价格策略
@Component(value = "vip1")
public class Vip1 implements Strategy {

    /***
     * Vip1
     * @param money
     * @return
     */
    @Override
    public Integer money(Integer money) {
        return money;
    }
}
//3.策略2
@Component(value = "vip2")
public class Vip2 implements Strategy {

    /***
     * Vip1
     * @param money
     * @return
     */
    @Override
    public Integer money(Integer money) {
        return money-5;
    }
}

//4.策略3
@Component(value = "vip3")
public class Vip3 implements Strategy {

    /***
     * Vip1
     * @param money
     * @return
     */
    @Override
    public Integer money(Integer money) {
        return 1;
    }
}
//5.配置策略映射,省去if判断
#等级和策略实例的映射关系(此处是yml配置文件配置)
strategy:
  vipmap:
    1: vip1
    2: vip2
    3: vip3

//6.创建策略工厂
@ConfigurationProperties(prefix = "strategy")
@Component(value = "strategyFactory")
public class StrategyFactory {

    /***
     * 1.定义一个容器对象(Map)->存储所有策略对象ApplicationContext
     *  vip1 --->new Vip1()
     *  vip2 --->new Vip2()
     *  vip3 --->new Vip3()
     */
    @Autowired
    private ApplicationContext act;

    private Map<Integer,String> vipmap;//把策略装到map中,如: 1: vip1

    //2.提供一个方法用于根据等级获取指定策略
    public Strategy get(Integer level){
        //建立等级和容器中策略的映射关系
        //1 - vip1
        //2 - vip2
        //3 - vip3
        String id = vipmap.get(level);
        return act.getBean(id,Strategy.class);
    }

    public void setVipmap(Map<Integer, String> vipmap) {
        this.vipmap = vipmap;
    }
}

调用时候直接strategyFactory.get(1).money;就可以获取到具体的价格了,参数1代表vip等级。

3、状态(state)模式

  • 定义:对有状态的对象,把复杂的“判断逻辑"提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
  • 优点:
    1、封装了转换规则。
    2、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    3、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 缺点:
    1、状态模式的使用必然会增加系统类和对象的个数。
    2、状态模式对"开闭原则""的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

状态模式案例(订单状态修改)

在电商案例中,订单状态每次发生变更,都要执行不同的操作,这里正好可以使用状态模式。当订单完成支付的时候,我们需要立即通知商家发货,当订单执行取消的时候,我们需要执行库存回滚,如果订单已支付,还需要执行退款操作,无论是通知商家发货还是执行库存回滚,都是有订单状态决定,因此这里可以使用状态模式来实现。

案例概况

//1.创建订单对象
public class Context {

    private State state;

    public Context() {
        this.state = null;
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }
}

//2..定义订单状态接口
public interface State {
    void doAction(Context context);
}
//3.此处定义成功状态处理,处理后将状态改为付款成功状态
public class StartState implements State{

    @Override
    public void doAction(Context context) {
        System.out.println("付款成功!准备发货");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "Start State";
    }
}
//4.此处作为退款处理,处理后将状态改为退款成功状态
public class StopState implements State{
    @Override
    public void doAction(Context context) {
        System.out.println("退款成功!准备退款");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "Stop State";
    }
}
//5.此处模拟订单付款、退款业务
public class StateTest {

    public static void main(String[] args) {
        Context context = new Context();

        StartState startState = new StartState();
        startState.doAction(context);
        System.out.println(context.getState().toString());

        StopState stopState = new StopState();
        stopState.doAction(context);
        System.out.println(context.getState().toString());
    }
}

执行结果:
Start State
Stop State

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值