本文转载自微信公众号“月亮与飞鱼”,作者日常加油站。转载本文请联系月版飞语公众号。代理模式中所谓的代理,就是实现了与代理对象相同的接口。客户端必须通过代理才能与代理目标类进行交互,而代理一般在交互过程中(交互前后)执行一些特定的任务。处理,比如调用该方法前的预处理,调用该方法后的后处理。Proxy分为静态代理和动态代理。Spring的AOP采用了动态代理的方式。Spring使用动态代理增强类的方法级方面,动态生成目标对象的代理类,并在代理类的方法中设置拦截器,增强代理方法的功能通过执行拦截器中的逻辑,从而实现AOP。策略模式前面我们提到,SpringAOP是通过动态代理实现的。具体到代码实现上,Spring支持两种动态代理实现方式,一种是JDK提供的动态代理实现方式,一种是Cglib提供的动态代理实现方式。Spring会在运行时动态选择不同的动态代理实现。这个应用场景其实就是策略模式的一个典型应用场景。我们只需要定义一个策略接口,让不同的策略类实现这个策略接口即可。对应Spring源码,AopProxy是一个策略接口,JdkDynamicAopProxy和CglibAopProxy是实现AopProxy接口的两个策略类。其中,AopProxy接口的定义如下:在策略模式下,策略的创建一般是通过工厂方法实现的。对应Spring源码,AopProxyFactory是一个工厂类接口,DefaultAopProxyFactory是创建AopProxy对象的默认工厂类。源码如下:策略模式的一个典型应用场景是通过环境变量、状态值、计算结果等动态决定使用哪种策略。对应Spring源码,我们可以参考的代码实现刚才给出的DefaultAopProxyFactory类中的createAopProxy()函数。其中,第10行代码是动态选择哪种策略的判断条件。装饰者模式我们知道缓存一般是和数据库结合使用的。如果写缓存成功,但是数据库事务回滚,缓存中就会有脏数据。为了解决这个问题,我们需要把缓存写操作和数据库写操作放在同一个事务中,要么成功要么都失败。为了实现这样的功能,Spring使用了装饰器模式。TransactionAwareCacheDecorator增加了对事务的支持,在事务提交和回滚时分别处理Cache数据。TransactionAwareCacheDecorator实现了Cache接口,将所有操作委托给targetCache,并在写操作中加入了事务功能。这是装饰器模式的典型应用场景和代码实现。单例模式单例模式是指在整个系统运行过程中只允许产生一个实例的类。在Spring中,Bean的定义有两种模式:Prototype(多实例)和Singleton(单例)。SpringBean默认是单例模式。那么Spring是如何实现单例模式的呢?答案是通过单例注册,具体就是HashMap的使用。简化后的代码如下:publicclassDefaultSingletonBeanRegistry{//使用线程安全容器ConcurrentHashMap保存各种单实例对象privatefinalMapsingletonObjects=newConcurrentHashMap;protectedObjectgetSingleton(StringbeanName){//首先获取HashMaps中的ObjectObjectsingletonObject=singletonObjects.get(beanName);//if(singletonObject==null){singletonObjects.put(beanName,Class.forName(beanName).newInstance());}//返回对象实例returnssingletonObjects.get(beanName);}}上面代码的逻辑比较清晰,先去HashMap中获取单例对象,没有获取到则创建一个添加到HashMap中。简单工厂模式有这样一个场景:当对象A需要调用对象B的方法时,我们需要在A中创建B的新实例。它的缺点是一旦需求发生变化,比如类C需要要代替B使用,需要重写类A的方法。如果应用程序中有100个类以独特的方式耦合B,那么将很难更改。使用简单工厂模式:简单工厂模式也称为静态工厂方法。本质上,一个工厂类是根据传入的参数动态决定创建哪个产品类。其中,Spring中的BeanFactory就是简单工厂模式的体现。BeanFactory是SpringIOC容器中的一个核心接口。它的定义如下:我们可以通过它的具体实现类(比如ClassPathXmlApplicationContext)获取Bean:BeanFactorybf=newClassPathXmlApplicationContext("spring.xml");FlyFishflyFishBean=(FlyFish)bf.getBean("flyfishBean");从上面的代码可以看出,用户不需要自己创建新的对象,而是通过工厂类的getBean方法获取对象实例,这是一个典型的简单工厂模式,只不过Spring使用了反射机制来创建豆子。工厂方法模式,在简单工厂中,工厂类进行所有的逻辑判断和实例创建;如果不想在工厂类中判断,可以为不同的产品提供不同的工厂,不同的工厂生产不同的产品。每个工厂都只对应一个对应的对象,这就是工厂方法模式。Spring中的FactoryBean就是这种思想的体现。FactoryBean可以理解为工厂bean。我们看一下它的定义:我们定义了一个类FlyFishFactoryBean来实现FactoryBean接口,主要是在getObject方法中新建一个FlyFish对象。这样,我们通过getBean(id)得到的是工厂生产出来的FlyFish的实例,而不是FlyFishFactoryBean本身的实例,如下:BeanFactorybf=newClassPathXmlApplicationContext("spring.xml");FlyFishflyFishBean=(FlyFish)bf。getBean("飞鱼豆");观察者模式Spring中实现的观察者模式由三部分组成:Event事件(相当于消息)、Listener监听器(相当于观察者)、Publisher发送者(相当于被观察对象)。我们通过一个例子来看看如何使用Spring提供的观察者模式Listener监听器@ComponentpublicclassDemoListenerimplementsApplicationListener{@OverridepublicvoidonApplicationEvent(DemoEventdemoEvent){Stringmessage=demoEvent.getMessage();System.out.println(message);}}//Publisher发送者@ComponentpublicclassDemoPublisher{@AutowiredprivateApplicationContextapplicationContext;publicvoidpublishEvent(DemoEventdemoEvent).this.applicationContextpublishEvent(demoEvent);}}从代码中可以看出主要包括三部分工作:定义一个继承自ApplicationEvent的事件(DemoEvent);定义一个实现ApplicationListener的监听器(DemoListener);定义一个发送者(DemoPublisher),发送者调用ApplicationContext发送事件消息。在Spring的实现中,观察者是在哪里注册的呢?它是如何注册的?Spring向ApplicationContext对象注册观察者。其实就源码而言,ApplicationContext只是一个接口,具体的代码实现包含在它的实现类AbstractApplicationContext中。我把与观察者模式相关的代码如下。你只需要关注它是如何发送事件和注册监听器的。从上面的代码中,我们发现真正的消息发送其实是通过ApplicationEventMulticaster类来完成的。下面这个类的源码我只摘录了最关键的部分,就是消息发送函数multicastEvent(),它支持异步非阻塞和通过线程池同步阻塞两种观察者模式。借助Spring提供的观察者模式的骨架代码,如果我们想在Spring下发送和监听一个事件,只需要做一点工作,定义事件,定义监听器,发送事件即可到ApplicationContext。其余的工作由Spring框架完成。其实这也体现了Spring框架的可扩展性,即可以在不修改任何代码的情况下扩展新的事件和监听器。模板模式我们在面试中经常被问到一个问题:请告诉我SpringBean的创建过程有哪些主要步骤。这就涉及到模板模式。也体现了Spring的扩展性。Spring使用模板方式,使用户能够自定义Bean的创建过程。下面是SpringBean的整个生命周期,一张图,清晰明了:仔细看源码就会发现,其实这里模板模式的实现并不是标准的抽象类实现,但有点类似于Callback,回调的实现是将要执行的函数封装成一个对象(比如初始化方法封装成一个InitializingBean对象),传递给模板(BeanFactory)执行。观察者模式和模板模式,这两种模式可以帮助我们创建扩展点,让框架用户可以在不修改源码的情况下,基于扩展点自定义框架功能。Adapter模式在SpringMVC中,定义Controller最常见的方式是通过@Controller注解将一个类标记为Controller类,通过@RequesMapping注解标记函数对应的URL。但是,我们也可以通过让类或Servlet接口定义一个Controller来实现Controller接口。对于这三种定义方法,我写了三段示例代码,如下://方法一:通过@Controller和@RequestMapping("FlyFish");model.addObject("message","FlyFish");returnmodel;}}//方法二:实现Controller接口+xml配置文件:配置DemoController与URL的对应关系)throwsException{ModelAndViewmodel=newModelAndView("FlyFish");model.addObject("message","FlyFish");returnmodel;}}//方法三:实现Servlet接口+xml配置文件:配置DemoController类与URL的对应关系关系publicclassDemoServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{this.doPost(req,resp);}@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{resp.getWriter().write("HelloWorld.");}}应用启动时,Spring容器会加载这些Controller类,并解析出URL对应的处理函数,封装成Handler对象,并存储在HandlerMapping对象中,当有请求到来时,DispatcherServlet从HanderMapping中查找请求URL对应的Handler,然后调用并执行Handler对应的函数代码,最后将执行结果返回给客户端。但是,不同方式定义的Controller的函数定义(函数名、入参、返回值等)并不统一。DispatcherServlet调用service()方法,DispatcherServlet需要根据不同类型的Controller调用不同的函数。Spring采用了适配器模式,我们将不同方式定义的Controller类中的函数适配为一个统一的函数定义。下面详细看下Spring的代码实现。Spring定义了一个统一的接口HandlerAdapter,为每个Controller定义了对应的适配器类。这些适配器类包括:AnnotationMethodHandlerAdapter、SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter等。在DispatcherServlet类中,我们不需要区别对待不同的Controller对象,统一调用HandlerAdapter的handle()函数即可
