一、简介
设计模式是软件工程领域的一组最佳实践,它们提供了一种通用解决方案来解决常见问题。Spring框架中融入了多种设计模式,以增强其灵活性、可扩展性和可重用性。
二、单例模式
Spring的单例模式指的是:确保一个类只有一个实例,并提供一个全局访问点。Spring管理的bean默认都是单例的,通过配置文件中的singleton属性进行控制。下面是一些单例模式的样例:
1.懒汉模式(线程不安全)优点:懒加载启动快,资源占用小,使用时才实例化,无锁。缺点:非线程安全。
public class LazySingleton {// 定义私有变量存储实例private static LazySingleton singleton = null;// 私有构造方法,控制实例无法在外部通过new创建实例private LazySingleton() {}public static LazySingleton getInstance() {if (singleton == null) {singleton = new LazySingleton ();}return singleton;}
}2.懒汉模式(线程安全)
public class LazySingleton {// 定义私有变量存储实例private static LazySingleton singleton = null;// 私有构造方法,控制实例无法在外部通过new创建实例private LazySingleton() {}// 加入synchronized 为独占排他锁,并发性能差。即使在创建成功以后,获取实例仍然是串行化操作。public static *synchronized* LazySingleton getInstance() {if (singleton == null) {singleton = new LazySingleton ();}return singleton;}
}3.懒汉模式(双重加锁检查DCL)
public class LazySingleton {// 对保存实例的变量添加volatile的修饰,保证实例变量的可见性private static volatile LazySingleton singleton = null;// 私有构造方法,控制实例无法在外部通过new创建实例private LazySingleton() {}public static LazySingleton getInstance() {// 先检查实例是否存在,如果不存在才进入下面的同步块,否则直接返回现有实例if (singleton == null) {// 同步块,线程安全的创建实例synchronized(LazySingleton .class){//再次检查实例是否存在,如果不存在才真的创建实例if(singleton == null){singleton = new LazySingleton ();}} }return singleton;}
}4.饿汉模式优点:饿汉模式天生是线程安全的,使用时没有延迟。缺点:启动时即创建实例,启动慢,有可能造成资源浪费。
public class Singleton {//直接在这里创建类实例,只会创建一次private static Singleton instance = new Singleton();//私有构造方法,控制实例无法在外部通过new创建实例private Singleton(){}//定义一个方法来为客户端提供类实例,这个方法需要定义成类方法,也就是要加staticpublic static Singleton getInstance(){//直接使用已经创建好的实例return instance;}
}5.静态内部类
public class Singleton {/*** 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载*/private static class SingletonHolder{/*** 静态初始化器,由JVM来保证线程安全*/private static Singleton instance = new Singleton();}// 私有构造方法,控制实例无法在外部通过new创建实例private Singleton(){}public static Singleton getInstance(){return SingletonHolder.instance;}
}
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,而这种方式是Singleton类被装载了,
instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,
才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,
另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,
那么这个时候实例化instance显然是不合适的。
三、工厂模式
在Spring框架中,工厂模式主要体现在以下几种形式:
- 简单工厂模式(Simple Factory Pattern):简单工厂模式是最直观的一种工厂模式,它通过一个单独的工厂类来创建其他类的实例。在Spring中,BeanFactory和ApplicationContext接口就是简单工厂模式的体现。例如,当你调用
ApplicationContext.getBean("beanName")
来获取Bean时,Spring容器就会返回与"beanName"关联的Bean实例。
1. 首先,定义一个产品接口和具体的产品实现类:
public interface Product {void use();
}public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("使用产品A");}
}public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("使用产品B");}
}2. 然后,实现一个简单工厂类:
public class SimpleFactory {public Product createProduct(String type) {switch (type) {case "A":return new ConcreteProductA();case "B":return new ConcreteProductB();default:throw new IllegalArgumentException("未知的产品类型");}}
}3. 接下来,在Spring的配置文件中注册工厂类和产品:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 注册简单工厂 --><bean id="simpleFactory" class="com.example.SimpleFactory"/><!-- 注册产品A --><bean id="productA" class="com.example.ConcreteProductA"/><!-- 注册产品B --><bean id="productB" class="com.example.ConcreteProductB"/>
</beans>4. 最后,通过Spring的ApplicationContext来获取工厂类的实例,并使用它来创建产品:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取工厂类
SimpleFactory factory = context.getBean("simpleFactory", SimpleFactory.class);// 创建并使用产品A
Product productA = factory.createProduct("A");
productA.use();// 创建并使用产品B
Product productB = factory.createProduct("B");
productB.use();
- 工厂方法模式(Factory Method Pattern):工厂方法模式是通过定义一个用于创建对象的接口,让子类决定要实例化哪一个类。在Spring中,FactoryBean接口就是工厂方法模式的应用。FactoryBean接口要求实现该类的实例必须是一个单例,并且这个实例本身也是被容器管理的Bean。
1. 首先,定义一个产品接口和具体的产品实现类:
public interface Product {void show();
}public class ProductA implements Product {@Overridepublic void show() {System.out.println("这是产品A");}
}2. 接着,创建一个FactoryBean实现类:
public class ProductFactory implements FactoryBean<Product> {@Overridepublic Product getObject() throws Exception {// 返回产品实例return new ProductA();}@Overridepublic Class<?> getObjectType() {// 返回产品的类类型return Product.class;}@Overridepublic boolean isSingleton() {// 返回true表示单例return true;}
}3. 接下来,在Spring的配置文件中注册FactoryBean:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 注册FactoryBean --><bean id="productFactory" class="com.example.ProductFactory"/><!-- 当请求Product类型的Bean时,将会从productFactory中获取 --><bean id="product" class="org.springframework.beans.factory.config.ListFactoryBean"><property name="targetBeanName" value="productFactory"/></bean>
</beans>4. 最后,通过Spring的ApplicationContext获取由FactoryBean创建的产品对象:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取由FactoryBean创建的产品对象
Product product = context.getBean("product", Product.class);
product.show();
- 抽象工厂模式:抽象工厂模式提供一个接口来创建一系列相关或依赖对象,而不需要指定它们具体的类。在Spring中,可以使用
AbstractApplicationContext
来创建不同类型的ApplicationContext
,每个ApplicationContext
可以针对特定的应用程序需求进行配置。
1. 首先,定义一个产品接口和几种产品的具体实现类:
public interface Product {String use();
}public class ProductA implements Product {@Overridepublic String use() {return "使用产品A";}
}public class ProductB implements Product {@Overridepublic String use() {return "使用产品B";}
}2. 然后,创建一个抽象工厂接口和具体的工厂实现类:
public interface AbstractFactory {Product createProductA();Product createProductB();
}public class ConcreteFactoryA implements AbstractFactory {@Overridepublic Product createProductA() {return new ProductA();}@Overridepublic Product createProductB() {return new ProductB();}
}public class ConcreteFactoryB implements AbstractFactory {@Overridepublic Product createProductA() {return new ProductA();}@Overridepublic Product createProductB() {return new ProductB();}
}3. 接下来,在Spring的配置文件中使用组件扫描和自动装配来创建具体的工厂实例:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 激活注解扫描 --><context:component-scan base-package="com.example"/><!-- 定义一个Map,用于存放不同工厂的实例 --><util:map id="factories" map-class="java.util.HashMap"><entry key="FactoryA" value-ref="concreteFactoryA"/><entry key="FactoryB" value-ref="concreteFactoryB"/></util:map>
</beans>4. 最后,在代码中使用Spring的ApplicationContext来获取具体的工厂实例,并通过工厂实例创建产品:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取工厂实例
AbstractFactory factory = (AbstractFactory) context.getBean("FactoryA");// 创建产品
Product productA = factory.createProductA();
Product productB = factory.createProductB();// 使用产品
System.out.println(productA.use());
System.out.println(productB.use());
四、代理模式
在Spring框架中,代理模式是一种非常重要的设计模式,它被广泛应用于AOP(面向切面编程)和事务管理等方面。Spring的代理模式主要分为两大类:静态代理和动态代理。
- 静态代理:是在编译时就已经确定好的代理对象,它需要手动编写一个代理类来实现目标类的接口或者继承目标类。这种方式比较简单,但是不够灵活,因为每次增加新的业务逻辑都需要修改代理类的代码。
- 动态代理:是在运行时动态生成的代理对象,它通过反射机制在运行时动态创建一个实现了目标接口的代理类。Spring主要使用JDK动态代理和CGLIB代理两种方式来实现动态代理。
- JDK动态代理:JDK动态代理只能代理实现了接口的类,它通过字节码技术在运行时动态地生成一个子类,这个子类实现了所有的接口,并在内部持有目标对象的引用。当调用接口方法时,代理对象会截取方法调用并执行相关的逻辑。
- CGLIB代理:CGLIB是一个第三方的字节码处理库,它可以在运行时扩展Java类和实现Java接口。与JDK动态代理相比,CGLIB可以代理没有实现接口的类,它通过继承目标类并覆盖其方法来实现代理。
以下是一个基于Spring AOP的代理模式实现样例:
1. 首先,我们定义一个目标接口:
public interface Service {void execute();
}2. 然后,创建实现该接口的目标类:
public class ServiceImpl implements Service {@Overridepublic void execute() {System.out.println("执行业务操作");}
}3. 接下来,定义一个切面类,用于拦截目标类的方法调用:
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.Service.*(..))")public void logBeforeService() {System.out.println("方法执行前的日志记录");}
}4. 然后,在Spring配置文件或使用注解配置中,开启AOP支持并定义切面组件:
@Configuration
@EnableAspectJAutoProxy // 启用基于注解的自动代理
public class AppConfig {
}5. 最后,在主应用程序中使用目标对象:
public class MainApp {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 获取代理对象Service service = context.getBean(Service.class);// 调用方法,此时会触发切面的逻辑service.execute();}
}
五、观察者模式
在Spring框架中,观察者模式通常是通过事件处理机制来实现的,这是观察者模式的一种应用。在Spring中,可以通过定义事件和监听器来实现观察者模式。通过Spring的事件发布/订阅机制,可以实现解耦的观察者模式,使得事件的发布者和监听器之间不需要直接的引用关系。以下是Spring观察者模式的一个简单实现示例:
1. 首先定义一个事件类:
public class MyEvent extends ApplicationEvent {private final String message;public MyEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}2. 接着,创建一个监听器接口:
public interface MyEventListener extends ApplicationListener<MyEvent> {void onApplicationEvent(MyEvent event);
}3. 然后实现监听器接口:
@Component
public class MyEventListenerImpl implements MyEventListener {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());}
}4. 在Spring配置中,需要注册事件监听器:
@Configuration
public class EventConfig {public EventConfig(MyEventListener myEventListener) {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.addApplicationListener(myEventListener);// 设置为Spring应用上下文中的事件发布者((AbstractApplicationContext) myEventListener).setEventMulticaster(eventMulticaster);}
}5. 最后,在需要触发事件的地方发布事件:
@Component
public class MyEventPublisher {private final ApplicationEventPublisher publisher;@Autowiredpublic MyEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}public void publishEvent() {MyEvent event = new MyEvent(this, "Hello, Spring!");publisher.publishEvent(event);}
}
六、模板方法模式
在Spring框架中,模板方法模式是一种行为设计模式,它在抽象类中定义了算法的骨架,将一些步骤延迟到子类中实现。通过这种方式,Spring框架的模板方法模式允许子类在不改变算法结构的前提下,重写算法中的某些步骤,从而实现代码复用和灵活性。Spring框架中的很多组件都采用了模板方法模式,例如JdbcTemplate
、HibernateTemplate
等。以下是一个简单的模板方法模式的实现示例:
1. 首先,定义一个抽象类,其中包含模板方法和一些钩子方法(hook methods):
public abstract class AbstractTemplate {// 模板方法,定义算法的骨架public final void templateMethod() {beforeHook();doStep();afterHook();}// 钩子方法1,子类可以重写该方法以提供具体实现protected void beforeHook() {// 默认实现,可以被覆盖}// 钩子方法2,子类可以重写该方法以提供具体实现protected abstract void doStep();// 钩子方法3,子类可以重写该方法以提供具体实现protected void afterHook() {// 默认实现,可以被覆盖}
}2. 然后,创建子类继承抽象类,并重写钩子方法:
public class ConcreteClass extends AbstractTemplate {@Overrideprotected void doStep() {// 实现具体步骤System.out.println("执行具体步骤");}
}3. 最后,使用抽象类的模板方法调用子类的具体实现:
public class Client {public static void main(String[] args) {AbstractTemplate template = new ConcreteClass();template.templateMethod();}
}