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@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 什么是单例注册表呢?
- 使用map实现注册表;
- 使用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 适配器概览
目的:对现有的接口进行转换以符合新的需求。
动机:通过转换或组合,间接复用。
主要用途和场景:
该模式并不是在设计开发阶段考虑的,主要用在想要修改一个已经存在的接口,或者组合若干关联对象的时候。
- 想用一个已经存在的类,但其接口不符合需求;
- 想创建一个可以复用的类,该类可以与其他不相关的类协同工作;
- 想使用一些已经存在的子类,但是不能对每一个都进行子类化以匹配它们的接口(仅适用于对象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种设计模式(一)相关推荐
- 9种设计模式在Spring中的运用,一定要非常熟练!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:iCoding91 https://blog.csdn.ne ...
- Spring 中经典的 9 种设计模式,打死也要记住啊!
点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客:点 ...
- controller调用另一个controller中的方法 获取返回值_必须掌握!你知道 Spring 中运用的 9 种设计模式吗 ?...
Spring中涉及的设计模式总结,在面试中也会经常问道 Spring 中设计模式的问题.本文以实现方式.实质.实现原理的结构简单介绍 Sping 中应用的 9 种设计模型,具体详细的刨析会在后面的文章 ...
- Spring主要用到两种设计模式
Spring主要用到两种设计模式 1.工厂模式 Spring容器就是实例化和管理全部Bean的工厂. 工厂模式可以将Java对象的调用者从被调用者的实现逻辑中分离出来. 调用者只关心被调用者必须满足的 ...
- spring中用到的9种设计模式
spring中用到了9种设计模式,学习spring的源码以及设计模式,可以提高开发人员软件设计以及开发的水平,写出更加优雅的代码. 文章目录 简单工厂(非23种设计模式中的一种) 工厂方法 单例模式 ...
- 九种设计模式在Spring中的应用
Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实现方式: BeanFactory.Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获 ...
- 面试官:Spring 用了哪些设计模式?说三种即可 = =
面试官:Spring 用了哪些设计模式?说三种即可 = = 转载:https://mp.weixin.qq.com/s/LGD54XaK4RmhZHxtU0BADg 转载理由:还可以
- 详解spring用到的九种设计模式
转载请注明出处,文章首发于:http://www.itxxz.com/a/javashili/tuozhan/2014/0601/7.html 设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬 ...
- Spring/SpringBoot系列之Spring中涉及的9种设计模式【七】
1. 总览 Spring中涉及的设计模式: 简单工厂(非23种设计模式中的一种) 工厂方法 单例模式 适配器模式 装饰器模式 代理模式 观察者模式 策略模式 模版方法模式 2. 详细介绍 2.1 简单 ...
- 初探Java设计模式5:一文了解Spring涉及到的9种设计模式
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...
最新文章
- 操作系统级虚拟化概述
- io调度maple调度程序_调度Java应用程序中的主体
- 36岁,程序员,为公司工作8年,昨天HR说公司不准备续约
- BeanPropertyRowMapper
- php读写文件要加锁
- php接口前端,php 接口与前端数据交互实现示例代码
- 算法高级(21)-如何通过IP地址进行定位?
- 教育培训行业的SOP
- 如何通过简历推销自己
- 电脑怎么用计算机打开指令,命令提示符怎么用 命令提示符快捷键打开使用技巧详解...
- 易保全:览契约文化,传契约精神
- 带宽和网速之间的关系
- MT7621原厂openwrt SDK使能串口2和串口3
- 双色球彩票生成之一用户彩票号码随机生成
- HashMap中Entry以及Hash值的理解
- 如果恢复计算机系统时间吗,经常重置、恢复系统对电脑的影响大不大?
- VisualGDB系列3:安装VisualGDB
- 【朝花夕拾】四大组件之(一)Broadcast篇
- Maven 环境配置
- 个人学习笔记(备份)
热门文章
- echarts 不支持 手机 浏览器_中国北斗卫星导航系统真的来了!获国产手机力挺:但iPhone却不支持...
- 容器技术Docker K8s 35 容器服务ACK基础与进阶-应用与发布管理
- 容器技术Docker K8s 5 容器技术在阿里巴巴落地历程
- 批量梯度下降算法BGD
- 概率图模型(PGM)学习笔记(二)贝叶斯网络-语义学与因子分解
- android studio for android learning (七) Android Log类全解
- 【2019南京ICPC网络赛 D】Robots【DAG上的随机游走】
- 实数系的完备性的含义
- hydra怎么构建字典_在Pytorch中构建流数据集
- win10树莓派改ip_用树莓派制作温湿度服务器