Spring涉及的设计模式很多,不下于十几种,我们就总结性的罗列一下,限于篇幅,不可能对每种设计模式都详细的讲解,只能说一些简单的概述
适配器模式在Spring中应用
在SpringMVC中,对于定义Controller,可以有多种实现方式
1,通过@Controller来标记某个类是Controller类,并且通过@RequestMapping注解来标记函数对应的URL,进行实现
2.实现Controller接口或者Servlet接口,来定义一个Controller
常见的方法如下:
// 方法一:通过@Controller、@RequestMapping来定义
@Controller public class DemoController { @RequestMapping(“/employname”) public ModelAndView getEmployeeName() { ModelAndView model = new ModelAndView(“Greeting”); model.addObject(“message”, “Dinesh”); 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(“Greeting”); model.addObject(“message”, “Dinesh Madhwal”); 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,其函数的定义是不统一的,如上面示例代码,不同的类中,定义的函数不一致,Controller注解中的函数千奇百怪,使用Controller接口,定义的是handleRequest,最后函数定义的是service(),DispatcherServlet需要调用不同的类型的Controller,调用不同的函数
Handler handler = handlerMapping.get(URL);
if (handler instanceof Controller) { ((Controller)handler).handleRequest(…); } else if (handler instanceof Servlet) { ((Servlet)handler).service(…); } else if (hanlder 对应通过注解来定义的Controller) { 反射调用方法… } |
上面可以看出,我们实现中存在着多种if-else分支判断,
于是我们可以使用适配器模式对代码进行改造,让其满足开闭原则,更好的支持扩展,适配器的一个作用就是统一多个类的接口设计,将不同方式定义在Controller类中的函数,适配为同一个函数定义,这样我们就能在DispatcherServlet类代码中,移除了if-else判断,
我们统一定义了接口 HandlerAdapter,并且对每种Controller定义了对应的适配器类
public interface HandlerAdapter {
boolean supports(Object var1); ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception; long getLastModified(HttpServletRequest var1, Object var2); } // 对应实现Controller接口的Controller public class SimpleControllerHandlerAdapter implements HandlerAdapter { public SimpleControllerHandlerAdapter() { } public boolean supports(Object handler) { return handler instanceof Controller; } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller)handler).handleRequest(request, response); } public long getLastModified(HttpServletRequest request, Object handler) { return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L; } } // 对应实现Servlet接口的Controller public class SimpleServletHandlerAdapter implements HandlerAdapter { public SimpleServletHandlerAdapter() { } public boolean supports(Object handler) { return handler instanceof Servlet; } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet)handler).service(request, response); return null; } public long getLastModified(HttpServletRequest request, Object handler) { return -1L; } } //AnnotationMethodHandlerAdapter对应通过注解实现的Controller, //代码太多了,我就不贴在这里了 |
在DispathcerServlet中,我们不需要区分对待不同的Controller对象了,统一调用HandlerAdapter的handle()函数就可以了,按照这个思路实现的代码,就不需要if-else了
策略模式在Spring汇总的应用
SpringAOP是通过动态代理实现,Spring提供了两种动态代理的实现方式,一种是JDK提供的动态代理实现方法,一种是Cglib提供的动态代理实现方式
前者需要被代理的类有抽象的接口定义,后者不需要,针对不同的被代理类,Spring会动态选择不同的代理实现方式,这个应用场景就是策略模式的典型应用场景
策略模式分为三个部分,策略定义,创建和使用,我们看一下,这三个部分如何体现在Spring源码红的
策略模式汇总,策略定义很简单,定义一个策略接口,让不同的策略实现一个接口,对应到Spring源码,JdkDynamicAopProxy,CglibAopProxy是两个实现AopProxy接口的策略类,AopProxy接口的定义如下
public interface AopProxy {
Object getProxy(); Object getProxy(ClassLoader var1); } |
策略模式,策略创建一般通过工厂模式实现,对应到Spring中,AopProxyFactroy是一个工厂类接口,DefaultAopProxyFactory是一个默认的工厂类,用来创建AopProxy对象
public interface AopProxyFactory {
AopProxy createAopProxy(AdvisedSupport var1) throws AopConfigException; } public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { public DefaultAopProxyFactory() { } public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { 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 { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } } //用来判断用哪个动态代理实现方式 private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]); } } |
通过环境变量,状态值,算出使用哪一个策略,对应到Spring源码中,可以看到给出的DefaultAopProxtFactory类中的createAopProxt()函数的代码实现
组合模式在Spring中的应用组合
组合模式就好比一个树,包含叶子节点和中间节点,SpringCache就提供了一套抽象的Cache接口,我们统一不同缓存实现的不同的访问方式,Spring中针对不同缓存实现的不同缓存访问类,
比如EnCacheCache,GuavaCache,NoOpCache,RedisCache,JCacheCache,ConcurrentMapCache
CaffeineCacahe
共同利用一个Cache接口
public interface Cache {
String getName(); Object getNativeCache(); Cache.ValueWrapper get(Object var1); <T> T get(Object var1, Class<T> var2); <T> T get(Object var1, Callable<T> var2); void put(Object var1, Object var2); Cache.ValueWrapper putIfAbsent(Object var1, Object var2); void evict(Object var1); void clear(); public static class ValueRetrievalException extends RuntimeException { private final Object key; public ValueRetrievalException(Object key, Callable<?> loader, Throwable ex) { super(String.format(“Value for key ‘%s’ could not be loaded using ‘%s'”, key, loader), ex); this.key = key; } public Object getKey() { return this.key; } } public interface ValueWrapper { Object get(); } } |
实际的开发中,一个项目可能会用到不同的缓存,既用到Google Guava的缓存,也用到了Redis缓存,比如同一个缓存实例,可以根据业务的不同,分割成多个小的逻辑缓存单元
Spring还提供了缓存管理功能,不过,包含的功能很简单,有两部分,一个是根据缓存的名字,获取Cache对象,一个是获取管理器管理的所有缓存的名字列表
public interface CacheManager {
Cache getCache(String var1); Collection<String> getCacheNames(); } |
在组合模式的叶子节点和中间节点中,对应到Spring源码中,EhCacheManager,SimpleCacheManager,NoOpCacheManager,RedisCacheManager可以当做叶子节点,CompositeCacheManager,也可以是具体的管理器,比如EhCacheManager,RedisManger等
比如中间层管理器,CompositeCacheManager的代码贴到了下面,可以结合着讲解一下,其中getCache(),getCacheNames()两个函数实现都用到了递归,这既是树形结构的体现
public class CompositeCacheManager implements CacheManager, InitializingBean {
private final List<CacheManager> cacheManagers = new ArrayList(); private boolean fallbackToNoOpCache = false; public CompositeCacheManager() { } public CompositeCacheManager(CacheManager… cacheManagers) { this.setCacheManagers(Arrays.asList(cacheManagers)); } public void setCacheManagers(Collection<CacheManager> cacheManagers) { this.cacheManagers.addAll(cacheManagers); } public void setFallbackToNoOpCache(boolean fallbackToNoOpCache) { this.fallbackToNoOpCache = fallbackToNoOpCache; } public void afterPropertiesSet() { if (this.fallbackToNoOpCache) { this.cacheManagers.add(new NoOpCacheManager()); } } public Cache getCache(String name) { Iterator var2 = this.cacheManagers.iterator(); Cache cache; do { if (!var2.hasNext()) { return null; } CacheManager cacheManager = (CacheManager)var2.next(); cache = cacheManager.getCache(name); } while(cache == null); return cache; } public Collection<String> getCacheNames() { Set<String> names = new LinkedHashSet(); Iterator var2 = this.cacheManagers.iterator(); while(var2.hasNext()) { CacheManager manager = (CacheManager)var2.next(); names.addAll(manager.getCacheNames()); } return Collections.unmodifiableSet(names); } } |
装饰器模式在Spring中的应用,
缓存一般是配合数据库来使用的,如果写缓存成功,但是数据库的事务回滚了,那么缓存中就会有脏数据,为了解决这个问题,我们需要将缓存的写操作和数据库的写操作必须放在一个事务中,要么都成功,要么都失败
Spring用到了装饰器模式,TransactionAwareCacheDecorator增加了对事务的支持,在事务提交回滚的时候分别对Cache的数据进行处理
TransactionAwareCacheDecorator实现了Cache接口,并将操作都委托给了targetCache实现,对写操作实现了事务功能
public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache; public TransactionAwareCacheDecorator(Cache targetCache) { Assert.notNull(targetCache, “Target Cache must not be null”); this.targetCache = targetCache; } public Cache getTargetCache() { return this.targetCache; } public String getName() { return this.targetCache.getName(); } public Object getNativeCache() { return this.targetCache.getNativeCache(); } public ValueWrapper get(Object key) { return this.targetCache.get(key); } public <T> T get(Object key, Class<T> type) { return this.targetCache.get(key, type); } public <T> T get(Object key, Callable<T> valueLoader) { return this.targetCache.get(key, valueLoader); } public void put(final Object key, final Object value) { if (TransactionSynchronizationManager.isSynchronizationActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { public void afterCommit() { TransactionAwareCacheDecorator.this.targetCache.put(key, value); } }); } else { this.targetCache.put(key, value); } } public ValueWrapper putIfAbsent(Object key, Object value) { return this.targetCache.putIfAbsent(key, value); } public void evict(final Object key) { if (TransactionSynchronizationManager.isSynchronizationActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { public void afterCommit() { TransactionAwareCacheDecorator.this.targetCache.evict(key); } }); } else { this.targetCache.evict(key); } } public void clear() { if (TransactionSynchronizationManager.isSynchronizationActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { public void afterCommit() { TransactionAwareCacheDecorator.this.targetCache.clear(); } }); } else { this.targetCache.clear(); } } } |
工厂模式在Spring中的应用
在Spring中,工厂模式最经典的莫过于IOC容器,对应的Spring源码是BeanFactory和ApplicationContext相关类,
Spring中,创建Bean的方式有很多,比如纯粹的构造函数,或者无参构造加上setter
public class Student {
private long id; private String name; public Student(long id, String name) { this.id = id; this.name = name; } public void setId(long id) { this.id = id; } public void setName(String name) { this.name = name; } } // 使用构造函数来创建Bean <bean id=”student” class=”com.xzg.cd.Student”> <constructor-arg name=”id” value=”1″/> <constructor-arg name=”name” value=”wangzheng”/> </bean> // 使用无参构造函数+setter方法来创建Bean <bean id=”student” class=”com.xzg.cd.Student”> <property name=”id” value=”1″></property> <property name=”name” value=”wangzheng”></property> </bean> |
我们或者使用工厂模式来创建一个Bean,用这种方式创建Bean的话,可以如下
public class StudentFactory {
private static Map<Long, Student> students = new HashMap<>(); static{ map.put(1, new Student(1,”wang”)); map.put(2, new Student(2,”zheng”)); map.put(3, new Student(3,”xzg”)); } public static Student getStudent(long id){ return students.get(id); } } // 通过工厂方法getStudent(2)来创建BeanId=”zheng””的Bean <bean id=”zheng” class=”com.xzg.cd.StudentFactory” factory-method=”getStudent”> <constructor-arg value=”2″></constructor-arg> </bean> |
其他模式在Spring中的应用
大部分是我们讲过的,其中还有我们上面没有提到的,简单的介绍一下
SPEL,Spring Expression Language,按照Spring中常用的来编写配置的表达式语言,定义了一系列的语法规则,我们按照这些语法规则来编写表达式,Spring可以解析出表达式的含义,这就是常见的解释器模式的应用场景
IOC还提供了单例模式,单元测试不友好的时候,应对策略就是通过IOC容器来管理对象,通过IOC容器来实现对象的唯一性控制,这样的单例利用IOC容器的唯一来控制唯一性
Spring还用到了观察者模式,模板模式,职责链模式,代理模式
Spring中,只要后缀带有Tempalte的类,基本上都是模板类,大部分利用了Callback回调,比如jadbTemplate,ReidsTemplate
拦截器使用了职责链模式
AOP使用了代理模式
本章重点:
Spring中涉及的设计模式,适配器模式 策略模式 组合模式 装饰器模式 工厂模式 单例模式 解释器模式 观察者模式 模板模式 职责链模式 代理模式
课后讨论
如何让Spring支持Builder模式创建Bean呢?
对象的初始化有两种实现方式。一种是在类中自定义一个初始化函数,并且通过配置文件,显式地告知 Spring,哪个函数是初始化函数