Java面试题:Spring框架除了IOC和AOP,还有哪些好玩的设计模式?

Spring是一个基于Java的企业级应用程序开发框架,它使用了多种设计模式来实现其各种特性和功能。本文将介绍一些在Spring中使用的常见设计模式以及相应的代码示例和说明。

单例模式

单例模式是Spring中最常用的设计模式之一。在ApplicationContext中,Bean默认为单例模式。当我们创建一个Bean时,默认情况下它就是单例的。这意味着当Bean被请求时,Spring会返回相同的实例。下面是一个示例代码:

public class MyBean {// ...
}@Configuration
public class AppConfig {@Beanpublic MyBean myBean() {return new MyBean();}}

在上面的代码中,myBean()方法返回了MyBean类的实例,这个实例将作为单例对象存在于ApplicationContext中。

在Spring AOP中,切面默认为单例模式。这意味着切面对象只会创建一次,并与所有目标对象共享。下面的代码演示了如何在Spring AOP中配置一个单例切面:

@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {// ...}}

这里,LoggingAspect类用@Aspect注解进行了标注,它包含了@Before通知,该通知将在com.example.service包中的所有方法执行前执行。由于LoggingAspect是一个@Component,所以它将被Spring自动扫描并创建一个单例实例。

在Spring MVC中,控制器(Controller)也通常是单例的。下面是一个简单的控制器类示例:

@Controller
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/users/{id}")public ResponseEntity<User> getUserById(@PathVariable Long id) {User user = userService.getUserById(id);return ResponseEntity.ok(user);}}

工厂模式

在Spring框架中工厂模式是一种常用的模式之一。下面我将介绍Spring中使用工厂模式的几个具体示例:

BeanFactory

Spring 的核心容器是BeanFactory和其子接口ApplicationContext。其中,BeanFactory使用了工厂模式来创建和管理bean实例。它包含了创建、配置和管理 bean 的所有功能,如下所示:

public interface BeanFactory {Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;boolean containsBean(String name);
}

上述代码中,BeanFactory接口定义了一个getBean()方法,通过传入bean的名称或类型,返回相应的bean实例。这里的getBean()方法就是工厂方法。

FactoryBean

FactoryBean 是 Spring 中另一个使用工厂模式的类。它用于创建复杂的 bean,这些 bean 可以有自己的生命周期、作用域和依赖项等。

public interface FactoryBean<T> {T getObject() throws Exception;Class<?> getObjectType();boolean isSingleton();
}

上述代码中,FactoryBean 定义了一个getObject()方法,用于创建并返回一个特定类型的bean。该方法会在应用程序需要访问bean时被调用。

MessageSource

Spring 的国际化支持是基于MessageSource接口实现的。MessageSource为应用程序提供了访问消息资源的方法,如下所示:

public interface MessageSource {String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
}

上述代码中,getMessage()方法使用工厂模式创建和管理消息资源。它接收消息代码、参数、默认消息和语言环境等参数,并返回相应的消息字符串。

代理模式

在Spring AOP中,代理模式被广泛应用。Spring使用JDK动态代理和CGLIB代理来创建切面。下面是一个简单的使用注解方式配置切面的示例:

@Aspect
@Component
public class LoggingAspect {@Before("execution(public * com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {// ...}}

在上面的代码中,LoggingAspect类使用了@Aspect和@Component注解进行标注,表明它是一个切面,并会被Spring自动扫描并创建代理对象。在@Before通知中,执行方法调用前进行日志记录。

在Spring事务管理中,代理模式也被广泛使用。Spring使用动态代理技术来实现声明式事务管理。下面是一个使用@Transactional注解来声明事务的示例:

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserRepository userRepository;@Override@Transactionalpublic User createUser(User user) {return userRepository.save(user);}}

在上面的代码中,createUser()方法使用@Transactional注解标记,Spring将在该方法调用之前创建一个代理对象。当然,这只是一个简单的示例,实际上,在复杂的应用程序中,Spring可以再通过多种方式来声明式事务。

对于Spring MVC中的控制器类,我们也可以使用代理模式来增强其功能,例如在控制器方法之前和之后添加日志记录。下面是一个基于注解方式实现AOP拦截器的示例:

@Aspect
@Component
public class LoggingInterceptor {@Before("execution(* com.example.controller.*.*(..))")public void logBefore(JoinPoint joinPoint) {// ...}@AfterReturning(value = "execution(* com.example.controller.*.*(..))", returning = "result")public void logAfterReturning(JoinPoint joinPoint, Object result) {// ...}}

在上面的代码中,LoggingInterceptor类使用了@Aspect和@Component注解进行标注,表明它是一个切面,并且会被Spring自动扫描并创建代理对象。在@Before通知中,执行方法调用前进行日志记录,在@AfterReturning通知中,执行方法调用后进行日志记录。

观察者模式

Spring中的事件机制也是基于观察者模式实现的。在Spring中,所有的Bean都可以作为事件源发布事件,其他的Bean则可以通过注册监听器来响应这些事件。

ApplicationEventPublisher

ApplicationEventPublisher是Spring 框架中使用观察者模式的一个类。它负责发布事件并通知已注册的监听器。以下是ApplicationEventPublisher的代码示例:

public interface ApplicationEventPublisher {void publishEvent(ApplicationEvent event);
}

上述代码中,publishEvent()方法用于发布一个事件,并通知已注册的所有监听器。具体的监听器实现可以通过实现ApplicationListener接口来完成。

ApplicationContext 

ApplicationContext是Spring的核心接口之一。它扩展了BeanFactory接口,并在其基础上添加了更多的功能,例如事件发布和提供环境信息等。以下是 ApplicationContext使用观察者模式的代码示例:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {void publishEvent(ApplicationEvent event);String[] getBeanNamesForType(ResolvableType type);<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
}

上述代码中,publishEvent()方法也用于发布事件,并通知已注册的所有监听器。与 ApplicationEventPublisher不同的是,ApplicationContext继承了多个接口,这使得它可以处理各种类型的事件。

BeanPostProcessor 

BeanPostProcessor是Spring框架中一个可插入的回调接口,用于在bean实例化和配置的过程中提供扩展点。以下是BeanPostProcessor使用观察者模式的代码示例:

public interface BeanPostProcessor {Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

postProcessBeforeInitialization()和postProcessAfterInitialization()方法分别在bean实例化和初始化之前/之后被调用。可以将这些方法视为钩子函数,可以在其中添加自定义逻辑以修改或扩展bean的默认行为。

责任链模式 

责任链模式是一种行为型设计模式,它允许你将请求沿着处理链传递,直到其中一个处理程序处理该请求。

HandlerInterceptor 

HandlerInterceptor是Spring MVC中使用责任链模式的一个类。它提供了多个方法,例如 preHandle()、postHandle()和afterCompletion()等,可以在请求处理过程中拦截并修改请求和响应。以下是HandlerInterceptor的代码示例:

public interface HandlerInterceptor {boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception;void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception;
}

上述代码中,HandlerInterceptor提供了三个方法,分别在请求处理前、处理后、以及完成后调用。通过实现这些方法,在请求处理过程中可以执行自定义逻辑,例如验证用户身份、记录日志等。

AbstractRequestLoggingFilter

AbstractRequestLoggingFilter是Spring中使用责任链模式的另一个类。它提供了预先和后续处理请求和响应的方法,可以进行访问日志记录。

以下是 AbstractRequestLoggingFilter的代码示例:

public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter {protected void beforeRequest(HttpServletRequest request, String message) {}protected void afterRequest(HttpServletRequest request, String message) {}
}

上述代码中,AbstractRequestLoggingFilter的beforeRequest()和afterRequest()方法分别在请求处理前和处理后调用。通过实现这些方法,可以记录访问日志,包括请求的地址、参数等信息。

HandlerExceptionResolver

HandlerExceptionResolver是Spring MVC中使用责任链模式的另一个类。它提供了多个方法,例如resolveException()和shouldHandle()等,可以处理异常并决定是否继续执行下一个处理器。以下是HandlerExceptionResolver的代码示例:

public interface HandlerExceptionResolver {@NullableModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler,Exception ex);boolean shouldHandle(HttpServletRequest request, @Nullable Exception ex);
}

上述代码中,HandlerExceptionResolver的resolveException()方法用于处理异常并返回ModelAndView对象,该对象可以包含自定义的错误页面或其他错误信息。而 shouldHandle()方法则用于判断是否应该由当前处理器处理异常,如果返回false,则会继续执行下一个处理器。

模板方法模式 

模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并允许子类实现算法中的某些步骤。在Spring框架中,JdbcTemplate和HibernateTemplate就是使用了模板方法模式的例子。

JdbcTemplate 

JdbcTemplate是Spring中使用模板方法模式的一个类。它提供了多个方法,例如 update()、query()等,可以执行SQL语句并返回结果。以下是JdbcTemplate的代码示例:

public class JdbcTemplate {public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {// ...}// ...public int update(String sql, Object... args) throws DataAccessException {// ...}public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {// ...}// ...
}

上述代码中,JdbcTemplate 提供了execute()、update()和query()等方法,它们都使用了模板方法模式。其中,execute()方法是一个模板方法,它接受一个 ConnectionCallback对象并执行其中的doInConnection()方法,该方法由子类实现。而 update()和 query()方法也是模板方法,它们都调用了execute()方法,并传入不同的参数。

HibernateTemplate 

HibernateTemplate是Spring中使用模板方法模式的另一个类。它提供了多个方法,例如 save()、delete()等,可以操作Hibernate实体并返回结果。以下是HibernateTemplate 的代码示例:

public class HibernateTemplate extends HibernateAccessor {public Object execute(HibernateCallback<?> action) throws DataAccessException {// ...}// ...public void save(Object entity) throws DataAccessException {// ...}public void delete(Object entity) throws DataAccessException {// ...}// ...
}

上述代码中,HibernateTemplate提供了 execute()、save()和 delete()等方法,它们也都使用了模板方法模式。其中,execute()方法是一个模板方法,它接受一个 HibernateCallback对象并执行其中的doInHibernate()方法,该方法由子类实现。而 save()和delete()方法也是模板方法,它们都调用了execute()方法,并传入不同的参数。

策略模式

在Spring框架中,策略模式被广泛应用于各种场景,例如事务管理、缓存管理等。以下是 Spring中使用策略模式的几个具体示例:

事务管理 

Spring提供了多种事务管理方式,其中之一就是基于策略模式实现的。该模式下,开发人员需要将不同的事务属性(如传播行为、隔离级别等)封装到TransactionDefinition 接口的实现类中,并将其作为参数传递给PlatformTransactionManager的方法。以下是一个示例代码:

public class TransactionalTest {private PlatformTransactionManager transactionManager;public void setTransactionManager(PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}public void doTransactional() {DefaultTransactionDefinition definition = new DefaultTransactionDefinition();definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);TransactionStatus status = transactionManager.getTransaction(definition);try {// 执行事务操作transactionManager.commit(status);} catch (Exception e) {transactionManager.rollback(status);}}
}

上述代码中,TransactionalTest类使用了策略模式来管理事务。它将 DefaultTransactionDefinition对象作为参数传递给PlatformTransactionManager的方法,并在try-catch块中执行事务操作。

缓存管理 

Spring 提供了多种缓存管理方式,其中之一就是基于策略模式实现的。该模式下,开发人员需要将不同的缓存属性(如缓存类型、缓存超时时间等)封装到CacheManager和Cache 接口的实现类中,并将其作为参数传递给CacheResolver和Cache的方法。以下是一个示例代码:

public class CacheTest {private CacheResolver cacheResolver;public void setCacheResolver(CacheResolver cacheResolver) {this.cacheResolver = cacheResolver;}public void doCached() {Cache cache = cacheResolver.resolveCache("myCache");Object value = cache.get("myKey");if (value == null) {// 从数据库或其他存储介质中获取数据value = "myValue";cache.put("myKey", value);}}
}

上述代码中,CacheTest类使用了策略模式来管理缓存。它将CacheResolver对象作为参数传递给resolveCache()方法,并根据缓存的键值对判断是否需要从缓存中获取数据。

文章转载自:猫鱼吐泡泡

原文链接:https://www.cnblogs.com/marsitman/p/18191973

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/703988.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPT-4o、GPT-4国内可用!新UI界面率先体验方法!

测试情况&#xff1a; 现根据测试结果&#xff0c;先对比一下普号4o和付费的区别&#xff1a; 注&#xff1a; plus限制情况&#xff1a;4的次数用完后可以用4o&#xff0c;但4o的80条用完后不能用4&#xff1b; team账户限制是100条/3h&#xff0c;4o和4共享额度 目前发现的…

SDL系列(三)—— SDL2.0 扩展库:SDL_image与SDL_mixer

SDL_image SDL 默认支持的&#xff0c;只能打开 BMP 格式的图片 。 然而我们常见的是 Png jpg 格式的图片&#xff0c;于是我们这节完成 SDL 借用 自带的三方库 &#xff0c;来 完成加载渲染 png 等其他图片格式。 SDL_image 简介 使用 SDL_image &#xff0c;您…

Kubernetes的CNI网络插件介绍

前言 CNI 网络在 Kubernetes 中起着关键作用&#xff0c;它负责管理容器的网络连接、通信和安全&#xff0c;为 K8s 集群中的容器提供了稳定、可靠的网络基础设施。在部署 K8s 集群时&#xff0c;选择合适的CNI&#xff08;容器网络接口&#xff09;插件至关重要。不同的 CNI …

【数据可视化-05】:Plotly数据可视化宝典

一、引言 数据可视化是机器学习流程中不可或缺的一部分。通过图形和图表展示数据&#xff0c;我们可以更直观地理解数据的分布、趋势和关联&#xff0c;从而更有效地进行数据分析、特征工程和模型评估。Plotly是一个功能强大且灵活的数据可视化库&#xff0c;它提供了丰富的图表…

【C++】从零开始构建二叉搜索树

送给大家一句话&#xff1a; 我们始终有选择的自由。选错了&#xff0c;只要真诚的反思&#xff0c;真诚面对&#xff0c;也随时有机会修正错误和选择。 – 《奇迹男孩(电影)》 &#x1f4bb;&#x1f4bb;&#x1f4bb;&#x1f4bb;&#x1f4bb;&#x1f4bb;&#x1f4bb;…

机器学习案例:加州房产价格(四)

参考链接&#xff1a;https://hands1ml.apachecn.org/2/#_12 数据探索和可视化、发现规律 通过之前的工作&#xff0c;你只是快速查看了数据&#xff0c;对要处理的数据有了整体了解&#xff0c;现在的目标是更深的探索数据。 首先&#xff0c;保证你将测试集放在了一旁&…

QT状态机1-三态循环状态机

#include "MainWindow.h" #include "ui_MainWindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)

Java学习48-Java 流(Stream)、文件(File)和IO - 复习章节

1.File类的使用 File类的一个实例对应着磁盘上的文件或文件目录。(必须熟悉)File的实例化(新建一个对象)&#xff0c;常用的方法File类中只有新建&#xff0c;删除&#xff0c;获取路径等方法&#xff0c;不包含读写文件的方法&#xff0c;此时需要使用使用下面说的IO流 IO流…

算法练习第22天|39. 组合总和、40.组合总和II

39. 组合总和 39. 组合总和 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/combination-sum/description/ 题目描述&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数…

docker镜像中搭建FastDfs

docker镜像中搭建FastDfs 一、搭建过程二、docker端口映射三、映射的方法三、配置Tracker 和 Storage 环境&#xff1a;腾讯云服务器上 ubuntu20.04镜像 一、搭建过程 正常直接在云服务器上搭建过程参考博客&#xff1a; https://blog.csdn.net/qq_38531706/article/details/…

[笔试训练](二十三)067:打怪068:字符串分类069:城市群数量

目录 067:打怪 068:字符串分类 069:城市群数量 067:打怪 题目链接:打怪 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 直接计算结果&#xff1a; 1.一只怪物能抗几次攻击 int m(H/a)(H%a0?0:1); 2.杀死一只怪物&#xff0c;玩家要抗几次攻击 int nm-1; *3.杀死一只…

嵌入式学习72-复习(字符设备驱动框架)

编辑 drivers/char/Kconfig 为了在make menuconfig是能够显示出我们写的驱动程序 make menuconfig 编辑 drivers/char/Makefile 才是真正把编写好的源文件加入到编译中去 make modules cp drivers/char/first_driver.ko ~/nfs/rootfs/