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;
}