1. 程式人生 > >第二十七章:SpringBoot使用ApplicationEvent&Listener完成業務解耦

第二十七章:SpringBoot使用ApplicationEvent&Listener完成業務解耦

ApplicationEvent以及Listener是Spring為我們提供的一個事件監聽、訂閱的實現,內部實現原理是觀察者設計模式,設計初衷也是為了系統業務邏輯之間的解耦,提高可擴充套件性以及可維護性。事件釋出者並不需要考慮誰去監聽,監聽具體的實現內容是什麼,釋出者的工作只是為了釋出事件而已。

我們平時日常生活中也是經常會有這種情況存在,如:我們在平時拔河比賽中,裁判員給我們吹響了開始的訊號,也就是給我們釋出了一個開始的事件,而拔河雙方人員都在監聽著這個事件,一旦事件釋出後雙方人員就開始往自己方使勁。而裁判並不關心你比賽的過程,只是給你釋出事件你執行就可以了。

本章目標

我們本章在SpringBoot

平臺上通過ApplicationEvents以及Listener來完成簡單的註冊事件流程。

SpringBoot 企業級核心技術學習專題

專題 專題名稱 專題描述
001 講解SpringBoot一些企業級層面的核心元件
002 Spring Boot 核心技術簡書每一篇文章碼雲對應原始碼
003 對Spring Cloud核心技術全面講解
004 Spring Cloud 核心技術簡書每一篇文章對應原始碼
005 全面講解QueryDSL核心技術以及基於SpringBoot整合SpringDataJPA

構建專案

我們本章只是簡單的講解如何使用ApplicationEvent以及Listener來完成業務邏輯的解耦,不涉及到資料互動所以依賴需要引入的也比較少,專案pom.xml配置檔案如下所示:

.....//省略
<dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId
>
</dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.16</version> </dependency> <!--test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> .....//省略

其中lombok依賴大家有興趣可以去深研究下,這是一個很好的工具,它可以結合Idea開發工具完成對實體的動態新增建構函式、Getter/Setter方法、toString方法等。

建立UserRegisterEvent事件

我們先來建立一個事件,監聽都是圍繞著事件來掛起的。事件程式碼如下所示:

package com.yuqiyu.chapter27.event;

import com.yuqiyu.chapter27.bean.UserBean;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:08
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Getter
public class UserRegisterEvent extends ApplicationEvent
{
    //註冊使用者物件
    private UserBean user;

    /**
     * 重寫建構函式
     * @param source 發生事件的物件
     * @param user 註冊使用者物件
     */
    public UserRegisterEvent(Object source,UserBean user) {
        super(source);
        this.user = user;
    }
}

我們自定義事件UserRegisterEvent繼承了ApplicationEvent,繼承後必須過載建構函式,建構函式的引數可以任意指定,其中source引數指的是發生事件的物件,一般我們在釋出事件時使用的是this關鍵字代替本類物件,而user引數是我們自定義的註冊使用者物件,該物件可以在監聽內被獲取。

在Spring內部中有多種方式實現監聽如:@EventListener註解、實現ApplicationListener泛型介面、實現SmartApplicationListener介面等,我們下面來講解下這三種方式分別如何實現。

建立UserBean

我們簡單建立一個使用者實體,並新增兩個欄位:使用者名稱、密碼。實體程式碼如下所示:

package com.yuqiyu.chapter27.bean;
import lombok.Data;
/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:05
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Data
public class UserBean
{
    //使用者名稱
    private String name;
    //密碼
    private String password;
}

建立UserService

UserService內新增一個註冊方法,該方法只是實現註冊事件釋出功能,程式碼如下所示:

package com.yuqiyu.chapter27.service;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:11
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Service
public class UserService
{
    @Autowired
    ApplicationContext applicationContext;

    /**
     * 使用者註冊方法
     * @param user
     */
    public void register(UserBean user)
    {
        //../省略其他邏輯

        //釋出UserRegisterEvent事件
        applicationContext.publishEvent(new UserRegisterEvent(this,user));
    }
}

事件釋出是由ApplicationContext物件管控的,我們釋出事件前需要注入ApplicationContext物件呼叫publishEvent方法完成事件釋出。

建立UserController

建立一個@RestController控制器,對應新增一個註冊方法簡單實現,程式碼如下所示:

package com.yuqiyu.chapter27.controller;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 使用者控制器
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:05
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class UserController
{
    //使用者業務邏輯實現
    @Autowired
    private UserService userService;

    /**
     * 註冊控制方法
     * @param user 使用者物件
     * @return
     */
    @RequestMapping(value = "/register")
    public String register
            (
                    UserBean user
            )
    {
        //呼叫註冊業務邏輯
        userService.register(user);
        return "註冊成功.";
    }
}

@EventListener實現監聽

註解方式比較簡單,並不需要實現任何介面,具體程式碼實現如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 使用@EventListener方法實現註冊事件監聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:50
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class AnnotationRegisterListener {

    /**
     * 註冊監聽實現方法
     * @param userRegisterEvent 使用者註冊事件
     */
    @EventListener
    public void register(UserRegisterEvent userRegisterEvent)
    {
        //獲取註冊使用者物件
        UserBean user = userRegisterEvent.getUser();

        //../省略邏輯

        //輸出註冊使用者資訊
        System.out.println("@EventListener註冊資訊,使用者名稱:"+user.getName()+",密碼:"+user.getPassword());
    }
}

我們只需要讓我們的監聽類被Spring所管理即可,在我們使用者註冊監聽實現方法上新增@EventListener註解,該註解會根據方法內配置的事件完成監聽。下面我們啟動專案來測試下我們事件釋出時是否被監聽者所感知。

測試事件監聽

2017-07-21 11:09:52.532  INFO 10460 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-07-21 11:09:52.532  INFO 10460 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-07-21 11:09:52.545  INFO 10460 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 13 ms
@EventListener註冊資訊,使用者名稱:admin,密碼:123456

可以看到我們使用@EventListener註解配置的監聽已經生效了,當我們在UserService內釋出了註冊事件時,監聽方法自動被呼叫並且輸出內資訊到控制檯。

ApplicationListener實現監聽

這種方式也是Spring之前比較常用的監聽事件方式,在實現ApplicationListener介面時需要將監聽事件作為泛型傳遞,監聽實現程式碼如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 原始方式實現
 * 使用者註冊監聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:24
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class RegisterListener implements ApplicationListener<UserRegisterEvent>
{
    /**
     * 實現監聽
     * @param userRegisterEvent
     */
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        //獲取註冊使用者物件
        UserBean user = userRegisterEvent.getUser();

        //../省略邏輯

        //輸出註冊使用者資訊
        System.out.println("註冊資訊,使用者名稱:"+user.getName()+",密碼:"+user.getPassword());
    }
}

我們實現介面後需要使用@Component註解來宣告該監聽需要被Spring注入管理,當有UserRegisterEvent事件釋出時監聽程式會自動呼叫onApplicationEvent方法並且將UserRegisterEvent物件作為引數傳遞。
我們UserService內的釋出事件不需要修改,我們重啟下專案再次訪問之前的地址檢視控制檯輸出的內容如下所示:

2017-07-21 13:03:35.399  INFO 4324 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-07-21 13:03:35.399  INFO 4324 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-07-21 13:03:35.411  INFO 4324 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 12 ms
註冊資訊,使用者名稱:admin,密碼:123456

我們看到了控制檯列印了我們監聽內輸出使用者資訊,事件釋出後就不會考慮具體哪個監聽去處理業務,甚至可以存在多個監聽同時需要處理業務邏輯。

我們在註冊時如果不僅僅是記錄註冊資訊到資料庫,還需要傳送郵件通知使用者,當然我們可以建立多個監聽同時監聽UserRegisterEvent事件,接下來我們先來實現這個需求。

郵件通知監聽

我們使用註解的方式來完成郵件傳送監聽實現,程式碼如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.event.UserRegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 註冊使用者事件傳送郵件監聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:13:08
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class RegisterUserEmailListener
{
    /**
     * 傳送郵件監聽實現
     * @param userRegisterEvent 使用者註冊事件
     */
    @EventListener
    public void sendMail(UserRegisterEvent userRegisterEvent)
    {
        System.out.println("使用者註冊成功,傳送郵件。");
    }
}

監聽編寫完成後,我們重啟專案,再次訪問註冊請求地址檢視控制檯輸出內容如下所示:

2017-07-21 13:09:20.671  INFO 7808 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-07-21 13:09:20.671  INFO 7808 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-07-21 13:09:20.685  INFO 7808 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 14 ms
使用者註冊成功,傳送郵件。
註冊資訊,使用者名稱:admin,密碼:123456

我們看到控制檯輸出的內容感到比較疑惑,我註冊時使用者資訊寫入資料庫應該在傳送郵件前面,為什麼沒有在第一步執行呢?
好了,證明了一點,事件監聽是無序的,監聽到的事件先後順序完全隨機出現的。我們接下來使用SmartApplicationListener實現監聽方式來實現該邏輯。

SmartApplicationListener實現有序監聽

我們對註冊使用者以及傳送郵件的監聽重新編寫,註冊使用者寫入資料庫監聽程式碼如下所示:

package com.yuqiyu.chapter27.listener;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import com.yuqiyu.chapter27.service.UserService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 使用者註冊>>>儲存使用者資訊監聽
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:10:09
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class UserRegisterListener implements SmartApplicationListener
{
    /**
     *  該方法返回true&supportsSourceType同樣返回true時,才會呼叫該監聽內的onApplicationEvent方法
     * @param aClass 接收到的監聽事件型別
     * @return
     */
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        //只有UserRegisterEvent監聽型別才會執行下面邏輯
        return aClass == UserRegisterEvent.class;
    }

    /**
     *  該方法返回true&supportsEventType同樣返回true時,才會呼叫該監聽內的onApplicationEvent方法
     * @param aClass
     * @return
     */
    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        //只有在UserService內釋出的UserRegisterEvent事件時才會執行下面邏輯
        return aClass == UserService.class;
    }

    /**
     *  supportsEventType & supportsSourceType 兩個方法返回true時呼叫該方法執行業務邏輯
     * @param applicationEvent 具體監聽例項,這裡是UserRegisterEvent
     */
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {

        //轉換事件型別
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
        //獲取註冊使用者物件資訊
        UserBean user = userRegisterEvent.getUser();
        //.../完成註冊業務邏輯
        System.out.println("註冊資訊,使用者名稱:"+user.getName()+",密碼:"+user.getPassword());
    }

    /**
     * 同步情況下監聽執行的順序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

SmartApplicationListener介面繼承了全域性監聽ApplicationListener,並且泛型物件使用的ApplicationEvent來作為全域性監聽,可以理解為使用SmartApplicationListener作為監聽父介面的實現,監聽所有事件釋出。

既然是監聽所有的事件釋出,那麼SmartApplicationListener介面添加了兩個方法supportsEventType、supportsSourceType來作為區分是否是我們監聽的事件,只有這兩個方法同時返回true時才會執行onApplicationEvent方法。

可以看到除了上面的方法,還提供了一個getOrder方法,這個方法就可以解決執行監聽的順序問題,return的數值越小證明優先順序越高,執行順序越靠前。

註冊成功傳送郵件通知監聽程式碼如下所示:

package com.yuqiyu.chapter27.listener.order;

import com.yuqiyu.chapter27.bean.UserBean;
import com.yuqiyu.chapter27.event.UserRegisterEvent;
import com.yuqiyu.chapter27.service.UserService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:13:38
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class UserRegisterSendMailListener implements SmartApplicationListener
{
    /**
     *  該方法返回true&supportsSourceType同樣返回true時,才會呼叫該監聽內的onApplicationEvent方法
     * @param aClass 接收到的監聽事件型別
     * @return
     */
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        //只有UserRegisterEvent監聽型別才會執行下面邏輯
        return aClass == UserRegisterEvent.class;
    }

    /**
     *  該方法返回true&supportsEventType同樣返回true時,才會呼叫該監聽內的onApplicationEvent方法
     * @param aClass
     * @return
     */
    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        //只有在UserService內釋出的UserRegisterEvent事件時才會執行下面邏輯
        return aClass == UserService.class;
    }

    /**
     *  supportsEventType & supportsSourceType 兩個方法返回true時呼叫該方法執行業務邏輯
     * @param applicationEvent 具體監聽例項,這裡是UserRegisterEvent
     */
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        //轉換事件型別
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
        //獲取註冊使用者物件資訊
        UserBean user = userRegisterEvent.getUser();
        System.out.println("使用者:"+user.getName()+",註冊成功,傳送郵件通知。");
    }

    /**
     * 同步情況下監聽執行的順序
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
}

在getOrder方法內我們返回的數值為“1”,這就證明了需要在儲存註冊使用者資訊監聽後執行,下面我們重啟專案訪問註冊地址檢視控制檯輸出內容如下所示:

2017-07-21 13:40:43.104  INFO 10128 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-07-21 13:40:43.104  INFO 10128 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-07-21 13:40:43.119  INFO 10128 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 15 ms
註冊資訊,使用者名稱:admin,密碼:123456
使用者:admin,註冊成功,傳送郵件通知。

這次我們看到了輸出的順序就是正確的了,先儲存資訊然後再發送郵件通知。

如果說我們不希望在執行監聽時等待監聽業務邏輯耗時,釋出監聽後立即要對介面或者介面做出反映,我們該怎麼做呢?

使用@Async實現非同步監聽

@Aysnc其實是Spring內的一個元件,可以完成對類內單個或者多個方法實現非同步呼叫,這樣可以大大的節省等待耗時。內部實現機制是執行緒池任務ThreadPoolTaskExecutor,通過執行緒池來對配置@Async的方法或者類做出執行動作。

執行緒任務池配置

我們建立一個ListenerAsyncConfiguration,並且使用@EnableAsync註解開啟支援非同步處理,具體程式碼如下所示:

package com.yuqiyu.chapter27;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * 非同步監聽配置
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/21
 * Time:14:04
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Configuration
@EnableAsync
public class ListenerAsyncConfiguration implements AsyncConfigurer
{
    /**
     * 獲取非同步執行緒池執行物件
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        //使用Spring內建執行緒池任務物件
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //設定執行緒池引數
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

我們自定義的監聽非同步配置類實現了AsyncConfigurer介面並且實現內getAsyncExecutor方法以提供執行緒任務池物件的獲取。
我們只需要在非同步方法上新增@Async註解就可以實現方法的非同步呼叫,為了證明這一點,我們在傳送郵件onApplicationEvent方法內新增執行緒阻塞3秒,修改後的程式碼如下所示:

 /**
     * supportsEventType & supportsSourceType 兩個方法返回true時呼叫該方法執行業務邏輯
     * @param applicationEvent 具體監聽例項,這裡是UserRegisterEvent
     */
    @Override
    @Async
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        try {
            Thread.sleep(3000);//靜靜的沉睡3秒鐘
        }catch (Exception e)
        {
            e.printStackTrace();
        }
        //轉換事件型別
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
        //獲取註冊使用者物件資訊
        UserBean user = userRegisterEvent.getUser();
        System.out.println("使用者:"+user.getName()+",註冊成功,傳送郵件通知。");
    }

下面我們重啟下專案,訪問註冊地址,檢視介面反映是否也有延遲。
我們測試發現訪問介面時反映速度要不之前還要快一些,我們去檢視控制檯時,可以看到註冊資訊輸出後等待3秒後再才輸出郵件傳送通知,而在這之前介面已經做出了反映。

注意:如果存在多個監聽同一個事件時,並且存在非同步與同步同時存在時則不存在執行順序。

總結

我們在傳統專案中往往各個業務邏輯之間耦合性較強,因為我們在service都是直接引用的關聯service或者jpa來作為協作處理邏輯,然而這種方式在後期更新、維護性難度都是大大提高了。然而我們採用事件通知、事件監聽形式來處理邏輯時耦合性則是可以降到最小。

更多幹貨文章掃碼關注微信公眾號

掃碼關注 - 專注分享

加入知識星球,恆宇少年帶你走以後的技術道路!!!

微信掃碼加入

相關推薦

第二SpringBoot使用ApplicationEvent&Listener完成業務

ApplicationEvent以及Listener是Spring為我們提供的一個事件監聽、訂閱的實現,內部實現原理是觀察者設計模式,設計初衷也是為了系統業務邏輯之間的解耦,提高可擴充套件性以及可維護性。事件釋出者並不需要考慮誰去監聽,監聽具體的實現內容是什麼,

第二JavaEE專案的三層架構

作者:java_wxid JavaEE專案的三層架構 分層的作用 方便專案後期的維護和升級,以及擴充套件。 分層的好處是降低程式碼的耦合度 分層後的程式碼包結構 Dao持久層的包 com.dao 放dao層的介面 com.dao.impl 放dao層的實現類 Se

“全棧2019”Java第二流程控制語句中迴圈語句for

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十七章:流程控制語句中迴圈語句for 下一章 “全棧2019”Java第二十八章:陣列詳

“全棧2019”Java多線程第二Lock獲取lock/釋放unlock鎖

ava image intel 線程 .com 初級 鏈接 版權 公眾號 難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文鏈接 “全棧2019”Java多線程第二十七章:

SpringBoot使用ApplicationEvent&Listener完成業務

ApplicationEvent以及Listener是Spring為我們提供的一個事件監聽、訂閱的實現,內部實現原理是觀察者設計模式,設計初衷也是為了系統業務邏輯之間的解耦,提高可擴充套件性以及可維護性。事件釋出者並不需要考慮誰去監聽,監聽具體的實現內容是什麼,釋出者的工作只

第二JQuery

作者:java_wxid 點選:API文件下載 Jquery介紹 1.什麼是JQuery ? jQuery,顧名思義,也就是JavaScript和查詢(Query),它就是輔助JavaScript開發的js類庫。 2.JQuery核心思想: 它的核心思想是write less

第二JavaScript語言

作者:java_wxid 《JavaScript語言精粹》點選下載,密碼:synu JavaScript介紹: 1.Javascript語言誕生主要是完成頁面的資料驗證。 2.它執行在客戶端,需要執行瀏覽器來解析執行JavaScript程式碼。 3.JS是Netscape網景公司

學習筆記第二AC自動機

正題       聽說NOIP要考,所以臨時補了一下,多了一種思考方式。       AC自動機是基於KMP和字典樹的,要想透徹瞭解AC自動機,最好先學KMP和字典樹。       那麼,AC自

第二EL表示式

作者:java_wxid 什麼是EL表示式? E L的全稱:Expression Language,就是表示式語言。可以輸出表達式的值。跟jsp的表示式指令碼一樣。計算表示式的值後輸出。 EL表示式出現的目的是為了使JSP寫起來更加簡單,讓jsp的程式碼更佳簡化。 我們先來看一下

第二JSP動態頁面

作者:java_wxid 1.什麼是Jsp? jsp是java server page,java的伺服器頁面。 2.為什麼要學習jsp技術 因為jsp技術可以很好的解決在Servlet程式中回傳資料是html內容,這個問題。 在Servlet程式中回傳html資料,為什麼是個問題

第二Servlet下

HttpServletRequest類 a)HttpServletRequest類有什麼作用。 HttpServletRequest類它的作用是。每次只要有請求進來 。Tomcat伺服器就會把請求的協議內容封裝到HttpServletRequest物件中。 我們主要是從Request類中

第二Servlet上

作者:java_wxid Servlet技術 a)什麼是Servlet 1、Servlet是一個介面(JavaEE規範) 2、Servlet是執行在伺服器(Tomcat或其他的伺服器)上的小程式。 3、Servlet程式用來接收使用者的請求,和給客戶端響應資料。(接收請求,回傳響應)

第二Tomcat伺服器

作者:java_wxid JavaWeb的概念 什麼是JavaWeb? JavaWeb指的是使用java語言編寫所有的能夠讓瀏覽器訪問的程式的總稱。我們叫javaWeb。 a)什麼是請求 請求是指客戶端給伺服器傳送資料。 b)什麼是響應 響應是指伺服器回傳給客戶端的資料。 c

“全棧2019”Java第賦值運算子和算術運算子

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第十七章:賦值運算子和算術運算子 下一章 “全棧2019”Java第十八章:一元運算子 學

“全棧2019”Java第賦值運算符和算術運算符

技術 intel 難度 允許 更多 https 同步 com 運算符 難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文鏈接 “全棧2019”Java第十七章:賦值運算符和

“全棧2019”Java第二流程控制語句中的決策語句if

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十一章:流程控制語句中的決策語句if 下一章 “全棧2019”Java第二十二章:控制流

“全棧2019”Java第二控制流程語句中的決策語句if-else

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十二章:控制流程語句中的決策語句if-else 下一章 “全棧2019”Java第二十三

“全棧2019”Java第二流程控制語句中迴圈語句do-while

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十六章:流程控制語句中迴圈語句do-while 下一章 “全棧2019”Java第二十七

“全棧2019”Java第二陣列詳(上篇)

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十八章:陣列詳解(上篇) 下一章 “全棧2019”Java第二十九章:陣列詳解(中篇)

“全棧2019”Java第二陣列詳(中篇)

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十九章:陣列詳解(中篇) 下一章 “全