Bootstrap

今日学习:Spring框架的常见设计模式

1. Spring框架的设计模式

简单工厂模式:
Spring 中的 BeanFactory 就是简单工厂模式的体现。根据传入一个唯一的标识来获得 Bean 对象,但是在传入参数后创建还是传入参数前创建,要根据具体情况来定。

工厂模式:
Spring 中的 FactoryBean 就是典型的工厂方法模式,实现了 FactoryBean 接口的 bean是一类叫做 factory 的 bean。其特点是,spring 在使用 getBean() 调用获得该 bean 时,会自动调用该 bean 的 getObject() 方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()方法的返回值。

单例模式:
在 spring 中用到的单例模式有: scope=“singleton” ,注册式单例模式,bean 存放于Map 中。bean name 当做 key,bean 当做 value。

原型模式:
在 spring 中用到的原型模式有: scope=“prototype” ,每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。

迭代器模式:
在 Spring 中有个 CompositeIterator 实现了 Iterator,Iterable 接口和 Iterator 接口,这两个都是迭代相关的接口。可以这么认为,实现了 Iterable 接口,则表示某个对象是可被迭代的。Iterator 接口相当于是一个迭代器,实现了 Iterator 接口,等于具体定义了这个可被迭代的对象时如何进行迭代的。

代理模式:
Spring 中经典的 AOP,就是使用动态代理实现的,分 JDK 和 CGlib 动态代理。

适配器模式:
Spring 中的 AOP 中 AdvisorAdapter 类,它有三个实现:MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。Spring会根据不同的 AOP 配置来使用对应的 Advice,与策略模式不同的是,一个方法可以同时拥有多个Advice。Spring 存在很多以 Adapter 结尾的,大多数都是适配器模式。

观察者模式:
Spring 中的 Event 和 Listener。spring 事件:ApplicationEvent,该抽象类继承了EventObject 类,JDK 建议所有的事件都应该继承自 EventObject。spring 事件监听器:ApplicationListener,该接口继承了 EventListener 接口,JDK 建议所有的事件监听器都应该继承EventListener。

模板模式:
Spring 中的 org.springframework.jdbc.core.JdbcTemplate 就是非常经典的模板模式的应用,里面的 execute 方法,把整个算法步骤都定义好了。

责任链模式:
DispatcherServlet 中的 doDispatch() 方法中获取与请求匹配的处理器HandlerExecutionChain,this.getHandler() 方法的处理使用到了责任链模式。

2.Spring的单例设计模式

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。为了控制创建对象数量

/**
 * 单例模式练习
 */
public class TestSingleton {
    public static void main(String[] args) {
        Set<Integer> set =new HashSet<>();
        for (int i = 0; i < 100; i++) {
           Singleton1 s = Singleton1.getInstance();

            set.add(s.hashCode());
        }
        System.out.println(set.size());
    }
}
/**
 * 单例模式对象(懒汉模式)
 */
public class Singleton1 {

    private static Singleton1 instance;
    //将构造器私有,不能通过构造器创造对象
    private Singleton1() {
    }
    public static Singleton1 getInstance(){
        //如果没对象,造一个对象
        if (null==instance){
            instance=new Singleton1();
        }
        //有的话直接返回原来造过的对象
        return instance;
    }

}
2.1 懒汉模式的线程安全问题

懒汉模式写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

      //多线程模式访问,懒汉模式会有线程安全问题
        Thread t1 = new Thread(() ->
        {
            for (int i = 0; i < 100; i++) {
                try {
                    Singleton1 s = Singleton1.getInstance();
                    System.out.println(s.hashCode());
                    Thread.sleep(200);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() ->
        {
            for (int i = 0; i < 100; i++) {
                try {
                    Singleton1 s = Singleton1.getInstance();
                    System.out.println(s.hashCode());
                    Thread.sleep(200);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //运行线程
        t1.start();
        t2.start();

//控制台打印了多个对象的hashcode
886649433
2121687866
2121687866

解决懒汉模式的线程安全问题,同步锁方式

使用线程同步,对getInstance()方法进行线程同步。
**缺点:**效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。

public class Singleton1 {

    private static Singleton1 instance;
    //将构造器私有,不能通过构造器创造对象
    private Singleton1() {
    }
    //加上synchronized 锁,解决线程安全问题,但是效率会降低
    public static synchronized Singleton1 getInstance(){
        //如果没对象,造一个对象
        if (null==instance){
            instance=new Singleton1();
        }
        //有的话直接返回原来造过的对象
        return instance;
    }

}
2.2 饿汉模式

利用JVM隔离其他线程,就是在类装载的时候就完成实例化,避免线程安全问题
缺点在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

/**
 * 单例,饿汉模式
 */
public class Singleton2 {
    //利用JVM隔离其他线程,就是在类装载的时候就完成实例化,避免线程安全问题
    //缺点在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

    private static Singleton2 instance = new Singleton2();

    private Singleton2(){

    }
    public static Singleton2 getInstance(){
        return instance;
    }


}
2.3 枚举法

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

/**
 * 枚举法的单例模式
 */
public enum Singleton3 {
    INSTANCE;

}

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;