常见设计模式以及它们在Spring中的应用

Spring中用到的设计模式
1 工厂方法模式:Spring使用工厂模式通过BeanFactory ApplicationContext
简单工厂模式:BeanFactory.getBean() 根据id从IoC中获取Bean
2 代理模式:AOP中的动态代理
3 单例模式:Spring中的Bean默认作用范围是为单例
4 适配器模式:AOP的通知、 SpringMVC中前端控制器调用Controller用到了适配器模式

工厂设计模式

工厂方法对加简单工厂的改进:
用工厂子类替代switch-case判断,满足了开闭原则。
简单工厂:在工厂类用switch-case来根据条件获取指定的操作类,但是违背了开闭原则(只能添加类不能修改原有代码)。
如果增加了操作类,就需要在工厂类中修改switach-case。
工厂方法:用指定的工厂子类去获取对应的操作类。后期只需要添加工厂子类即可

在这里插入图片描述

简单工厂在Spring IoC中的应用:
在需要使用Bean时,使用getBean()来获取
BeanFactory.getBean(),即通过id从IoC容器中获取Bean时,就是通过工厂类来从操作类中获取一个Bean

工厂设计模式在Spring IoC中的应用:
先通过反射找到对应工厂类,反射的信息配置在

然后由该工厂类调用实际操作类来获取Bean,工厂类和操作类的对应关系配置在

1 定义操作类,相当于上图中的加法类,属于操作类

public class CarInstanceFactory {
    private Map<Integer, Car> map = new HashMap<Integer,Car>();

    public void setMap(Map<Integer, Car> map) {
        this.map = map;
    }

    public CarInstanceFactory(){
    }

    public Car getCar(int id){
        return map.get(id);
    }
}

2 在XML中定义一个工厂类或者多个工厂类,指定该工厂类对应的操作类
然后定义每个Bean对应的工厂类
carFactory就是一个工厂类,相当于上面图中的一个加法工厂
com.home.factoryMethod.CarInstanceFactory就是carFactory对应的操作类,相当于上图中的加法类

 <bean id="carFactory" class="com.home.factoryMethod.CarInstanceFactory">
    <property name="map">
        <map>
            <entry key="4">
                    <bean class="com.home.factoryMethod.Car">
                        <property name="id" value="4"></property>   
                        <property name="name" value="Honda"></property> 
                        <property name="price" value="300000"></property>   
                    </bean>
            </entry>    

            <entry key="6">
                    <bean class="com.home.factoryMethod.Car">
                        <property name="id" value="6"></property>   
                        <property name="name" value="ford"></property>  
                        <property name="price" value="500000"></property>   
                    </bean>
            </entry>
        </map>  
    </property>
 </bean>

 <!-- 2.use Factory bean to get bean objectr 
    factory-bean : the bean define above
    factory-method: method of get Bean Object
    constructor-arg: parameters of factory-method
 -->
 <bean id="car4" factory-bean="carFactory" factory-method="getCar">
    <constructor-arg value="4"></constructor-arg>           
 </bean>

 //当系统需要取出car6时,会通过反射找到该Bean对应的工厂为carFactory
 <bean id="car6" factory-bean="carFactory" factory-method="getCar">
    <constructor-arg value="6"></constructor-arg>           
 </bean

单例模式

保证一个类只有一个实例
特点:单例类的构造函数都是私有的

懒汉模式:
特点:在第一次使用单例对象时,才去实例化单例对象
实现方式:
1 使用双重检查锁
使用volatile来禁止实例化对象时指令重排,防止实例化的对象为一空内存区域
使用锁 + 双if,防止多线程下单例对象被实例化多次

public classs Singtonle{
    private volatile static Singtone instance;
    
    public static Singleton(){
        if(instance == null){
            synchornized(Singtone.class){
                if (instance == null){
                    instance = new Singletone();
                }
            }
        }
        return instance;
    }
}

2 使用私有静态内部类。
在内部类中,定义一个静态的单例类实例对象,并实例化它
在外部类中用静态方法返回刚才实例的对象

public class Singleton{
    private static class SingletonHolder{
        private static final Singleton instance = new Singletone();
    }
    
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

饿汉模式:
特点:在第一次使用前,单例对象就被实例完毕
实现方式: 将对象实例设置成静态成员变量,并直接初始化。在单例类被加载时就会实例化单例对象
缺点:无法在系统运行时给单例对象的创建添加额外的参数,只能在编译阶段读取配置文件中的参数

public class Singleton{
    private static final Singletoninstance = new Singleton();
    private Singleton(){)
    
    public static Singleton getInstance(){
        return instance;
    }
}

单例模式在IoC容器中的使用:单例注册表
单例注册表:就是一个ConcurrentHashMap,key为Bean的id,value为Bean对象
使用ConcurrentHashMap可以避免多线程下的安全问题,如并发修改异常、数据不一致
jdk1.8中ConcurrentHashMap基于HashMap + ReentrantLock + CAS
使用单例注册表的原因:
IoC中的单例模式既不是饿汉模式,也不是懒汉模式, 因为这些单例模式都是从构造函数上着手的
因为IoC管理的是任意的对象,所以不能修改所有类的构造函数
所以IoC中比没有从构造函数上去控制单例,而是在后期的使用过程中,用注册表控制单例对象实例的个数

使用流程:
1 IoC容器初始化时,会读取XML或者扫描注解,将一些单例模式的Bean注入IoC容器,即插入ConcurrentHashMap中
2 当需要使用相应的对象时,就根据名称或类型去map中寻找。
3 如果找不到,就会创建一个
4 判断该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;    
}    
} 

代理模式

在这里插入图片描述

用代理对象去执行目标对象中的方法

代理模式在AOP中的使用:
1 通过运行时动态代理,来动态地生成目标对象的代理对象,从而可以增强目标对象中的方法
2 编译时的通过cglib来生成代理对象,基于字节码操作

比如需要用AOP生成给UserService的代理对象,添加日志管理
那么IUserService就是上图中的Subject类, UserService就是RrealSubject, 而代生成的代理对象就是Proxy类

使用动态代理来给常规的service添加事务
1 根据接口信息动态创建一个代理对象
2 将代理对象注入IoC容器
3 需要使用时就从IoC中取出这个代理对象

@Configuration
public class BeanFactory {

    private IAccountService accountService;

    private TransactionManager txManager;

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }


    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    /**
     * 获取Service代理对象
     * @return
     */
     @Bean
    public IAccountService proxyAccountService() {
        return (IAccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事务的支持
                     *
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        if("test".equals(method.getName())){
                            return method.invoke(accountService,args);
                        }

                        Object rtValue = null;
                        try {
                            //1.开启事务
                            txManager.beginTransaction();
                            //2.执行操作
                            rtValue = method.invoke(accountService, args);
                            //3.提交事务
                            txManager.commit();
                            //4.返回结果
                            return rtValue;
                        } catch (Exception e) {
                            //5.回滚操作
                            txManager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            //6.释放连接
                            txManager.release();
                        }
                    }
                });
    }
}

在需要使用这个service时,根据id来注入

@Resource("proxyAccountService")
IAccountService proxyAccountService;

适配器模式

作用:将一个接口转换为用户希望的另一个接口。从而使原本因为接口不兼容而不能一起工作的那些类可以一起工作
使用方式:在两个不兼容的类之间,加一个适配器类
使用场景:想使用一个已经存在的类,但是该类中方法的名称与你预期的不相同。
即想复用已有的类,但是接口与复用环境要求不一致
那就新创建一个类,类中的方法名是你所希望的,然后用这个类的方法去调用原有类中对应的方法
优点:提高了系统的复用性和扩展性。可以模糊具体的实现,对外提供统一的访问接口

比如SpringBoot的日志系统中,就是使用门面框架去调日志体实现框架。即用sl4j去调用Logback或者Log4j
sl4j中的API是固定的,但是Logback和Log4j中操作日志的API是不同的,为了实现无缝切换日志实现框架,
就需要在门面框架和实现框架中间,加一个适配器类。每个日志实现框架有自己对应的适配器类
这些适配器类中的方法的名称是相同的, 但是不同适配器中方法的具体实现是针对特定的日志实现框架
具体调用过程就是:门面框架调用适配器类,适配器类调用它对应的日志实现框架

例如target类是:

class Target{
    public virtual void Request(){
        Console.WriteLine("普通请求");
    }
}


   Adatee(需要被适配的类)为:
class Adaptee{
    public void SpecificRequest(){
        COnsole.WriteLine("特殊请求");
    }
}

此时我们需要调用一个类中的Request(),但是这个类中对应方法的名称是SpecificRequest()兼容
我们就需要在中间加一个适配器类,适配器类中的方法名是我们所期望的
然后在适配器类中去调用实际的方法

class Adapter : Target{
    private Adaptee adpatee = new Adaptee();
     
    public override void Request(){
        adaptee.SpecificRequest();
    }
}

此时我们就可以通过适配器类,来调用目标类了,因为适配器类中的方法名符合我们的要求

Target target = new Adapter();
target.Request()

这里的Adaptee类相当于日志实现框架,Target类相当于日志门面框架。
门面框架想直接调用日志实现框架,但是门面框架中保存的方法名与实现框架的不一致
所以就添加一个适配器类,在适配器类中定义一个符合门面框架要求的方法,在该方法中去调用日志实现框架

适配器模式在SpringMVC中的使用:

使用的原因:
在SpringMVC中,前端控制器处理请求的方式是固定的,但是不同的请求需要由不同的Controller来处理,
不同Controller中,方法名称是不同的,所以不同的Controller不能与前端控制器兼容
就需要在它们中间加一个适配器(HandlerAdapter) 每个Controller都有对应的适配器

运行流程:
1 前端控制器在收到请求后,会利用HandleMapping来寻找处理该请求的Controller
2 获取这个Controller的适配器
3 前端控制器会通过这个适配器来调用Controller中方法

适配器模式在AOP中的应用:
AOP中处理通知的接口是统一的,但是通知的类型却有多种,如前置通知、后置通知‘、异常通知
每个通知都有对用的拦截器:MethodBeforeAdviceInterceptor AfterReturningAdviceInterceptor ThrowsAdviceInterceptor
Spring需要将每个通知都封装成对应的拦截器类型
因为AOP的本质就是先用动态代理来生成代理对象,然后把通知转换成拦截器,用来拦截目标对象方法的执行
但是每个通知中的方法名是不同的,所以与拦截器不兼容
所以需要在它们中间添加适配器

需要被适配的类如下,即Adaptee

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
}

public interface AfterReturningAdvice extends AfterAdvice {
    void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
}

public interface ThrowsAdvice extends AfterAdvice {
}

而目标类如下,即target

public interface AdvisorAdapter {
    boolean supportsAdvice(Advice var1);

    MethodInterceptor getInterceptor(Advisor var1);
}

我们发现target中的方法和Adaptee中的不兼容,所以需要适配器类Adapter

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}
}

@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof AfterReturningAdvice);
	}
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
		return new AfterReturningAdviceInterceptor(advice);
	}
}

class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof ThrowsAdvice);
	}
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		return new ThrowsAdviceInterceptor(advisor.getAdvice());
	}
}

客户端DefaultAdvisorAdapterRegistry,即结构图中的Client类

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
    private final List<AdvisorAdapter> adapters = new ArrayList(3);

    public DefaultAdvisorAdapterRegistry() {
        // 这里注册了适配器
        this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }
    
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor)advice);
        }

        Iterator var4 = this.adapters.iterator();

        while(var4.hasNext()) {
            AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
            if (adapter.supportsAdvice(advice)) {   // 这里调用适配器方法
                interceptors.add(adapter.getInterceptor(advisor));  // 这里调用适配器方法
            }
        }

        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        } else {
            return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);
        }
    }
    // ...省略...
}    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值