一、简介
设计模式是软件工程领域的一组最佳实践,它们提供了一种通用解决方案来解决常见问题。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(){}
//定义一个方法来为客户端提供类实例,这个方法需要定义成类方法,也就是要加static
public 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 {
@Override
public void use() {
System.out.println("使用产品A");
}
}
public class ConcreteProductB implements Product {
@Override
public 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/beans
http://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 {
@Override
public void show() {
System.out.println("这是产品A");
}
}
2. 接着,创建一个FactoryBean实现类:
public class ProductFactory implements FactoryBean<Product> {
@Override
public Product getObject() throws Exception {
// 返回产品实例
return new ProductA();
}
@Override
public Class<?> getObjectType() {
// 返回产品的类类型
return Product.class;
}
@Override
public 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/beans
http://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 {
@Override
public String use() {
return "使用产品A";
}
}
public class ProductB implements Product {
@Override
public String use() {
return "使用产品B";
}
}
2. 然后,创建一个抽象工厂接口和具体的工厂实现类:
public interface AbstractFactory {
Product createProductA();
Product createProductB();
}
public class ConcreteFactoryA implements AbstractFactory {
@Override
public Product createProductA() {
return new ProductA();
}
@Override
public Product createProductB() {
return new ProductB();
}
}
public class ConcreteFactoryB implements AbstractFactory {
@Override
public Product createProductA() {
return new ProductA();
}
@Override
public 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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://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 {
@Override
public 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 {
@Override
public 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;
@Autowired
public 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 {
@Override
protected void doStep() {
// 实现具体步骤
System.out.println("执行具体步骤");
}
}
3. 最后,使用抽象类的模板方法调用子类的具体实现:
public class Client {
public static void main(String[] args) {
AbstractTemplate template = new ConcreteClass();
template.templateMethod();
}
}
七、适配器模式
在Spring的设计哲学中,适配器模式是一种常见的模式,它允许Spring容器提供灵活的方式来集成和扩展现有组件,无论是在Web层还是在AOP编程中。通过这种方式,Spring提高了其框架的可扩展性和可重用性。以下是适配器模式常用的两种形式:
Web MVC
的HandlerAdapter
:在Spring MVC中,HandlerAdapter
是一个关键组件,它负责协调控制器(Controller
)和处理器映射(HandlerMapping
)。HandlerAdapter
定义了一个统一的接口,允许不同类型的控制器以相同的方式被Spring MVC框架调用。这意味着无论控制器是基于简单的Java类、注解方法还是其他任何机制,HandlerAdapter
都能够适配并执行它们。- AOP Proxy:Spring AOP(面向切面编程)使用适配器模式来创建代理对象。在Spring中,可以通过配置或注解来声明一个切面(Aspect),而AOP框架则需要创建一个代理对象来包装目标对象(Target Object),以便在执行目标方法前后添加额外的行为(Advice)。
下面是使用HandlerAdapter
来处理不同类型的控制器请求的适配器模式样例:
1. 首先,假设我们有一个简单的控制器接口:
public interface SimpleController {
String handleRequest(HttpServletRequest request);
}
2. 然后,我们有一个具体的控制器实现:
public class HelloWorldController implements SimpleController {
@Override
public String handleRequest(HttpServletRequest request) {
// 处理请求并返回响应字符串
return "Hello, World!";
}
}
3. 接下来,我们定义SimpleControllerHandlerAdapter,它实现了HandlerAdapter接口:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
// 检查是否支持当前的handler
return handler instanceof SimpleController;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!supports(handler)) {
throw new IllegalArgumentException("Unsupported handler type: " + handler.getClass().getName());
}
SimpleController controller = (SimpleController) handler;
String viewName = controller.handleRequest(request);
// 这里我们简单地将响应内容作为视图名称返回
return new ModelAndView(viewName);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
// 返回内容最后修改时间,此处为了简化,直接返回-1
return -1;
}
}
4. 最后,在Spring的配置中注册这个适配器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}
// 其他配置...
}
在这个样例中,SimpleControllerHandlerAdapter适配器检查传入的handler是否为SimpleController的实例,
如果是,则调用它的handleRequest方法来处理请求,并返回一个ModelAndView对象表示响应。
这样,当Spring MVC框架接收到一个请求时,它可以使用这个适配器来调用相应的控制器,并生成适当的响应。
八、装饰器模式
在Spring框架中,装饰器模式通常是通过使用AOP(面向切面编程)来实现的。装饰器模式允许在运行时动态地给一个对象添加一些额外的职责,而不改变其结构。这在Spring中非常有用,因为它提供了一种灵活的方式来增强对象的行为,而无需创建大量的子类。下面是一个使用Spring AOP实现装饰器模式的样例:
1. 定义一个业务接口:
public interface BusinessService {
void performTask();
}
2. 实现业务接口:
public class SimpleBusinessService implements BusinessService {
@Override
public void performTask() {
System.out.println("Performing simple task.");
}
}
3. 定义一个装饰器接口:
public interface ServiceDecorator extends BusinessService {
void additionalFunction();
}
4. 实现装饰器接口:
public class Decorator implements ServiceDecorator {
private BusinessService businessService;
public Decorator(BusinessService businessService) {
this.businessService = businessService;
}
@Override
public void performTask() {
businessService.performTask();
additionalFunction();
}
@Override
public void additionalFunction() {
System.out.println("Additional function is called.");
}
}
5. 在Spring配置文件中配置AOP代理:
<aop:config>
<aop:pointcut id="businessServiceOperation" expression="execution(* com.example.BusinessService.*(..))"/>
<aop:advisor advice-ref="decoratorAdvice" pointcut-ref="businessServiceOperation"/>
</aop:config>
<bean id="businessService" class="com.example.SimpleBusinessService"/>
<bean id="decoratedBusinessService" class="com.example.Decorator">
<constructor-arg ref="businessService"/>
</bean>
6. 定义一个通知(Advice)类:
@Component
public class DecoratorAdvice {
private final BusinessService businessService;
@Autowired
public DecoratorAdvice(BusinessService businessService) {
this.businessService = businessService;
}
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before performing the task.");
Object result = joinPoint.proceed();
System.out.println("After performing the task.");
return result;
}
}
在这个例子中,我们定义了一个名为SimpleBusinessService的简单业务服务,
并通过Decorator类来增强它的功能。然后我们使用Spring AOP来创建Decorator的代理,
这样当调用performTask方法时,它会先执行原始的SimpleBusinessService的performTask方法,
然后执行额外的功能。
九、策略模式
在Spring框架中,策略模式可以通过利用依赖注入和接口实现来实现。这种模式定义了一系列的算法,并将每个算法封装起来,使它们可以互换。策略的选择可以在运行时决定,而不需要修改使用策略的客户端代码。以下是如何在Spring中实现策略模式的步骤:
1. 定义一个策略接口:
public interface Strategy {
void execute();
}
2. 实现策略接口:
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
// 实现算法A
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
// 实现算法B
}
}
3. 定义上下文类,用于持有策略接口的引用,并在运行时切换策略:
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
4. 在Spring配置文件中配置策略的实现和上下文:
<!-- 定义策略Bean -->
<bean id="strategyA" class="com.example.ConcreteStrategyA"/>
<bean id="strategyB" class="com.example.ConcreteStrategyB"/>
<!-- 定义上下文Bean,并注入所需的策略 -->
<bean id="context" class="com.example.Context">
<constructor-arg ref="strategyA"/>
</bean>
5. 使用策略:
public class Client {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Context ctx = context.getBean("context", Context.class);
// 在运行时切换策略
ctx.setStrategy(context.getBean("strategyB", Strategy.class));
ctx.executeStrategy();
}
}
在上述代码中,Strategy接口定义了一个execute方法,用于执行算法逻辑。ConcreteStrategyA和
ConcreteStrategyB是策略的具体实现。Context类负责维护对策略的引用,并提供方法来切换和执行策略。
十、建造者模式
在Spring框架中,建造者模式通常用于创建具有复杂内部状态的对象。这种模式通过将对象的构建过程与其表示分离,允许用户以逐步的方式构建对象。在Spring中,建造者模式可以通过多种方式实现,包括使用Spring的@Bean注解和Java配置类。
以下是一个简单的示例,展示如何在Spring中使用建造者模式来创建一个复杂对象:
1. 首先,定义一个复杂对象的类:
public class Product {
private String partA;
private String partB;
private String partC;
// 构造函数、getter和setter省略
}
2. 然后,创建一个建造者类来逐步构建这个对象:
public class ProductBuilder {
private Product product = new Product();
public ProductBuilder withPartA(String partA) {
product.setPartA(partA);
return this;
}
public ProductBuilder withPartB(String partB) {
product.setPartB(partB);
return this;
}
public ProductBuilder withPartC(String partC) {
product.setPartC(partC);
return this;
}
public Product build() {
return product;
}
}
3. 最后,在Spring配置中定义一个方法来创建ProductBuilder的实例,并使用它来构建Product对象:
@Configuration
public class AppConfig {
@Bean
public ProductBuilder productBuilder() {
return new ProductBuilder();
}
@Bean
public Product product(ProductBuilder builder) {
return builder
.withPartA("Part A")
.withPartB("Part B")
.withPartC("Part C")
.build();
}
}
在这个示例中,ProductBuilder类提供了一系列的方法来逐步构建Product对象。
每个方法都返回ProductBuilder的实例,以便链式调用。最终,build方法用于生成完整的Product对象。