前言
前几天,一位 读者面阿里 被问到一个问题:Spring 框架用到了哪些设计模式?,答的不是很好,于是打算写篇文章讲讲这个!
最近几天肝文章都到凌晨 3,4 点,因为最近工作的事件比拟多,只能早晨写文章(为了放弃日更,也是拼了),也收到一些读者的关怀,谢谢大家~
感觉不错,心愿点赞,再看,转发反对一下,谢谢
代理模式
所谓代理,是指它与被代理对象实现了雷同的接口,客户端必须通过代理能力与被代理的指标类进行交互,而代理个别在交互的过程中(交互前后),进行某些特定的解决,比方在调用这个办法前做前置解决,调用这个办法后做后置解决。
代理又分为动态代理和动静代理两种形式,Spring 的 AOP 采纳的是动静代理的形式
Spring 通过动静代理对类进行办法级别的切面加强,动静生成指标对象的代理类,并在代理类的办法中设置拦截器,通过执行拦截器中的逻辑加强了代理办法的性能,从而实现 AOP。
策略模式
咱们后面讲到,Spring AOP 是通过动静代理来实现的。
具体到代码实现,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(单例),Spring Bean 默认是单例模式。
那 Spring 是如何实现单例模式的呢?
答案是通过单例注册表的形式,具体来说就是应用了 HashMap。简化代码如下:
public class DefaultSingletonBeanRegistry {
// 应用了线程平安容器 ConcurrentHashMap,保留各种单实例对象
private final Map singletonObjects = new ConcurrentHashMap;
protected Object getSingleton(String beanName) {
// 先到 HashMap 中拿 Object
Object singletonObject = singletonObjects.get(beanName);
// 如果没拿到通过反射创立一个对象实例,并增加到 HashMap 中
if (singletonObject == null) {
singletonObjects.put(beanName,
Class.forName(beanName).newInstance());
}
// 返回对象实例
return singletonObjects.get(beanName);
}
}
下面的代码逻辑比拟清晰,先到 HashMap 去拿单实例对象,没拿到就创立一个增加到 HashMap。
简略工厂模式
有这样一个场景:
当 A 对象须要调用 B 对象的办法时,咱们须要在 A 中 new 一个 B 的实例,它的毛病是一旦需要发生变化,比方须要应用 C 类来代替 B 时,就要改写 A 类的办法。
如果利用中有 100 个类以的形式耦合了 B,那改起来就吃力了。
应用简略工厂模式:
简略工厂模式又叫动态工厂办法,其实质是由一个工厂类依据传入的参数,动静决定应该创立哪一个产品类。
其中 Spring 中的 BeanFactory 就是简略工厂模式的体现,BeanFactory 是 Spring IOC 容器中的一个外围接口,它的定义如下:
咱们能够通过它的具体实现类(比方 ClassPathXmlApplicationContext)来获取 Bean:
BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
从下面代码能够看到,使用者不须要本人来 new 对象,而是通过工厂类的办法 getBean 来获取对象实例,这是典型的简略工厂模式,只不过 Spring 是用反射机制来创立 Bean 的。
工厂办法模式
在简略工厂中,由工厂类进行所有的逻辑判断、实例创立;如果不想在工厂类中进行判断,能够为不同的产品提供不同的工厂,不同的工厂生产不同的产品,每一个工厂都只对应一个相应的对象,这就是工厂办法模式。
Spring 中的 FactoryBean 就是这种思维的体现,FactoryBean 能够了解为工厂 Bean,先来看看它的定义:
咱们定义一个类 FlyFishFactoryBean 来实现 FactoryBean 接口,次要是在 getObject 办法里 new 一个 FlyFish 对象。这样咱们通过 getBean(id) 取得的是该工厂所产生的 FlyFish 的实例,而不是 FlyFishFactoryBean 自身的实例,像上面这样:
BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
观察者模式
Spring 中实现的观察者模式蕴含三局部:Event 事件(相当于音讯)、Listener 监听者(相当于观察者)、Publisher 发送者(相当于被观察者)
咱们通过一个例子来看下 Spring 提供的观察者模式是怎么应用的
// Event 事件
public class DemoEvent extends ApplicationEvent {
private String message;
public DemoEvent(Object source, String message) {super(source);
}
public String getMessage() {return this.message;}
}
// Listener 监听者
@Component
public class DemoListener implements ApplicationListener {
@Override
public void onApplicationEvent(DemoEvent demoEvent) {String message = demoEvent.getMessage();
System.out.println(message);
}
}
// Publisher 发送者
@Component
public class DemoPublisher {
@Autowired
private ApplicationContext applicationContext;
public void publishEvent(DemoEvent demoEvent) {this.applicationContext.publishEvent(demoEvent);
}
}
从代码中,咱们能够看出,次要蕴含三局部工作:
- 定义一个继承 ApplicationEvent 的事件(DemoEvent);
- 定义一个实现了 ApplicationListener 的监听器(DemoListener);
- 定义一个发送者(DemoPublisher),发送者调用 ApplicationContext 来发送事件音讯。
在 Spring 的实现中,观察者注册到了哪里呢?又是如何注册的呢?
Spring 把观察者注册到了 ApplicationContext 对象中。
实际上,具体到源码来说,ApplicationContext 只是一个接口,具体的代码实现蕴含在它的实现类 AbstractApplicationContext 中。我把跟观察者模式相干的代码,如下。你只须要关注它是如何发送事件和注册监听者就好。
从下面的代码中,咱们发现,真正的音讯发送,实际上是通过 ApplicationEventMulticaster 这个类来实现的。
上面这个类的源码我只摘抄了最要害的一部分,也就是 multicastEvent()这个音讯发送函数,它通过线程池,反对异步非阻塞、同步阻塞这两种类型的观察者模式。
借助 Spring 提供的观察者模式的骨架代码,如果咱们要在 Spring 下实现某个事件的发送和监听,只须要做很少的工作,定义事件、定义监听器、往 ApplicationContext 中发送事件就能够了,剩下的工作都由 Spring 框架来实现。
实际上,这也体现了 Spring 框架的扩展性,也就是在不须要批改任何代码的状况下,扩大新的事件和监听。
模板模式
咱们常常在面试中被问到的一个问题:
请你说下 Spring Bean 的创立过程蕴含哪些次要的步骤。
这其中就波及模板模式。它也体现了 Spring 的扩展性。利用模板模式,Spring 能让用户定制 Bean 的创立过程。
上面是 Spring Bean 的整个生命周期,一张图,清晰明了:
如果你认真看过源码会发现,实际上,这里的模板模式的实现,并不是规范的抽象类的实现形式,而是有点相似 Callback 回调的实现形式,也就是将要执行的函数封装成对象(比方,初始化办法封装成 InitializingBean 对象),传递给模板(BeanFactory)来执行。
观察者模式和模板模式,这两种模式可能帮忙咱们创立扩大点,让框架的使用者在不批改源码的状况下,基于扩大点定制化框架性能。
适配器模式
在 Spring MVC 中,定义一个 Controller 最罕用的形式是,通过 @Controller 注解来标记某个类是 Controller 类,通过 @RequesMapping 注解来标记函数对应的 URL
不过,咱们还能够通过让类实现 Controller 接口或者 Servlet 接口,来定义一个 Controller。
针对这三种定义形式,我写了三段示例代码,如下所示:
// 办法一:通过 @Controller、@RequestMapping 来定义
@Controller
public class DemoController {@RequestMapping("/FlyFish")
public ModelAndView getEmployeeName() {ModelAndView model = new ModelAndView("FlyFish");
model.addObject("message", "FlyFish");
return model;
}
}
// 办法二:实现 Controller 接口 + xml 配置文件: 配置 DemoController 与 URL 的对应关系
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {ModelAndView model = new ModelAndView("FlyFish");
model.addObject("message", "FlyFish");
return model;
}
}
// 办法三:实现 Servlet 接口 + xml 配置文件: 配置 DemoController 类与 URL 的对应关系
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("Hello World.");
}
}
在利用启动的时候,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()函数就能够了
最初
感觉有播种,心愿帮忙点赞,转发下哈,谢谢,谢谢