Spring是一個基於Java的企業級應用程式開發框架,它使用了多種設計模式來實現其各種特性和功能。本文將介紹一些在Spring中使用的常見設計模式以及相應的程式碼示例和說明。
單例模式
單例模式是Spring中最常用的設計模式之一。在ApplicationContext中,Bean預設為單例模式。當我們建立一個Bean時,預設情況下它就是單例的。這意味著當Bean被請求時,Spring會返回相同的例項。下面是一個示例程式碼:
public class MyBean { // ... } @Configuration public class AppConfig { @Bean public 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 { @Autowired private 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 { @Autowired private UserRepository userRepository; @Override @Transactional public 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 { @Nullable ModelAndView 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()方法,並根據快取的鍵值對判斷是否需要從快取中獲取資料。
往期面試題:
Java面試題:如果你這樣做,你會後悔的,兩次啟動同一個執行緒~~~
Java面試題:@PostConstruct、init-method和afterPropertiesSet執行順序?
Java面試題:SimpleDateFormat是執行緒安全的嗎?使用時應該注意什麼?
Java面試題:細數ThreadLocal大坑,記憶體洩露本可避免
Java面試題:請談談對ThreadLocal的理解?
Java面試題:為什麼HashMap不建議使用物件作為Key?
Java面試題:你知道Spring的IOC嗎?那麼,它為什麼這麼重要呢?
Java面試題:執行緒池內“鬧情緒”的執行緒,怎麼辦?
Java面試題:Spring Bean執行緒安全?別擔心,只要你不寫併發程式碼就好了!