Java Learn Note

# Java 筆記部分

需要注意的點: 源碼、中間件、JVM、數據庫、微服務、分佈式、JUCIO、操作系統、RPC、調優、算法、redis、設計模式、編譯原理

代碼優化

if...else的優化

  • Function 函數式介面
    • Supplier 供給型函數
    • Consumer消費型函數
    • Runnable無參無返回型函數
    • Function函數的表現形式為接收一個參數,並返回一個值。SupplierConsumerRunnable可以看作Function的一種特殊表現形式
  • 使用小技巧
    • 處理拋出異常的if
    • 處理if分支操作
    • 如果存在值執行消費操作,否則執行基於空的操作
Function 函數式介面

使用注解@FunctionalInterface標識,並且只包含一個抽象方法的介面是函數式介面。函數式介面主要分為Supplier供給型函數、Consumer消費型函數、Runnable無參無返回型函數和Function有參有返回型函數。

使用小技巧
處理拋出異常
  • 定義函數

定義一個拋出異常的形式的函數式介面, 這個介面只有參數沒有返回值是個消費型介面

@FunctionalInterface
public interface ThrowExceptionFunction {
    void throwMessage(String message);
}
  • 編寫判斷方法
    創建工具類VUtils並創建一個isTure方法,方法的返回值為剛才定義的函數式介面-ThrowExceptionFunctionThrowExceptionFunction的介面實現邏輯為當參數btrue時拋出異常
// In VUtils
public static ThrowExceptionFunction isTrue(boolean b){

    return (errorMessage) -> {
        if (b){
            throw new RuntimeException(errorMessage);
        }
    };
}
  • 使用方式
    調用工具類參數參數後,調用函數式介面的throwMessage方法傳入異常資訊。當出入的參數為false時正常執行
@Test
void isTrue() {
	VUtils.isTure(false).throwMessage("拋出的異常"); // 當爲true,正確拋出異常
}
處理if分支操作
  • 定義函數式介面
    創建一個名為BranchHandle的函數式介面,介面的參數為兩個Runnable介面。這兩個兩個Runnable介面分別代表了為truefalse時要進行的操作
@FunctionalInterface
public interface BranchHandle {
    void trueOrFalseHandle(Runnable trueHandle, Runnable falseHandle);
}
  • 編寫判斷方法
    創建一個名為isTureOrFalse的方法,方法的返回值為剛才定義的函數式介面-BranchHandle
public static BranchHandle isTureOrFalse(boolean b){  
    return (trueHandle, falseHandle) -> {
        if (b){
            trueHandle.run();
        } else {
            falseHandle.run();
        }
    };
}
  • 使用方式
    參數為true時,執行trueHandle
@Test
void isTrueOrFalse() {
	VUtils.isTrueOrFalse(true).tureOrFalseHanle(
	() -> {
		System.out.println("This is True")
	},
	() -> {
		System.out.println("This is False")
	});
}
如果存在值執行消費操作,否則執行基於空的操作
  • 定義函數
    創建一個名為PresentOrElseHandler的函數式介面,介面的參數一個為Consumer介面。一個為Runnable,分別代表值不為空時執行消費操作和值為空時執行的其他操作
public interface PresentOrElseHandler<T extends Object> {
   void presentOrElseHandle(Consumer<? super T> action, Runnable emptyAction);
}
  • 編寫判斷方法
    創建一個名為isBlankOrNoBlank的方法,方法的返回值為剛才定義的函數式介面-PresentOrElseHandler
public static PresentOrElseHandler<?> isBlankOrNoBlank(String str){
    return (consumer, runnable) -> {
        if (str == null || str.length() == 0){
            runnable.run();
        } else {
            consumer.accept(str);
        }
    };
}
  • 使用方式
    調用工具類參數參數後,調用函數式介面的presentOrElseHandle方法傳入一個ConsumerRunnable

參數不為空時,列印參數

@Test
void isBlankOrNoBlank() {
	VUtils.isBlankOrNoBlank("Hello")
	.presentOrElseHandle(
	System.out::println, 
	() -> {
		System.out.println("空字元串");
	});
}

Spring 部分

Spring 基礎

Spring 的奇技淫巧

嘗試提高 Spring 的吞吐量

非同步執行

實現方式二種:

  1. 使用非同步注解@aysnc、啟動類:添加@EnableAsync注解

  2. JDK 8本身有一個非常好用的Future類——CompletableFuture

@AllArgsConstructor
public class AskThread implements Runnable{
    private CompletableFuture<Integer> re = null;

    public void run() {
        int myRe = 0;
        try {
            myRe = re.get() * re.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(myRe);
    }

    public static void main(String[] args) throws InterruptedException {
        final CompletableFuture<Integer> future = new CompletableFuture<>();
        new Thread(new AskThread(future)).start();
        //模擬長時間的計算過程
        Thread.sleep(1000);
        //告知完成結果
        future.complete(60);
    }
}

在該示例中,啟動一個線程,此時AskThread對象還沒有拿到它需要的數據,執行到 myRe = re.get() * re.get()會阻塞。我們用休眠1秒來模擬一個長時間的計算過程,並將計算結果告訴future執行結果,AskThread線程將會繼續執行。

public class Calc {
    public static Integer calc(Integer para) {
        try {
            //模擬一個長時間的執行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return para * para;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        final CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> calc(50))
                .thenApply((i) -> Integer.toString(i))
                .thenApply((str) -> "\"" + str + "\"")
                .thenAccept(System.out::println);
        future.get();
    }
}

CompletableFuture.supplyAsync方法構造一個CompletableFuture實例,在supplyAsync()方法中,它會在一個新線程中,執行傳入的參數。在這裏它會執行calc()方法,這個方法可能是比較慢的,但這並不影響CompletableFuture實例的構造速度,supplyAsync()會立即返回。

而返回的CompletableFuture實例就可以作為這次調用的契約,在將來任何場合,用於獲得最終的計算結果。

supplyAsync用於提供返回值的情況,CompletableFuture還有一個不需要返回值的非同步調用方法runAsync(Runnable runnable),一般我們在優化Controller時,使用這個方法比較多。這兩個方法如果在不指定線程池的情況下,都是在ForkJoinPool.common線程池中執行,而這個線程池中的所有線程都是Daemon(守護)線程,所以,當主線程結束時,這些線程無論執行完畢都會退出系統。

核心代碼

CompletableFuture.runAsync(() ->
   this.afterBetProcessor(betRequest,betDetailResult,appUser,id)
);

非同步調用使用Callable來實現

@RestController  
public class HelloController {
  
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
      
    @Autowired  
    private HelloService hello;
  
    @GetMapping("/helloworld")
    public String helloWorldController() {
        return hello.sayHello();
    }
  
    /**
     * 非同步調用restful
     * 當controller返回值是Callable的時候,springmvc就會啟動一個線程將Callable交給TaskExecutor去處理
     * 然後DispatcherServlet還有所有的spring攔截器都退出主線程,然後把response保持打開的狀態
     * 當Callable執行結束之後,springmvc就會重新啟動分配一個request請求,然後DispatcherServlet就重新
     * 調用和處理Callable非同步執行的返回結果, 然後返回視圖
     *
     * @return
     */  
    @GetMapping("/hello")
    public Callable<String> helloController() {
        logger.info(Thread.currentThread().getName() + " 進入helloController方法");
        Callable<String> callable = new Callable<String>() {
  
            @Override  
            public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " 進入call方法");
                String say = hello.sayHello();
                logger.info(Thread.currentThread().getName() + " 從helloService方法返回");
                return say;
            }
        };
        logger.info(Thread.currentThread().getName() + " 從helloController方法返回");
        return callable;
    }
}

非同步調用的方式 WebAsyncTask

@RestController  
public class HelloController {
  
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
      
    @Autowired  
    private HelloService hello;
  
        /**
     * 帶超時時間的非同步請求 通過WebAsyncTask自定義客戶端超時間
     *
     * @return
     */  
    @GetMapping("/world")
    public WebAsyncTask<String> worldController() {
        logger.info(Thread.currentThread().getName() + " 進入helloController方法");
  
        // 3s鐘沒返回,則認為超時
        WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, new Callable<String>() {
  
            @Override  
            public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " 進入call方法");
                String say = hello.sayHello();
                logger.info(Thread.currentThread().getName() + " 從helloService方法返回");
                return say;
            }
        });
        logger.info(Thread.currentThread().getName() + " 從helloController方法返回");
  
        webAsyncTask.onCompletion(new Runnable() {
  
            @Override  
            public void run() {
                logger.info(Thread.currentThread().getName() + " 執行完畢");
            }
        });
  
        webAsyncTask.onTimeout(new Callable<String>() {
  
            @Override  
            public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " onTimeout");
                // 超時的時候,直接拋異常,讓外層統一處理超時異常
                throw new TimeoutException("調用超時");
            }
        });
        return webAsyncTask;
    }
  
    /**
     * 非同步調用,異常處理,詳細的處理流程見MyExceptionHandler類
     *
     * @return
     */  
    @GetMapping("/exception")
    public WebAsyncTask<String> exceptionController() {
        logger.info(Thread.currentThread().getName() + " 進入helloController方法");
        Callable<String> callable = new Callable<String>() {
  
            @Override  
            public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " 進入call方法");
                throw new TimeoutException("調用超時!");
            }
        };
        logger.info(Thread.currentThread().getName() + " 從helloController方法返回");
        return new WebAsyncTask<>(20000, callable);
    }
  
}
增加內嵌Tomcat的最大連接數
@Configuration
public class TomcatConfig {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory();
        tomcatFactory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
        tomcatFactory.setPort(8005);
        tomcatFactory.setContextPath("/api-g");
        return tomcatFactory;
    }
    class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
        public void customize(Connector connector) {
            Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
            //設置最大連接數
            protocol.setMaxConnections(20000);
            //設置最大線程數
            protocol.setMaxThreads(2000);
            protocol.setConnectionTimeout(30000);
        }
    }

}
使用@ComponentScan()定位掃包比@SpringBootApplication掃包更快
默認tomcat容器改為Undertow(Jboss下的伺服器,Tomcat吞吐量5000,Undertow吞吐量8000)
<exclusions>
  <exclusion>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-tomcat</artifactId>
  </exclusion>
</exclusions>

改為

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
使用 BufferedWriter 進行緩衝
Deferred方式實現非同步實現非同步調用
@RestController
public class AsyncDeferredController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final LongTimeTask taskService;
    
    @Autowired
    public AsyncDeferredController(LongTimeTask taskService) {
        this.taskService = taskService;
    }
    
    @GetMapping("/deferred")
    public DeferredResult<String> executeSlowTask() {
        logger.info(Thread.currentThread().getName() + "進入executeSlowTask方法");
        DeferredResult<String> deferredResult = new DeferredResult<>();
        // 調用長時間執行任務
        taskService.execute(deferredResult);
        // 當長時間任務中使用deferred.setResult("world");這個方法時,會從長時間任務中返回,繼續controller裏面的流程
        logger.info(Thread.currentThread().getName() + "從executeSlowTask方法返回");
        // 超時的回調方法
        deferredResult.onTimeout(new Runnable(){
  
   @Override
   public void run() {
    logger.info(Thread.currentThread().getName() + " onTimeout");
    // 返回超時資訊
    deferredResult.setErrorResult("time out!");
   }
  });
        
        // 處理完成的回調方法,無論是超時還是處理成功,都會進入這個回調方法
        deferredResult.onCompletion(new Runnable(){
  
   @Override
   public void run() {
    logger.info(Thread.currentThread().getName() + " onCompletion");
   }
  });
        
        return deferredResult;
    }
}

非同步調用可以使用AsyncHandlerInterceptor進行攔截
@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
 
 private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
 
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
   throws Exception {
  return true;
 }
 
 @Override
 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
   ModelAndView modelAndView) throws Exception {
// HandlerMethod handlerMethod = (HandlerMethod) handler;
  logger.info(Thread.currentThread().getName()+ "服務調用完成,返回結果給客戶端");
 }
 
 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
   throws Exception {
  if(null != ex){
   System.out.println("發生異常:"+ex.getMessage());
  }
 }
 
 @Override
 public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
   throws Exception {
  
  // 攔截之後,重新寫回數據,將原來的hello world換成如下字串
  String resp = "my name is chhliu!";
  response.setContentLength(resp.length());
  response.getOutputStream().write(resp.getBytes());
  
  logger.info(Thread.currentThread().getName() + " 進入afterConcurrentHandlingStarted方法");
 }
 
}

Spring 源碼部分

Spring Internal Code

Bean 的生命周期

Bean 生命周期

PostProcesser

package io.github.andre_hjr;

public class MySelfBeanFactoryPostProcessor
	implements BeanFactoryPostProcessor
{
	System.out.println("调用自定义的beanFactoryPostProcessor");
}

**在相應的xml文件中有如下創建:

<bean
<!-- tx.xml 的部分文件 -->
class="io.github.andre_hjr.MySelfBeanFactoryPostProcessor"></bean>

調用相關的測試代碼

public class TxTest {
	public static void main(String[] args) {
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "\//path");
		ApplicationContext context = new ClassPathXmlApplicationContext("tx.xml");
		B
	}
}

對象的創建

how to create object

三級緩存

一些問題
  • 三級緩存中分別保存的是什麼對象
    • 成品對象
    • 半成品對象
    • lambda運算式
  • 如果只使用1級緩存行不行?
    不行,因為成品和半成品對象會放到一起,在進行對象獲取的時候有可能獲取到半成品對象,這樣的對象是沒法使用的
  • 如果只有二級緩存行不行?
    • getSingleton
    • doCreateBean
    • 只有二級緩存的時候也可以解決迴圈依賴的問題
    • 添加aop的實現之後,報錯,Thismeans that said other beans do not use the final version of the bean.
  • 三級緩存存在到底做了什麼事?
    • 如果一個對象需要被代理,生成代理對象,那麼這個對象需要預先生成非代理對象嗎?
    • 需要三級緩存到底做了什麼?
      • lambda:getEarlyBeanReference()只要搞清楚這個方法的具體執行邏輯即可
      • 在當前方法中,有可能會用代理對象替換非代理對象,如果沒有三級緩存的話,那麼就無法得到代理對象,換句話說
      • 在整個容器中,包含了同名對象的代理對象和非代理對象,你覺得可以嗎?
      • 容器中,對象都是單例的,意味著根據名稱只能獲取一個對象的值,此時同時存在兩個對象的話,使用的時候應該取哪一個?無法判斷
      • 誰也無法確認什麼時候會調用當前對象,是在其他對象的執行過程中來進行調用的,而不是人為指定的,所以必須要保證容器中任何時候都只有一個對象供外部調用,所以在三級緩存中,完整了一件代理對象替換非代理對象的工作,確定返回的是唯一的對象
  • 三級緩存是為了解決在aop代理過程中產生的迴圈依賴問題,如果沒有aop的話,二級緩存足矣解決迴圈依賴問題
    • 其實相當於是一個回調機制,當我都需要使用當前對象的時候,會判斷此對象是否需要被代理實現,如果直接替換,不需要直接返回非代理對象即可
  • spring是一個框架,跟業務完全無關,框架如何知道你什麼時候用aop,什麼時候不用aop呢?
    但是在調用時候的時候,如果出現此問題怎麼辦呢?最重要的一件事,走總體的流程,如果有,就處理,沒有就直接返回,不會有額外的任何影響

Spring-Boot 源碼部分

自動裝配實現的原理:

  1. 當啟動spring boot應用程式的時候, 會先創建SpringApplication的對象, 在對象的構造方法中會進行某些參數的初始化工作,最主要的是判斷當前應用程式的類型以及初始化器和監聽器,在這個過程中會加載整個應用程式中的spring.factories檔, 將檔的內容放到緩存對象中, 方便後續獲取。
  2. SpringApplication對象創建完成之後, 開始執行run方法, 來完成整個啟動, 啟動過程中最主要的有兩個方法, 第一個叫做prepareContext, 第二個叫做refreshContext, 在這兩個關鍵步驟中完整了自動裝配的核心功能, 前面的處理邏輯包含了上下文對象的創建, banner的列印, 異常報告期的準備等各個準備工作, 方便後續來進行調用。
  3. prepareContext方法中主要完成的是對上下文對象的初始化操作, 包括了屬性值的設置, 比如環境對象, 在整個過程中有一個非常重要的方法, 叫做loadload主要完成一件事, 將當前啟動類做為一個beanDefinition註冊到registry中, 方便後續在進行BeanFactoryPostProcessor調用執行的時候, 找到對應的主類, 來完成@SpringBootApplicaiton@EnableAutoConfiguration等注解的解析工作
  4. refreshContext方法中會進行整個容器刷新過程, 會調用中spring中的refresh方法, refresh中有13個非常關鍵的方法, 來完成整個spring應用程式的啟動, 在自動裝配過程中, 會調用invokeBeanFactoryPostProcessor方法, 在此方法中主要是對ConfigurationClassPostProcessor類的處理, 這次是BFPP的子類也是BDRPP的子類, 在調用的時候會先調用BDRPP中的postProcessBeanDefinitionRegistry方法, 然後調用postProcessBeanFactory方法, 在執行postProcessBeanDefinitionRegistry的時候回解析處理各種注解, 包含@PropertySource@ComponentScan@ComponentScans@Bean@lmport等注解, 最主要的是@lmport注解的解析
  5. 在解析@Import注解的時候, 會有一個getImports的方法, 從主類開始遞歸解析注解, 把所有包含@lmport的注解都解析到, 然後在processlmport方法中對Import的類進行分類, 此處主要識別的時候AutoConfigurationImportSelect歸屬於ImportSelect的子類, 在後續過程中會調用deferredImportSelectorHandler中的process方法, 來完整EnableAutoConfiguration的加載。

Kafka 部分

簡介

Apache Kafka 是一個分佈式的發佈-訂閲消息系統,能夠支撐海量數據傳遞,在離綫和實時的消息處理業務系統中,Kafka都有廣汎的應用,Kafka將消息持久化到磁盤中,並對消息創建了備份保證數據的安全,Kafka在保證了較高的處理速度的同時,又能保證數據處理的低延遲和數據的零丟失。

特點

  • 高吞吐量、低延遲: Kafka每秒可以處理幾十萬條消息,它的延遲最低只有几毫秒,每個主題可以分爲多個分區,消費組對分區進行消費操作;
  • 可拓展性: Kafka集群支持熱擴展
  • 持久性、可靠性:消息被持久化到本地磁盤,并且支持數據備份、從而防止數據丟失;
  • 容錯性:允許集群中節點失敗(若副本數量為n,則允許n - 1個節點失敗);
  • 高并發:支持數簽個客戶端同時讀寫

使用場景

  • 日誌收集:一個公司可以用Kafka可以收集各種服務的log,通過kafka以統一介面服務的方式開放給各種consumer.例如Hadoop. HbaseSolr等:
  • 消息系統:解耦和生產者和消費者、緩存消息等;
  • 用戶活動跟蹤: Kafka經常被用來記錄web用戶或者app用戶的各種活動,如流覽網頁、搜索、點擊等活動,這些活動資訊被各個伺服器發佈到kafka的topic中,然後訂閱者通過訂閱這些topic來做即時的監控分析,或者裝載到Hadoop、數據倉庫中做離線分析和挖掘;
  • 運營指標: Kafka也經常用來記錄運營監控數據。包括收集各種分佈式應用的數據,生產各種操作的集中回饋,比如報警和報告;
  • 流式處理:比如spark streamingstorm;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值