我打江南走过
那等在季节里的容颜如莲花的开落
东风不来,三月的柳絮不飞
你的心如小小的寂寞的城
恰若青石的街道向晚
跫音不响,三月的春帷不揭
你的心是小小的窗扉紧掩
我达达的马蹄是美丽的错误
我不是归人,是个过客……——郑愁予《错误》
前言
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 什么是单例注册表呢?
- 使用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;
}
}
继续分析单例的获取流程:
@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,也是值得分析和借鉴的。
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 {
@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 类