1. 程式人生 > >SpringBoot學習筆記2 - 20181128

SpringBoot學習筆記2 - 20181128

一.Springboot常見錯誤總結

  1. 控制檯亂碼問題(外掛啟動,控制檯亂碼問題)
    在pom檔案,springboot外掛啟動中,增加如下(一些虛擬機器方法)
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <!-- spring-boot:run 中文亂碼解決 -->
    <configuration>
<fork>true</fork> <!--增加jvm引數--> <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments> </configuration> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId
>
springloaded</artifactId> <version>1.2.5.RELEASE</version> </dependency> </dependencies> </plugin>
  1. 無法自動注入問題
    ①.包掃描錯誤,@MapperScan指定錯誤 或 @ComponentScan入口類位置錯誤
    ②.Mapper檔案錯誤,dao的實現類無法建立,導致無法注入
  2. yml檔案格式錯誤
  3. springboot的pom檔案中引入多餘jar包
  4. springboot的版本問題(springboot 2.0 使用 spring 5.x)

二.Springboot自帶日誌

  • 預設使用 logback,相比log4j更輕量級,無需引入依賴或jar
  • 匯入logback.xml 定製顯示資訊
  • 日誌 (面試關鍵點)
  1. 級別

OFF > FATAL> ERROR > WARN > INFO > DEBUG > TRACE > ALL

  • OFF Level是最高等級的,用於關閉所有日誌記錄。

  • FATAL Level指出每個嚴重的錯誤事件將會導致應用程式的退出。

指出每個嚴重的錯誤事件將會導致應用程式的退出。這個級別比較高了。重大錯誤,這種級別你可以直接停止程式了。

  • ERROR Level指出雖然發生錯誤事件,但仍然不影響系統的繼續執行。

指出雖然發生錯誤事件,但仍然不影響系統的繼續執行。列印錯誤和異常資訊,如果不想輸出太多的日誌,可以使用這個級別。

  • WARN Level表明會出現潛在錯誤的情形。

  • INFO Level表明 訊息在粗粒度級別上突出強調應用程式的執行過程。

訊息在粗粒度級別上突出強調應用程式的執行過程。列印一些你感興趣的或者重要的資訊,這個可以用於生產環境中輸出程式執行的一些重要資訊,但是不能濫用,避免列印過多的日誌。

  • DEBUG Level指出細粒度資訊事件對除錯應用程式是非常有幫助的。

  • TRACE Level較低的日誌級別,一般不會使用。

  • ALL Level是最低等級的,用於開啟所有日誌記錄。

ps:log4j建議只使用四個級別

  1. 兩個種類
    ①.根日誌:專案級別日誌,專案中所有指定的資訊都會被日誌展示出來。
    ②.子日誌:包級別/類級別的日誌,獲取指定包或類的日誌資訊。
  • logback日誌框架使用
    logback.xml (resources根目錄下)
<?xml version="1.0" encoding="UTF-8" ?>
<!--配置項-->
<configuration>
    <!-- appender:日誌如何做展示的配置 name:展示配置的別名 class:日誌如何做展示-->
    <appender name="out" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 展示佈局 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern> [%p] %d{yyyy-MM-dd HH:mm:ss} %m %n</pattern>
        </layout>
    </appender>

    <!-- 根日誌 -->
    <root level="ERROR">
        <appender-ref ref="out"/>
    </root>
    
    <!-- 子日誌 使用根日誌的展示配置-->
    <logger name="com.abc.dao" level="DEBUG"/>
</configuration>

三.Springboot的熱部署

  • 熱部署是什麼?
    大家都知道在專案開發過程中,常常會改動頁面資料或者修改資料結構,為了顯示改動效果,往往需要重啟應用檢視改變效果,其實就是重新編譯生成了新的 Class 檔案,這個檔案裡記錄著和程式碼等對應的各種資訊,然後 Class 檔案將被虛擬機器的 ClassLoader 載入。
    而熱部署正是利用了這個特點,它監聽到如果有 Class 檔案改動了,就會建立一個新的 ClaassLoader 進行載入該檔案,經過一系列的過程,最終將結果呈現在我們眼前。
  1. 使用 Spring Loaded
  2. 使用 spring-boot-devtools
  3. jrebel外掛
    要求:springboot專案,電腦配置好
    原理:
    java編譯,使用類載入器,編譯成class檔案
    jrebel使用兩個類載入器,一個在檔案執行時生效,一個在程式碼變動時類載入器生效

四.Springboot中面向切面程式設計(AOP)

  • 回顧spring基於註解的aop開發(spring 4 提出概念,純註解開發)
  1. 引入aop註解依賴
		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.7.1</version>
		</dependency>
  1. 通知類上加上@Aspect,指定類為通知類
    @Around(execution(* com.baizhi.service..(…))) 指定方法為環繞通知方法
  • springboot基於註解的aop開發
  1. 引入依賴
	<!-- Springboot的AOP支援 -->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
  1. 開發通知類
    常用註解:
@Configuration
@Component

@Aspect	//作用是把當前類標識為一個切面供容器讀取
@Before	//標識一個前置增強方法,相當於BeforeAdvice的功能
@After	//final增強,不管是丟擲異常或者正常退出都會執行
@Around	//環繞增強,相當於MethodInterceptor

@AfterReturning	//後置增強,相當於AfterReturningAdvice,方法退出時執行
@AfterThrowing	//異常丟擲增強,相當於ThrowsAdvice

示例程式碼:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//使用在哪裡:方法生效
@Target({ElementType.METHOD})
//什麼時候生效
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectAnnotion {	//自定義註解
    public String value() default "no search method~~~";

}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

//@Component
@Configuration  //開啟Spring相關自動配置
@Aspect         //指定類為通知類
public class AspectDemo {

    @Pointcut("execution(public * com.abc.service.*.*(..))")
    public void pointCut(){
    }

    /**
     * @Before 前置通知
     * @param joinPoint
     */
    @Before("execution(* com.abc.service.*.*(..))")
    public void before(JoinPoint joinPoint){
        System.out.println(joinPoint.getArgs());    //獲取引數
        System.out.println(joinPoint.getTarget());  //獲取目標物件
        System.out.println(joinPoint.getSignature().getName()); //獲取方法名
        System.out.println("------------this is before-------------");
    }

    /**
     * @Around 環繞通知
     * @param proceedingJoinPoint
     * @return
     */
    @Around(value = "@annotation(com.baizhi.aspect.AspectAnnotion)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        //獲取方法上註解
        //拿到方法method
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        //拿到類物件 反射
        AspectAnnotion aspectAnnotion = method.getAnnotation(AspectAnnotion.class);
        //通過反射拿到例項 呼叫方法 拿到自定義的方法名
        String name = aspectAnnotion.value();

        System.out.println(proceedingJoinPoint.getArgs());    //同joinPoint

        try {
            //前置

            Object proceed = proceedingJoinPoint.proceed();     //放行目標方法

            //後置

            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }

    /**
     * @After 後置通知
     * @param joinPoint
     */
    @After("pointCut()")
    public void after(JoinPoint joinPoint) {
    }
}

Proceedingjoinpoint 和 JoinPoint 的關係?
環繞通知=前置+目標方法執行+後置通知,proceed方法就是用於啟動目標方法執行的。

環繞通知 ProceedingJoinPoint 執行proceed方法的作用是讓目標方法執行,這也是環繞通知和前置、後置通知方法的一個最大區別。

Proceedingjoinpoint 繼承了 JoinPoint 。是在JoinPoint的基礎上暴露出 proceed 這個方法。proceed很重要,這個是aop代理鏈執行的方法。

圖2
暴露出這個方法,就能支援 aop:around 這種切面(而其他的幾種切面只需要用到JoinPoint,這跟切面型別有關), 能決定是否走代理鏈還是走自己攔截的其他邏輯。建議看一下JdkDynamicAopProxy的invoke方法,瞭解一下代理鏈的執行原理。

五.Springboot中攔截器的實現

  • 回顧springMVC中如何實現攔截器
  1. 實現HandlerInterceptor介面,覆蓋preHandle(進入前)、postHandle(跳轉前)、afterCompletion(跳轉後)
  2. springmvc.xml中配置
		<!--配置攔截器示例 student-->
		<mvc:interceptors>
			<mvc:interceptor>
				<mvc:mapping path="/**"/>
				<mvc:exclude-mapping path="/user/*"/>
				<mvc:exclude-mapping path="/vcode/*"/>
				<bean class="com.abc.interceptor.InterceptorController"/>
			</mvc:interceptor>
		</mvc:interceptors>
  • springboot中實現攔截器
  1. 實現一個自定義攔截器
  2. 書寫配置類 extends WebMvcConfigurerAdapter
@EnableWebMvc
@Configuration	//或者 @SpringBootConfiguration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
	//類似DI注入
    @Bean
    InterceptorController localInterceptor() {
        return new InterceptorController();
    }
    //覆蓋springboot中的預設攔截器方法
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localInterceptor())
        		.addPathPatterns("/menu/*")		//攔截哪些
                .excludePathPatterns("/user/*","/vcode/*");	 //不攔截哪些
        
        super.addInterceptors(registry);
    }
}

注意:存在靜態資源丟失問題。。。

六.Springboot中上傳下載

  • 在yml檔案中,修改上傳檔案大小限制
spring:
   http:
    multipart:
      enabled: true
      max-file-size: 30MB      #上傳檔案限制大小
      max-request-size: 30MB  #限制檔案大小
  • 上傳幾乎沒有什麼變化
  • 下載,使用springboot內建FileCopyUtils.copy 方式
@RequestMapping("/download")
    @ResponseBody
    public void download(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
        //獲取檔案路徑
        String realPath = request.getSession().getServletContext().getRealPath("/upload");
        //找到檔案,獲取輸入流
        File file = new File(realPath, filename);
        FileInputStream fileInputStream = new FileInputStream(file);
        //獲取響應型別
        String substring = filename.substring(filename.lastIndexOf("."));
        //設定響應型別
        response.setContentType(substring);
        //設定響應頭
        response.setHeader("Content-Disposition", "attachment;fileName=" + filename);
        //獲取輸出流
        ServletOutputStream outputStream = response.getOutputStream();
        //檔案Copy
        FileCopyUtils.copy(fileInputStream,outputStream);
    }

補充.我的後期專案.yml

server:
  port: 9090
  context-path: /cmfz
  jsp-servlet:
    init-parameters:
      development: true #開啟jsp的熱部署配置
  tomcat:
    max-http-post-size: -1

spring:
  mvc:
    view:
      prefix: /
      suffix: .jsp
  profiles:
    active: dev #啟用哪個檔案生效
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #指定資料來源
    driver-class-name: com.mysql.jdbc.Driver #指定驅動
    url: jdbc:mysql://localhost:3306/abc?characterEncoding=UTF-8 #指定url
    username: root
    password: root
  jackson:
    date-format: "yyyy-MM-dd" #適用responseBody時,設定全域性日期格式
    time-zone: "GMT+08" #設定時區
  http:
    multipart:
      enabled: true
      max-file-size: 30MB      #上傳檔案限制大小
      max-request-size: 30MB  #限制檔案大小

mybatis:
  mapper-locations: classpath:com/baizhi/mapper/*Mapper.xml #指定mapper配置檔案位置
  type-aliases-package: com.abc.pojo #指定起別名的包

資料來源測試

	@Autowired
    DataSourceProperties dataSourceProperties;
    @Autowired
    ApplicationContext applicationContext;

    @Test
    public void testttt() throws SQLException {
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        System.out.println(dataSource.getConnection());
    }