Java面試題:Spring框架除了IOC和AOP,還有哪些好玩的設計模式?

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線程安全?別擔心,只要你不寫併發代碼就好了!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章