小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

《Spring 手?jǐn)]專欄》第 11 章:更上層樓,基于觀察者實現(xiàn),容器事件和事件監(jiān)聽器

 小傅哥 2021-12-13

作者:小傅哥
博客:https://

?

沉淀、分享、成長,讓自己和他人都能有所收獲!??

?

目錄

  • 一、前言

  • 二、目標(biāo)

  • 三、方案

  • 四、實現(xiàn)

    • 1. 工程結(jié)構(gòu)

    • 2. 定義和實現(xiàn)事件

    • 3. 事件廣播器

    • 4. 事件發(fā)布者的定義和實現(xiàn)

  • 五、測試

    • 1. 創(chuàng)建一個事件和監(jiān)聽器

    • 2. 配置文件

    • 3. 單元測試

  • 六、總結(jié)

  • 七、系列推薦

一、前言

能解耦,是多么重要的一件事情!

摔杯為號、看我眼色行事、見南面火起,這是在嘎哈么?這其實是在通過事物傳播進(jìn)行解耦引線和炸彈,僅僅是這樣的一個解耦,它放到了多少村夫莽漢,劫了法場,篡了兵權(quán)!

這樣的解耦場景在互聯(lián)網(wǎng)開發(fā)的設(shè)計中使用的也是非常頻繁,如:這里需要一個注冊完成事件推送消息、用戶下單我會發(fā)送一個MQ、收到我的支付消息就可以發(fā)貨了等等,都是依靠事件訂閱和發(fā)布以及MQ消息這樣的組件,來處理系統(tǒng)之間的調(diào)用解耦,最終通過解耦的方式來提升整體系統(tǒng)架構(gòu)的負(fù)載能力。

其實解耦思路可以理解為設(shè)計模式中觀察者模式的具體使用效果,在觀察者模式中當(dāng)對象間存在一對多關(guān)系時,則使用觀察者模式,它是一種定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。這讓我想起了我每個月的車牌搖號,都會推送給我一條本月沒中簽的消息?。?!

二、目標(biāo)

在 Spring 中有一個 Event 事件功能,它可以提供事件的定義、發(fā)布以及監(jiān)聽事件來完成一些自定義的動作。比如你可以定義一個新用戶注冊的事件,當(dāng)有用戶執(zhí)行注冊完成后,在事件監(jiān)聽中給用戶發(fā)送一些優(yōu)惠券和短信提醒,這樣的操作就可以把屬于基本功能的注冊和對應(yīng)的策略服務(wù)分開,降低系統(tǒng)的耦合。以后在擴(kuò)展注冊服務(wù),比如需要添加風(fēng)控策略、添加實名認(rèn)證、判斷用戶屬性等都不會影響到依賴注冊成功后執(zhí)行的動作。

那么在本章節(jié)我們需要以觀察者模式的方式,設(shè)計和實現(xiàn) Spring Event 的容器事件和事件監(jiān)聽器功能,最終可以讓我們在現(xiàn)有實現(xiàn)的 Spring 框架中可以定義、監(jiān)聽和發(fā)布自己的事件信息。

三、方案

其實事件的設(shè)計本身就是一種觀察者模式的實現(xiàn),它所要解決的就是一個對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協(xié)作。

在功能實現(xiàn)上我們需要定義出事件類、事件監(jiān)聽、事件發(fā)布,而這些類的功能需要結(jié)合到 Spring 的 AbstractApplicationContext#refresh(),以便于處理事件初始化和注冊事件監(jiān)聽器的操作。整體設(shè)計結(jié)構(gòu)如下圖:

  • 在整個功能實現(xiàn)過程中,仍然需要在面向用戶的應(yīng)用上下文 AbstractApplicationContext 中添加相關(guān)事件內(nèi)容,包括:初始化事件發(fā)布者、注冊事件監(jiān)聽器、發(fā)布容器刷新完成事件。
  • 使用觀察者模式定義事件類、監(jiān)聽類、發(fā)布類,同時還需要完成一個廣播器的功能,接收到事件推送時進(jìn)行分析處理符合監(jiān)聽事件接受者感興趣的事件,也就是使用 isAssignableFrom 進(jìn)行判斷。
  • isAssignableFrom 和 instanceof 相似,不過 isAssignableFrom 是用來判斷子類和父類的關(guān)系的,或者接口的實現(xiàn)類和接口的關(guān)系的,默認(rèn)所有的類的終極父類都是Object。如果A.isAssignableFrom(B)結(jié)果是true,證明B可以轉(zhuǎn)換成為A,也就是A可以由B轉(zhuǎn)換而來。

四、實現(xiàn)

1. 工程結(jié)構(gòu)

small-spring-step-10
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework
    │           ├── beans
    │           │   ├── factory
    │           │   │   ├── config
    │           │   │   │   ├── AutowireCapableBeanFactory.java
    │           │   │   │   ├── BeanDefinition.java
    │           │   │   │   ├── BeanFactoryPostProcessor.java
    │           │   │   │   ├── BeanPostProcessor.java
    │           │   │   │   ├── BeanReference.java
    │           │   │   │   ├── ConfigurableBeanFactory.java
    │           │   │   │   └── SingletonBeanRegistry.java
    │           │   │   ├── support
    │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   │   ├── AbstractBeanDefinitionReader.java
    │           │   │   │   ├── AbstractBeanFactory.java
    │           │   │   │   ├── BeanDefinitionReader.java
    │           │   │   │   ├── BeanDefinitionRegistry.java
    │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   │   ├── DefaultListableBeanFactory.java
    │           │   │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   │   ├── DisposableBeanAdapter.java
    │           │   │   │   ├── FactoryBeanRegistrySupport.java
    │           │   │   │   ├── InstantiationStrategy.java
    │           │   │   │   └── SimpleInstantiationStrategy.java  
    │           │   │   ├── support
    │           │   │   │   └── XmlBeanDefinitionReader.java
    │           │   │   ├── Aware.java
    │           │   │   ├── BeanClassLoaderAware.java
    │           │   │   ├── BeanFactory.java
    │           │   │   ├── BeanFactoryAware.java
    │           │   │   ├── BeanNameAware.java
    │           │   │   ├── ConfigurableListableBeanFactory.java
    │           │   │   ├── DisposableBean.java
    │           │   │   ├── FactoryBean.java
    │           │   │   ├── HierarchicalBeanFactory.java
    │           │   │   ├── InitializingBean.java
    │           │   │   └── ListableBeanFactory.java
    │           │   ├── BeansException.java
    │           │   ├── PropertyValue.java
    │           │   └── PropertyValues.java 
    │           ├── context  
    │           │   ├── event
    │           │   │   ├── AbstractApplicationEventMulticaster.java 
    │           │   │   ├── ApplicationContextEvent.java 
    │           │   │   ├── ApplicationEventMulticaster.java 
    │           │   │   ├── ContextClosedEvent.java 
    │           │   │   ├── ContextRefreshedEvent.java 
    │           │   │   └── SimpleApplicationEventMulticaster.java 
    │           │   ├── support
    │           │   │   ├── AbstractApplicationContext.java 
    │           │   │   ├── AbstractRefreshableApplicationContext.java 
    │           │   │   ├── AbstractXmlApplicationContext.java 
    │           │   │   ├── ApplicationContextAwareProcessor.java 
    │           │   │   └── ClassPathXmlApplicationContext.java 
    │           │   ├── ApplicationContext.java 
    │           │   ├── ApplicationContextAware.java 
    │           │   ├── ApplicationEvent.java 
    │           │   ├── ApplicationEventPublisher.java 
    │           │   ├── ApplicationListener.java 
    │           │   └── ConfigurableApplicationContext.java
    │           ├── core.io
    │           │   ├── ClassPathResource.java 
    │           │   ├── DefaultResourceLoader.java 
    │           │   ├── FileSystemResource.java 
    │           │   ├── Resource.java 
    │           │   ├── ResourceLoader.java 
    │           │   └── UrlResource.java
    │           └── utils
    │               └── ClassUtils.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── event
                │   ├── ContextClosedEventListener.java
                │   ├── ContextRefreshedEventListener.java
                │   ├── CustomEvent.java
                │   └── CustomEventListener.java
                └── ApiTest.java

工程源碼公眾號「bugstack蟲洞?!?,回復(fù):Spring 專欄,獲取完整源碼

容器事件和事件監(jiān)聽器實現(xiàn)類關(guān)系,如圖 11-2

圖 10-2
  • 以上整個類關(guān)系圖以圍繞實現(xiàn) event 事件定義、發(fā)布、監(jiān)聽功能實現(xiàn)和把事件的相關(guān)內(nèi)容使用 AbstractApplicationContext#refresh 進(jìn)行注冊和處理操作。
  • 在實現(xiàn)的過程中主要以擴(kuò)展 spring context 包為主,事件的實現(xiàn)也是在這個包下進(jìn)行擴(kuò)展的,當(dāng)然也可以看出來目前所有的實現(xiàn)內(nèi)容,仍然是以IOC為主。
  • ApplicationContext 容器繼承事件發(fā)布功能接口 ApplicationEventPublisher,并在實現(xiàn)類中提供事件監(jiān)聽功能。
  • ApplicationEventMulticaster 接口是注冊監(jiān)聽器和發(fā)布事件的廣播器,提供添加、移除和發(fā)布事件方法。
  • 最后是發(fā)布容器關(guān)閉事件,這個仍然需要擴(kuò)展到 AbstractApplicationContext#close 方法中,由注冊到虛擬機(jī)的鉤子實現(xiàn)。

2. 定義和實現(xiàn)事件

cn.bugstack.springframework.context.ApplicationEvent

public abstract class ApplicationEvent extends EventObject {

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */

    public ApplicationEvent(Object source) {
        super(source);
    }

}
  • 以繼承 java.util.EventObject 定義出具備事件功能的抽象類 ApplicationEvent,后續(xù)所有事件的類都需要繼承這個類。

cn.bugstack.springframework.context.event.ApplicationContextEvent

public class ApplicationContextEvent extends ApplicationEvent {

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */

    public ApplicationContextEvent(Object source) {
        super(source);
    }

    /**
     * Get the <code>ApplicationContext</code> that the event was raised for.
     */

    public final ApplicationContext getApplicationContext() {
        return (ApplicationContext) getSource();
    }

}

cn.bugstack.springframework.context.event.ContextClosedEvent

public class ContextClosedEvent extends ApplicationContextEvent{

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */

    public ContextClosedEvent(Object source) {
        super(source);
    }

}

cn.bugstack.springframework.context.event.ContextRefreshedEvent

public class ContextRefreshedEvent extends ApplicationContextEvent{
    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */

    public ContextRefreshedEvent(Object source) {
        super(source);
    }

}
  • ApplicationContextEvent 是定義事件的抽象類,所有的事件包括關(guān)閉、刷新,以及用戶自己實現(xiàn)的事件,都需要繼承這個類。
  • ContextClosedEvent、ContextRefreshedEvent,分別是 Spring 框架自己實現(xiàn)的兩個事件類,可以用于監(jiān)聽刷新和關(guān)閉動作。

3. 事件廣播器

cn.bugstack.springframework.context.event.ApplicationEventMulticaster

public interface ApplicationEventMulticaster {

    /**
     * Add a listener to be notified of all events.
     * @param listener the listener to add
     */

    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * Remove a listener from the notification list.
     * @param listener the listener to remove
     */

    void removeApplicationListener(ApplicationListener<?> listener);

    /**
     * Multicast the given application event to appropriate listeners.
     * @param event the event to multicast
     */

    void multicastEvent(ApplicationEvent event);

}
  • 在事件廣播器中定義了添加監(jiān)聽和刪除監(jiān)聽的方法以及一個廣播事件的方法 multicastEvent 最終推送時間消息也會經(jīng)過這個接口方法來處理誰該接收事件。

cn.bugstack.springframework.context.event.AbstractApplicationEventMulticaster

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticasterBeanFactoryAware {

    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.remove(listener);
    }

    @Override
    public final void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
        LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
        for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
            if (supportsEvent(listener, event)) allListeners.add(listener);
        }
        return allListeners;
    }

    /**
     * 監(jiān)聽器是否對該事件感興趣
     */

    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的實例化類型,需要判斷后獲取目標(biāo) class
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];

        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;
        try {
            eventClassName = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new BeansException("wrong event class name: " + className);
        }
        // 判定此 eventClassName 對象所表示的類或接口與指定的 event.getClass() 參數(shù)所表示的類或接口是否相同,或是否是其超類或超接口。
        // isAssignableFrom是用來判斷子類和父類的關(guān)系的,或者接口的實現(xiàn)類和接口的關(guān)系的,默認(rèn)所有的類的終極父類都是Object。如果A.isAssignableFrom(B)結(jié)果是true,證明B可以轉(zhuǎn)換成為A,也就是A可以由B轉(zhuǎn)換而來。
        return eventClassName.isAssignableFrom(event.getClass());
    }

}
  • AbstractApplicationEventMulticaster 是對事件廣播器的公用方法提取,在這個類中可以實現(xiàn)一些基本功能,避免所有直接實現(xiàn)接口放還需要處理細(xì)節(jié)。
  • 除了像 addApplicationListener、removeApplicationListener,這樣的通用方法,這里這個類中主要是對 getApplicationListeners 和 supportsEvent 的處理。
  • getApplicationListeners 方法主要是摘取符合廣播事件中的監(jiān)聽處理器,具體過濾動作在 supportsEvent 方法中。
  • 在 supportsEvent 方法中,主要包括對Cglib、Simple不同實例化需要獲取目標(biāo)Class,Cglib代理類需要獲取父類的Class,普通實例化的不需要。接下來就是通過提取接口和對應(yīng)的 ParameterizedType 和 eventClassName,方便最后確認(rèn)是否為子類和父類的關(guān)系,以此證明此事件歸這個符合的類處理。可以參考代碼中的注釋

supportsEvent 方法運(yùn)行截圖

  • 在代碼調(diào)試中可以看到,最終 eventClassName 和 event.getClass() 在 isAssignableFrom 判斷下為 true
  • 關(guān)于 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 可以嘗試在 AbstractApplicationContext 類中更換驗證。

4. 事件發(fā)布者的定義和實現(xiàn)

cn.bugstack.springframework.context.ApplicationEventPublisher

public interface ApplicationEventPublisher {

    /**
     * Notify all listeners registered with this application of an application
     * event. Events may be framework events (such as RequestHandledEvent)
     * or application-specific events.
     * @param event the event to publish
     */

    void publishEvent(ApplicationEvent event);

}
  • ApplicationEventPublisher 是整個一個事件的發(fā)布接口,所有的事件都需要從這個接口發(fā)布出去。

cn.bugstack.springframework.context.support.AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    @Override
    public void refresh() throws BeansException {

        // 6. 初始化事件發(fā)布者
        initApplicationEventMulticaster();

        // 7. 注冊事件監(jiān)聽器
        registerListeners();

        // 9. 發(fā)布容器刷新完成事件
        finishRefresh();
    }

    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

    private void registerListeners() {
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener : applicationListeners) {
            applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    private void finishRefresh() {
        publishEvent(new ContextRefreshedEvent(this));
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

    @Override
    public void close() {
        // 發(fā)布容器關(guān)閉事件
        publishEvent(new ContextClosedEvent(this));

        // 執(zhí)行銷毀單例bean的銷毀方法
        getBeanFactory().destroySingletons();
    }

}
  • 在抽象應(yīng)用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件發(fā)布者注冊事件監(jiān)聽器、發(fā)布容器刷新完成事件,三個方法用于處理事件操作。
  • 初始化事件發(fā)布者(initApplicationEventMulticaster),主要用于實例化一個 SimpleApplicationEventMulticaster,這是一個事件廣播器。
  • 注冊事件監(jiān)聽器(registerListeners),通過 getBeansOfType 方法獲取到所有從 spring.xml 中加載到的事件配置 Bean 對象。
  • 發(fā)布容器刷新完成事件(finishRefresh),發(fā)布了第一個服務(wù)器啟動完成后的事件,這個事件通過 publishEvent 發(fā)布出去,其實也就是調(diào)用了 applicationEventMulticaster.multicastEvent(event); 方法。
  • 最后是一個 close 方法中,新增加了發(fā)布一個容器關(guān)閉事件。publishEvent(new ContextClosedEvent(this));

五、測試

1. 創(chuàng)建一個事件和監(jiān)聽器

cn.bugstack.springframework.test.event.CustomEvent

public class CustomEvent extends ApplicationContextEvent {

    private Long id;
    private String message;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */

    public CustomEvent(Object source, Long id, String message) {
        super(source);
        this.id = id;
        this.message = message;
    }

    // ...get/set
}
  • 創(chuàng)建一個自定義事件,在事件類的構(gòu)造函數(shù)中可以添加自己的想要的入?yún)⑿畔?。這個事件類最終會被完成的拿到監(jiān)聽里,所以你添加的屬性都會被獲得到。

cn.bugstack.springframework.test.event.CustomEventListener

public class CustomEventListener implements ApplicationListener<CustomEvent{

    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("收到:" + event.getSource() + "消息;時間:" + new Date());
        System.out.println("消息:" + event.getId() + ":" + event.getMessage());
    }

}
  • 這個是一個用于監(jiān)聽 CustomEvent 事件的監(jiān)聽器,這里你可以處理自己想要的操作,比如一些用戶注冊后發(fā)送優(yōu)惠券和短信通知等。
  • 另外是關(guān)于 ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent>、ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> 監(jiān)聽器,這里就不演示了,可以參考下源碼。

2. 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean class="cn.bugstack.springframework.test.event.ContextRefreshedEventListener"/>

    <bean class="cn.bugstack.springframework.test.event.CustomEventListener"/>

    <bean class="cn.bugstack.springframework.test.event.ContextClosedEventListener"/>

</beans>
  • 在 spring.xml 中配置了三個事件監(jiān)聽器,監(jiān)聽刷新、監(jiān)控自定義事件、監(jiān)聽關(guān)閉事件。

3. 單元測試

public class ApiTest {

    @Test
    public void test_event() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L"成功了!"));

        applicationContext.registerShutdownHook();
    }

}
  • 通過使用 applicationContext 新增加的發(fā)布事件接口方法,發(fā)布一個自定義事件 CustomEvent,并透傳了相應(yīng)的參數(shù)信息。

測試結(jié)果

刷新事件:cn.bugstack.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$440a36f5
收到:cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@71c7db30消息;時間:22:32:50
消息:1019129009086763:成功了!
關(guān)閉事件:cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d

Process finished with exit code 0
  • 從測試結(jié)果可以看到,我們自己定義的事件和監(jiān)聽,以及監(jiān)聽系統(tǒng)的事件信息,都可以在控制臺完整的輸出出來了。你也可以嘗試增加一些其他事件行為,并調(diào)試代碼學(xué)習(xí)觀察者模式。

六、總結(jié)

  • 在整個手寫 Spring 框架的學(xué)習(xí)過程中,可以逐步看到很多設(shè)計模式的使用,比如:簡單工廠BeanFactory、工廠方法FactoryBean、策略模式訪問資源,現(xiàn)在又實現(xiàn)了一個觀察者模式的具體使用。所以學(xué)習(xí) Spring 的過程中,要更加注意關(guān)于設(shè)計模式的運(yùn)用,這是你能讀懂代碼的核心也是學(xué)習(xí)的重點(diǎn)。
  • 那么本章節(jié)關(guān)于觀察者模式的實現(xiàn)過程,主要包括了事件的定義、事件的監(jiān)聽和發(fā)布事件,發(fā)布完成后根據(jù)匹配策略,監(jiān)聽器就會收到屬于自己的事件內(nèi)容,并做相應(yīng)的處理動作,這樣的觀察者模式其實日常我們也經(jīng)常使用,不過在結(jié)合 Spring 以后,除了設(shè)計模式的學(xué)習(xí),還可以學(xué)到如何把相應(yīng)觀察者的實現(xiàn)和應(yīng)用上下文結(jié)合。
  • 所有在 Spring 學(xué)習(xí)到的技術(shù)、設(shè)計、思路都是可以和實際的業(yè)務(wù)開發(fā)結(jié)合起來的,而這些看似比較多的代碼模塊,其實也是按照各自職責(zé)一點(diǎn)點(diǎn)的擴(kuò)充進(jìn)去的。在自己的學(xué)習(xí)過程中,可以先動手嘗試完成這些框架功能,在一點(diǎn)點(diǎn)通過調(diào)試的方式與 Spring 源碼進(jìn)行對照參考,最終也就慢慢掌握這些設(shè)計和編碼能力了。

七、系列推薦

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多