我打江南走过
那等在季节里的容颜如莲花的开落
东风不来,三月的柳絮不飞
你的心如小小的寂寞的城
恰若青石的街道向晚
跫音不响,三月的春帷不揭
你的心是小小的窗扉紧掩
我达达的马蹄是美丽的错误
我不是归人,是个过客……

——郑愁予《错误》

前言

spring作为经典的框架,用到了很多设计模式

一、简单工厂

又叫静态工厂(StaticFactory)模式,但不属于23种设计模式之一。

简单工厂模式实质是一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

spring的BeanFactory就是简单工厂模式的体现,根据传入一个唯一标识来获得bean对象,但是否在传入参数后创建还是传入参数前创建要根据具体情况来定。如下配置,就是在HelloItxxz类种创建一个itxxBean。

<beans><bean id="singletonBean" class="com.itxxz.HelloItxxz"><constructor-arg><value>Hello! 这是singletonBean!value></constructor-arg></ bean><bean id="itxxzBean" class="com.itxxz.HelloItxxz"singleton="false"><constructor-arg><value>Hello! 这是itxxzBean! value></constructor-arg></bean>
</beans>

1.1 BeanFactory自己实现简版

首先写一个接口类,BeanFactory.class:

public interface BeanFactory {Object getBean(String beanName);
}

下面是xml配置文件 springtest.xml:

<?xml version="1.0" encoding="UTF-8"?><beans><bean id="usertest" class="com.qwx.myProject.pojo.User"><property name="username" value="lxlx" /><property name="passWord" value="111" /><property name="age" value="11"/></bean>
</beans>

下面是bean定义的class文件 User类:

@Data
@NoArgsConstructor                 //无参构造
@AllArgsConstructor                //有参构造
@ToString
public class User {private String username;private String passWord;private int age;
}

接下来是实现类 ConcreteBeanFactory:

public class ConcreteBeanFactory implements BeanFactory{private Map<String, Object> beanDefinitionMap = new HashMap<String, Object>();//简单工厂模式的特征是:一个工厂中可以生产多种不同的产品,这里的Bean其实是没有区分不同的bean,是可以通过get返回不同的bean@Overridepublic Object getBean(String beanName) {return beanDefinitionMap.get(beanName);}//增加一个init的操作方法//从xml配置文件中进行解析读取public void init(String xmlPath){SAXReader saxReader = new SAXReader();File file = new File(xmlPath);try {Document document = saxReader.read(file);Element root = document.getRootElement();Element foo;// 遍历beanfor (Iterator i = root.elementIterator("bean"); i.hasNext();) {foo = (Element) i.next();// 获取bean的属性id和classAttribute id = foo.attribute("id");Attribute cls = foo.attribute("class");// 利用Java反射机制,通过class的名称获取Class对象Class<?> bean = Class.forName(cls.getText());// 获取对应class的信息java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);// 获取其属性描述java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();// 设置值的方法Method mSet = null;// 创建一个对象Object obj = bean.newInstance();// 遍历该bean的property属性for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {Element foo2 = (Element) ite.next();// 获取该property的name属性Attribute name = foo2.attribute("name");String value = null;Object typeValue = null;//获取value值value = foo2.attributeValue("value");for (int k = 0; k < pd.length; k++) {if (pd[k].getName().equalsIgnoreCase(name.getText())) {mSet = pd[k].getWriteMethod();//设置值这里,需要根据类型给value做类型转换//properties中包含了properType的项,因为当前程序中就只有String和Int,先处理这样的类型Type mType = pd[k].getPropertyType();if (mType.getTypeName().equals("java.lang.String")){typeValue = String.valueOf(value);}else if(mType.getTypeName().equals("int")){typeValue = Integer.parseInt(value);}mSet.invoke(obj, typeValue);}}}// 将对象放入beanMap中,其中key为id值,value为对象beanDefinitionMap.put(id.getText(), obj);}}catch (Exception e){System.out.println(e.toString());}}
}

下面是测试类:

public class SimpleFactoryClient {public static void main(String[] args){ConcreteBeanFactory absbf = new ConcreteBeanFactory();absbf.init("D:\\WorkSpace\\IdeaProjects\\myProject\\src\\main\\resources\\springtest.xml");User user = (User)absbf.getBean("usertest");System.out.println("User类的bean有没有创建成功:" + user);System.out.println("属性值:" + user.getUsername() + "," + user.getPassWord() + "," + user.getAge());}
}

二、工厂方法(Factory Method

通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。

一般情况下,应用程序有自己的工厂对象来创建bean.如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。

以下设计到xml,在spring项目中均可在applicationContext.xml直接写。

2.1 工厂方法自己实现简版

2.1.1 静态工厂方法创建bean

所谓静态工厂方式就是指Factory类不本身不需要实例化, 这个Factory类中提供了1个静态方法来生成bean对象

下面是例子:

1.bean类User同上;

2.然后我们再定义1个工厂类UserStaticFactory:

public class UserStaticFactory {private static Map<String, User> map = new HashMap<String,User>();static{map.put("qwx", new User("qwx","123456",10));map.put("cxq", new User("cxq","123456",9));map.put("xf", new User("xf","123456",10));}public static User getUser(String name){return map.get(name);}
}

里面定义了1个静态的bean 容器map. 然后提供1个静态方法根据User 的name 来获取容器里的User对象。

3.xml配置——static-factory-bean.xml

<?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 id="qwx" class="springDesignMode.factoryMethod.UserStaticFactory" factory-method="getUser"><constructor-arg value="qwx"></constructor-arg></bean><bean id="cxq" class="springDesignMode.factoryMethod.UserStaticFactory" factory-method="getUser"><constructor-arg value="cxq"></constructor-arg></bean>
</beans>

可以见到, 利用静态工厂方法定义的bean item种, class属性不在是bean的全类名, 而是静态工厂的全类名, 而且还需要指定工厂里的getBean静态方法名字(getUser)和参数(qwx)。

4.客户端代码

public class StaticFactoryMethodClient {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("static-factory-bean.xml");User u = (User) ctx.getBean("qwx");System.out.println(u);User u2 = (User) ctx.getBean("cxq");System.out.println(u2);}
}

小结:

由上面的例子, 静态工厂方法方式是非常适用于作为1个bean容器(集合的), 只不过吧bean集合定义在工厂类里面而不是bean config file里面。 
缺点也比较明显, 把数据写在class里面而不是配置文件中违反了我们程序猿的常识和spring的初衷。当然优点就是令到令人恶心的bean config file更加简洁啦。

2.1.2 实例工厂创建bean

1.实例工厂:

public class UserInstaceFactory {public User getUser(String name) {System.out.println("实例工厂造人...");User user = new User();user.setUsername(name);user.setPassWord("123456");user.setAge(1);return user;}
}

2.

在xml中注册实例工厂:

  • factory-bean=“InstanceFactory” :指定使用哪个工厂实例。
  • factory-method=“getUser”:使用哪个工厂方法。
<?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 id="instaceFactory" class="springDesignMode.factoryMethod.UserInstaceFactory"></bean><bean id="user1" factory-bean="instaceFactory" factory-method="getUser"><constructor-arg value="qwx"></constructor-arg></bean>
</beans>

3.测试:

        /*** 工厂方法——实例工厂方法*/@Testpublic void testInstaceFacMethod(){ApplicationContext ctx = new ClassPathXmlApplicationContext("instance-factory-bean.xml");User user1 =(User) ctx.getBean("user1");System.out.println(user1);}

2.1.3 FactoryBean工厂创建Bean

  • ioc容器启动的时候不会创建实例(无论是单实例还是多实例),获取的时候才创建实例对象。

当某些对象的实例话过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器中的时候,就可以实现org.spring-framework.beans.factory.FactoryBean接口,给出自己的对象实例化代码。当然实现自定义工厂也是可以的。但是FactoryBean是Spring的标准

Spring中的FactoryBean就是典型的工厂方法模式。如下图:

1.实现FactoryBean接口:

public class UserFactoryBean implements FactoryBean<User> {@Overridepublic User getObject() throws Exception {return new User("cxq","123456",1);}@Overridepublic Class<?> getObjectType() {return User.class;}@Overridepublic boolean isSingleton() {return true;}
}

2.在xml中注册:

<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="userFactoryBean" class="springDesignMode.factoryMethod.UserFactoryBean"></bean>
</beans>

3.测试:

       @Testpublic void factoryBeanTest(){ApplicationContext ctx = new ClassPathXmlApplicationContext("user-factory-bean.xml");System.out.println("容器启动完成");Object bean = ctx.getBean("userFactoryBean");System.out.println("打印属性:" + bean);}

三、单例模式(Singleton

常用单例模式写法:饿汉式、懒汉式、注册式、序列化。

3.1懒汉式与饿汉式

1.饿汉式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。

Public class Singleton1{  Private static final Singleton1 instance=new Singleton1();  //私有的默认构造函数  Public Singleton1(){}  //静态工厂方法  Public static Singleton1 getInstance(){  Return instance;  }  }

这是比较常见的写法,在类加载的时候就完成了实例化,避免了多线程的同步问题。当然缺点也是有的,因为类加载时就实例化了,没有达到Lazy Loading (懒加载) 的效果,如果该实例没被使用,内存就浪费了。

2.懒汉式:在类加载时不初始化,等到第一次被使用时才初始化。

一步到位,双重检查锁

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

这是懒汉式中最简单的一种写法,只有在方法第一次被访问时才会实例化,达到了懒加载的效果。

注意:

这种写法用了两个if判断,也就是Double-Check,并且同步的不是方法,而是代码块,效率较高。为什么要做两次判断呢?这是为了线程安全考虑,还是那个场景,对象还没实例化,两个线程A和B同时访问静态方法并同时运行到第一个if判断语句,这时线程A先进入同步代码块中实例化对象,结束之后线程B也进入同步代码块,如果没有第二个if判断语句,那么线程B也同样会执行实例化对象的操作了。

3.2 spring设置单例与设置懒汉式

spring缺省值就是单例,scope为空或者填singleton:

<bean id="dvdTypeDAO" class="com.machome.hibernate.impl.DvdTypeDAOImpl" scope="singleton" />

spring若想设置多例模式,scope填prototype:

<bean id="dvdTypeDAO" class="com.machome.hibernate.impl.DvdTypeDAOImpl" scope="prototype" />

spring设置懒汉模式:

<beans default-lazy-init="true"></beans>

3.3 spring怎么实现单例

我们可以使用另外一种特殊化的单例模式,它被称为单例注册表。

3.3.1 什么是单例注册表呢?

  1. 使用map实现注册表;
  2. 使用protect修饰构造方法;
public class RegSingleton {private static HashMap registry=new HashMap();//静态块,在类被加载时自动执行static{RegSingleton rs=new RegSingleton();registry.put(rs.getClass().getName(),rs);}//受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点protected RegSingleton(){}//静态工厂方法,返回此类的唯一实例public static RegSingleton getInstance(String name){if(name==null){name="RegSingleton";}if(registry.get(name)==null){try{registry.put(name,Class.forName(name).newInstance());}catch(Exception ex){ex.printStackTrace();}}return (RegSingleton)registry.get(name);}
}

3.3.2 Spring单例模式源码 
Spring框架对单例的支持是采用单例注册表的方式进行实现的

1.获取Bean源码如下

public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{  /** * 充当了Bean实例的缓存,实现方式和单例注册表相同 */  private final Map singletonCache=new HashMap();  public Object getBean(String name)throws BeansException{  return getBean(name,null,null);  }
...  public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{  //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)  String beanName=transformedBeanName(name);  Object bean=null;  //手工检测单例注册表  Object sharedInstance=null;  //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高  synchronized(this.singletonCache){  sharedInstance=this.singletonCache.get(beanName);  }  if(sharedInstance!=null){  ...  //返回合适的缓存Bean实例  bean=getObjectForSharedInstance(name,sharedInstance);  }else{  ...  //取得Bean的定义  RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);  ...  //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关  //`<bean id="date" class="java.util.Date" scope="singleton"/>`  //如果是单例,做如下处理  if(mergedBeanDefinition.isSingleton()){  synchronized(this.singletonCache){  //再次检测单例注册表  sharedInstance=this.singletonCache.get(beanName);  if(sharedInstance==null){  ...  try {  //真正创建Bean实例  sharedInstance=createBean(beanName,mergedBeanDefinition,args);  //向单例注册表注册Bean实例  addSingleton(beanName,sharedInstance);  }catch (Exception ex) {  ...  }finally{  ...  }  }  }  bean=getObjectForSharedInstance(name,sharedInstance);  }  //如果是非单例,即prototpye,每次都要新创建一个Bean实例  //<bean id="date" class="java.util.Date" scope="prototype"/>  else{  bean=createBean(beanName,mergedBeanDefinition,args);  }
}
...  return bean;
}
}  

继续分析单例的获取流程:

@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {Map var4 = this.singletonObjects;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);}}}}return singletonObject;}

这里涉及到三个单例容器:
singletonObjects
earlySingletonObjects
singletonFactories

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);private final Map<String, Object> earlySingletonObjects = new HashMap(16);

2.三级缓存
通过分析源码:
单例的获取顺利是singletonObjects ——》earlySingletonObjects ——》singletonFactories 这样的三级层次。

我们发现,在singletonObjects 中获取bean的时候,没有使用synchronized关键字,而在singletonFactories 和earlySingletonObjects 中的操作都是在synchronized代码块中完成的,正好和他们各自的数据类型对应,singletonObjects 使用的使用ConcurrentHashMap线程安全,而singletonFactories 和earlySingletonObjects 使用的是HashMap,线程不安全。

从字面意思来说:singletonObjects指单例对象的cache,singletonFactories指单例对象工厂的cache,earlySingletonObjects指提前曝光的单例对象的cache。以上三个cache构成了三级缓存,Spring就用这三级缓存巧妙的解决了循环依赖问题。

另外,源码中这三个容器的初始容积也做了配置,分别是256,16,16,也是值得分析和借鉴的。

spring单例下循环依赖的处理,三级缓存

 3.单例的注册
DefaultSingletonBeanRegistry类中:

 public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {Assert.notNull(beanName, "Bean name must not be null");Assert.notNull(singletonObject, "Singleton object must not be null");Map var3 = this.singletonObjects;synchronized(this.singletonObjects) {Object oldObject = this.singletonObjects.get(beanName);if (oldObject != null) {throw new IllegalStateException("Could not register object [" + singletonObject + "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");} else {this.addSingleton(beanName, singletonObject);}}}
protected void addSingleton(String beanName, Object singletonObject) {Map var3 = this.singletonObjects;synchronized(this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}

向一级缓存中加入单例对象,同时,移除二级缓存,三级缓存中的单例对象。
并向注册登记表registeredSingletons中,记录单例的名称beanName。
由于容器对象都是map对象,所以能自动保存通一个beanName保存的对象唯一。

这里也可以知道了容器是什么?
spring中的存放bean的容器就是ConcurrentHashMap

四、适配器(Adapter

4.1定义

将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够一起工作。

自己的话:就是从该接口视角出发,我需要扩展功能去适配不同的需求。

和装饰模式的区别:装饰模式属于第三人称的视角,也就是上帝视角!

4.2 适配器概览

目的:对现有的接口进行转换以符合新的需求。

动机:通过转换或组合,间接复用。

主要用途和场景:

该模式并不是在设计开发阶段考虑的,主要用在想要修改一个已经存在的接口,或者组合若干关联对象的时候。

  1. 想用一个已经存在的类,但其接口不符合需求;
  2. 想创建一个可以复用的类,该类可以与其他不相关的类协同工作;
  3. 想使用一些已经存在的子类,但是不能对每一个都进行子类化以匹配它们的接口(仅适用于对象Adapter)。对象适配器可以适配他的父类接口。

4.2.1 原理

类适配器

原理:通过类继承实现适配,继承Target的接口,继承Adaptee的实现

对象适配器

原理:通过类对象组合实现适配

Target:

 定义Client真正需要使用的接口。

Adaptee:

 其中定义了一个已经存在的接口,也是我们需要进行适配的接口。

Adapter:

 对Adaptee和Target的接口进行适配,保证对target中接口的调用可以间接转换为对Adaptee中接口进行调用。

4.2.2 类适配器实现

定义目标接口类:Target

public interface Target {void request();
}

被适配的类:Adaptee

public class Adaptee {public void adapteeRequest() {System.out.println("adapteeRequest method of Adaptee! ");}
}

适配类Adapter,继承Target的接口request,同时继承Adaptee的实现adapteeRequest

public class Adapter extends Adaptee implements Target {@Overridepublic void request() {// TODO Auto-generated method stubsuper.adapteeRequest();}
}

演示:

public class Demo {public static void main(String [] args) {Target target = new Adapter();target.request(); // result: adapteeRequest method of Adaptee! }
}

4.2.3 对象适配器的实现

从上面两张UML图中可以清楚的看出两者的区别,对象中Adapter不在继承Adaptee,而是将Adaptee作为一个数据成员组合到类定义中,从而实现对其接口的访问。

public class Adapter implements Target {private Adaptee adaptee = new Adaptee();@Overridepublic void request() {// TODO Auto-generated method stubadaptee.adapteeRequest();}
}

4.3 Spring中适配器模式的典型应用

在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。

Advice(通知)的类型有:BeforeAdvice、AfterReturningAdvice、ThreowSadvice的。

在每个类型Advice(通知)都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。

Spring需要将每个Advice(通知)都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对Advice进行转换。下面我们看看具体的代码。

4.3.1 Spring Aop源码

参考文章:Spring之AOP适配器模式

MethodBeforeAdvice类:Adaptee


public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method method, Object[] args, Object target) throws Throwable;
}

AdvisorAdapter类接口: Target


public interface AdvisorAdapter {boolean supportsAdvice(Advice advice);MethodInterceptor getInterceptor(Advisor advisor);
}

MethodBeforeAdviceAdapter类,Adapter


class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {public boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);}public MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();return new MethodBeforeAdviceInterceptor(advice);}
}

DefaultAdvisorAdapterRegistry类,Client

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);/*** Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.*/public DefaultAdvisorAdapterRegistry() {//这里注册了适配器registerAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());}public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {if (adviceObject instanceof Advisor) {return (Advisor) adviceObject;}if (!(adviceObject instanceof Advice)) {throw new UnknownAdviceTypeException(adviceObject);}Advice advice = (Advice) adviceObject;if (advice instanceof MethodInterceptor) {// So well-known it doesn't even need an adapter.return new DefaultPointcutAdvisor(advice);}for (AdvisorAdapter adapter : this.adapters) {// Check that it is supported.if (adapter.supportsAdvice(advice)) {//这里调用了适配器的方法return new DefaultPointcutAdvisor(advice);}}throw new UnknownAdviceTypeException(advice);}public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);Advice advice = advisor.getAdvice();if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {//这里调用了适配器的方法interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[interceptors.size()]);}public void registerAdvisorAdapter(AdvisorAdapter adapter) {this.adapters.add(adapter);}}

4.4 适配器模式的优缺点

  优点:

  • 能提高类的透明性和复用性,现有的类会被复用但不需要改变。
  • 目标类和适配器类解耦,可以提高程序的扩展性。
  • 在很多业务场景中符合开闭原则。

  缺点:

  • 在适配器代码编写过程中需要进行全面考虑,可能会增加系统复杂度。
  • 增加代码阅读难度,过多使用适配器会使系统代码变得凌乱。

五、装饰者(也叫包装器,Decorator

装饰者模式(Decorator Pattern)是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的方案(扩展原有对象的功能),属于结构型模式。装饰者模式适用于以下场景:

  • 扩展一个类的功能或给一个类添加附加职责。
  • 动态给一个对象天机功能,这些功能可以再动态的撤销。

装饰者图解:

在装饰模式中的角色有:

  • 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

  • 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

  • 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

  • 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。

5.1 装饰者模式和适配器模式对比

装饰者模式和适配器模式都是包装模式(Wrapper Pattern),装饰者模式是一种特殊的代理模式,二者对比如下:

  装饰者模式 适配器模式
形式 是一种非常特别的适配器 没有层级关系,装饰者模式有层级关系
定义 装饰者和被装饰着实现同一接口,主要目的是为了扩展后依旧保留旧的oop关系 适配器和被适配这没有必然的关系,通常采用继承或代理的形式进行包装
关系 满足is-a关系 满足has-a关系
功能 注重覆盖、扩展 注重兼容、转换
设计 前置考虑 后置考虑

5.2 装饰者模式的实例演示

5.2参考文章:spring常用模式--------装饰者模式

实例介绍:
在原有的登录的接口的情况下,动态的增加了发送短信的功能

1.抽象构件角色 定义登录的接口

/*** @Project: spring* @description:     抽象构件角色   登录的接口业务* @author: sunkang* @create: 2018-09-05 20:51* @ModificationHistory who      when       What**/
public interface ISiginSerevice {ResultMsg login(String username, String password);
}

2.具体构件角色 ,登录的具体实现

/***
* @Description:
* @Param:   具体构件角色    登录的具体实现
* @return:
* @Author: sunkang
* @Date: 2018/9/5
*/
public class SiginService implements ISiginSerevice {/*** 登录的方法* @param username* @param password* @return*/public ResultMsg login(String username,String password){return  new ResultMsg("200","登录成功",new Object());}
}

3.装饰角色,拓展了发送短信的功能

/*** @Project: spring* @description:    装饰角色    拓展了发送短信的功能* @author: sunkang* @create: 2018-09-05 21:41* @ModificationHistory who      when       What**/
public interface ISiginForThirdService extends ISiginSerevice {/*** 原有登录的方法* @param username* @param password* @return*/ResultMsg login(String username, String password);/*** 发送短信* @param msg* @return*/ResultMsg sendShortMessage(String msg);
}

4.具体的装饰角色,原有的登录功能动态增加了发送短信的功能

/*** @Project: spring* @description:  具体的装饰角色    原有的登录功能增加了发送短信的功能* @author: sunkang* @create: 2018-09-06 09:14* @ModificationHistory who      when       What**/
public class SiginForThirdService implements  ISiginForThirdService {private ISiginSerevice  siginSerevice ;public SiginForThirdService(ISiginSerevice siginSerevice) {this.siginSerevice = siginSerevice;}@Overridepublic ResultMsg login(String username, String password) {ResultMsg msg =    siginSerevice.login(username,password);//注册成功发送短信的功能if(msg.getCode().equals("200")){System.out.println("用户登录成功");msg = sendShortMessage(username);}return msg;}/*** 发送短信的功能   这个是装饰器 增加的额外的功能,在登录成功之后发送短信通知* @param username* @return*/@Overridepublic ResultMsg sendShortMessage(String username) {System.out.println("恭喜用户:"+username+"发送短信成功");return new ResultMsg("200","发送短信成功",new Object());}
}

5.测试案例

/*** @Project: spring* @description:  装饰者测试* @author: sunkang* @create: 2018-09-06 09:23* @ModificationHistory who      when       What**/
public class SiginTest {public static void main(String[] args) {ISiginSerevice siginSerevice = new SiginService();ISiginSerevice  siginForThirdService = new SiginForThirdService(siginSerevice);siginForThirdService.login("sunkang","4324");}
}

6.测试结果

5.3 装饰者模式在源码中的应用

5.3.1 TransactionAwareCacheDecorator类

这个类主要是用来处理事务缓存的

public class TransactionAwareCacheDecorator implements Cache {private final Cache targetCache;/*** Create a new TransactionAwareCache for the given target Cache.* @param targetCache the target Cache to decorate*/public TransactionAwareCacheDecorator(Cache targetCache) {Assert.notNull(targetCache, "Target Cache must not be null");this.targetCache = targetCache;}
}

5.3.2  HttpHeadResponseDecorator 类

public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator {public HttpHeadResponseDecorator(ServerHttpResponse delegate) {super(delegate);}
}

5.3.3 MyBatis 中的一段处理缓存的设计 org.apache.ibatis.cache.Cache 类

Spring的9种设计模式(一)相关推荐

  1. 9种设计模式在Spring中的运用,一定要非常熟练!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:iCoding91 https://blog.csdn.ne ...

  2. Spring 中经典的 9 种设计模式,打死也要记住啊!

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客:点 ...

  3. controller调用另一个controller中的方法 获取返回值_必须掌握!你知道 Spring 中运用的 9 种设计模式吗 ?...

    Spring中涉及的设计模式总结,在面试中也会经常问道 Spring 中设计模式的问题.本文以实现方式.实质.实现原理的结构简单介绍 Sping 中应用的 9 种设计模型,具体详细的刨析会在后面的文章 ...

  4. Spring主要用到两种设计模式

    Spring主要用到两种设计模式 1.工厂模式 Spring容器就是实例化和管理全部Bean的工厂. 工厂模式可以将Java对象的调用者从被调用者的实现逻辑中分离出来. 调用者只关心被调用者必须满足的 ...

  5. spring中用到的9种设计模式

    spring中用到了9种设计模式,学习spring的源码以及设计模式,可以提高开发人员软件设计以及开发的水平,写出更加优雅的代码. 文章目录 简单工厂(非23种设计模式中的一种) 工厂方法 单例模式 ...

  6. 九种设计模式在Spring中的应用

    Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实现方式: BeanFactory.Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获 ...

  7. 面试官:Spring 用了哪些设计模式?说三种即可 = =

    面试官:Spring 用了哪些设计模式?说三种即可 = = 转载:https://mp.weixin.qq.com/s/LGD54XaK4RmhZHxtU0BADg 转载理由:还可以

  8. 详解spring用到的九种设计模式

    转载请注明出处,文章首发于:http://www.itxxz.com/a/javashili/tuozhan/2014/0601/7.html 设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬 ...

  9. Spring/SpringBoot系列之Spring中涉及的9种设计模式【七】

    1. 总览 Spring中涉及的设计模式: 简单工厂(非23种设计模式中的一种) 工厂方法 单例模式 适配器模式 装饰器模式 代理模式 观察者模式 策略模式 模版方法模式 2. 详细介绍 2.1 简单 ...

  10. 初探Java设计模式5:一文了解Spring涉及到的9种设计模式

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

最新文章

  1. 操作系统级虚拟化概述
  2. io调度maple调度程序_调度Java应用程序中的主体
  3. 36岁,程序员,为公司工作8年,昨天HR说公司不准备续约
  4. BeanPropertyRowMapper
  5. php读写文件要加锁
  6. php接口前端,php 接口与前端数据交互实现示例代码
  7. 算法高级(21)-如何通过IP地址进行定位?
  8. 教育培训行业的SOP
  9. 如何通过简历推销自己
  10. 电脑怎么用计算机打开指令,命令提示符怎么用 命令提示符快捷键打开使用技巧详解...
  11. 易保全:览契约文化,传契约精神
  12. 带宽和网速之间的关系
  13. MT7621原厂openwrt SDK使能串口2和串口3
  14. 双色球彩票生成之一用户彩票号码随机生成
  15. HashMap中Entry以及Hash值的理解
  16. 如果恢复计算机系统时间吗,经常重置、恢复系统对电脑的影响大不大?
  17. VisualGDB系列3:安装VisualGDB
  18. 【朝花夕拾】四大组件之(一)Broadcast篇
  19. Maven 环境配置
  20. 个人学习笔记(备份)

热门文章

  1. echarts 不支持 手机 浏览器_中国北斗卫星导航系统真的来了!获国产手机力挺:但iPhone却不支持...
  2. 容器技术Docker K8s 35 容器服务ACK基础与进阶-应用与发布管理
  3. 容器技术Docker K8s 5 容器技术在阿里巴巴落地历程
  4. 批量梯度下降算法BGD
  5. 概率图模型(PGM)学习笔记(二)贝叶斯网络-语义学与因子分解
  6. android studio for android learning (七) Android Log类全解
  7. 【2019南京ICPC网络赛 D】Robots【DAG上的随机游走】
  8. 实数系的完备性的含义
  9. hydra怎么构建字典_在Pytorch中构建流数据集
  10. win10树莓派改ip_用树莓派制作温湿度服务器