Spring的9种设计模式(一)

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

——郑愁予《错误》

前言

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
	@Override
	public 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;
			// 遍历bean
			for (Iterator i = root.elementIterator("bean"); i.hasNext();) {
				foo = (Element) i.next();
				// 获取bean的属性id和class
				Attribute 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.测试:

        /**
	 * 工厂方法——实例工厂方法
	 */
	@Test
	public 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> {
    @Override
    public User getObject() throws Exception {
        return new User("cxq","123456",1);
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public 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.测试:

       @Test
	public 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;  
}  
}  

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

@Nullable
    protected 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 {
    @Override
    public void request() {
        // TODO Auto-generated method stub
        super.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();
    @Override
    public void request() {
        // TODO Auto-generated method stub
        adaptee.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;
    }
    @Override
    public 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
     */
    @Override
    public 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 类

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值