一步一步學spring boot

竹林聽雨行發表於2018-12-15

Spring boot實現監聽器

package com.example.demo.listener.test;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * 使用@WebListener註解,實現ServletContextListener介面
 * 在啟動器處加註解@ServletComponentScan
 * @author cxx
 */
@WebListener
public class MyServletContextListener implements ServletContextListener{
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext初始化");
        System.out.println(servletContextEvent.getServletContext().getServerInfo());
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext銷燬");
    }
}

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@MapperScan(value = "com.example.demo.mapper")
//filter和listener監聽器註解需要用的
@ServletComponentScan
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}
複製程式碼

Spring Boot實現過濾器

第一步:定義MyFilter過濾器

package com.example.demo.filter.test;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * 使用註解過濾器
 * @WebFilter將一個實現了import javax.servlet.Filter介面的類定義為過濾器
 * 屬性filterName生命過濾器的名稱,可選
 * 屬性urlPatterns指定要過濾的URL模式,也可使用屬性value來宣告(指定要過濾的URL是必選屬性)
 */
@WebFilter(filterName = "MyFilter",urlPatterns = "/*")
public class MyFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("過濾器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("執行過濾操作");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("過濾器銷燬");
    }
}
複製程式碼

第二部:在入口處新增@ServletComponentScan註解

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@MapperScan(value = "com.example.demo.mapper")
//filter和listener監聽器註解需要用的
@ServletComponentScan
public class DemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}
複製程式碼

Spring Boot全域性異常處理

全域性異常處理:@ControllerAdvice:包含@Componet,可以被掃描到

統一異常處理:@ExceptionHandler(Exception.class):用在方法上面表示遇到這個異常就執行以下方法。

package com.example.demo.global.exception.test;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ExceptionTestController {
    @RequestMapping(value = "ExceptionTest",method = RequestMethod.GET)
    public String index(){
        String str=null;
        str.split("1");
        return "ExceptionTestController";
    }
}

package com.example.demo.global.exception.test;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@ControllerAdvice
//如果返回的為json資料或其他物件,新增該註解
@ResponseBody
public class GlobalDefaultExceptionHandler {
    public static final String DEFAULT_ERROR_VIEW="error";
    @ExceptionHandler({NullPointerException.class,NumberFormatException.class})
    public ModelAndView formatErrorHandler(HttpServletRequest request,Exception ex){
        System.out.println("已經捕獲到異常");
        ModelAndView mv=new ModelAndView();
        mv.addObject("exception",ex);
        mv.addObject("timeStamp",new Date());
        mv.setViewName(DEFAULT_ERROR_VIEW);
        return mv;
    }
}
複製程式碼

幾款好用的外掛

1、FindBugs 查詢潛在bug

2、Maven help分析pom檔案的依賴情況(方便解決依賴版本的衝突問題)

配置檔案

1、配置檔案

SpringBoot使用一個全域性的配置檔案,配置檔名是固定的;

•application.properties

•application.yml

配置檔案的作用:修改SpringBoot自動配置的預設值;SpringBoot在底層都給我們自動配置好;

標記語言:

以前的配置檔案;大多都使用的是  xxxx.xml檔案;

YAML:以資料為中心,比json、xml等更適合做配置檔案;

YAML:配置例子

server:
  port: 8081

XML:

<server>
	<port>8081</port>
</server>
複製程式碼

2、YAML語法:

1、基本語法

k:(空格)v:表示一對鍵值對(空格必須有);

以空格的縮排來控制層級關係;只要是左對齊的一列資料,都是同一個層級的

server:
    port: 8081
    path: /hello
複製程式碼

屬性和值也是大小寫敏感;

2、值的寫法

字面量:普通的值(數字,字串,布林)

k: v:字面直接來寫;

	字串預設不用加上單引號或者雙引號;

	"":雙引號;不會轉義字串裡面的特殊字元;特殊字元會作為本身想表示的意思

			name:   "zhangsan \n lisi":輸出;zhangsan 換行  lisi

	'':單引號;會轉義特殊字元,特殊字元最終只是一個普通的字串資料

			name:   ‘zhangsan \n lisi’:輸出;zhangsan \n  lisi
複製程式碼

物件、Map(屬性和值)(鍵值對):

k: v:在下一行來寫物件的屬性和值的關係;注意縮排

	物件還是k: v的方式

friends:
		lastName: zhangsan
		age: 20
複製程式碼

行內寫法:

friends: {lastName: zhangsan,age: 18}
複製程式碼

陣列(List、Set):

用- 值表示陣列中的一個元素

pets:
 - cat
 - dog
 - pig
複製程式碼

行內寫法

pets: [cat,dog,pig]
複製程式碼

3、配置檔案值注入

配置檔案

person:
    lastName: hello
    age: 18
    boss: false
    birth: 2017/12/12
    maps: {k1: v1,k2: 12}
    lists:
      - lisi
      - zhaoliu
    dog:
      name: 小狗
      age: 12
複製程式碼

javaBean:

/**
 * 將配置檔案中配置的每一個屬性的值,對映到這個元件中
 * @ConfigurationProperties:告訴SpringBoot將本類中的所有屬性和配置檔案中相關的配置進行繫結;
 *      prefix = "person":配置檔案中哪個下面的所有屬性進行一一對映
 *
 * 只有這個元件是容器中的元件,才能容器提供的@ConfigurationProperties功能;
 *
 */
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;

    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
複製程式碼

Controller

@RestController
public class PersonController {
    @Autowired
    private Person person;
    @GetMapping(value = "GetPersonInfo")
    public String GetPersonInfo(){
        return person.toString();
    }
}
複製程式碼

我們可以匯入配置檔案處理器,以後編寫配置就有提示了

<!--匯入配置檔案處理器,配置檔案進行繫結就會有提示-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
複製程式碼

1、properties配置檔案在idea中預設utf-8可能會亂碼

調整

2、@Value獲取值和@ConfigurationProperties獲取值比較

        	@ConfigurationProperties	@Value
複製程式碼

功能 批量注入配置檔案中的屬性 一個個指定 鬆散繫結(鬆散語法) 支援 不支援
SpEL 不支援 支援
JSR303資料校驗 支援 不支援
複雜型別封裝 支援 不支援

配置檔案yml還是properties他們都能獲取到值;

如果說,我們只是在某個業務邏輯中需要獲取一下配置檔案中的某項值,使用@Value;

如果說,我們專門編寫了一個javaBean來和配置檔案進行對映,我們就直接使用@ConfigurationProperties;

3、配置檔案注入值資料校驗

@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {

    /**
     * <bean class="Person">
     *      <property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property>
     * <bean/>
     */

   //lastName必須是郵箱格式
    @Email
    //@Value("${person.last-name}")
    private String lastName;
    //@Value("#{11*2}")
    private Integer age;
    //@Value("true")
    private Boolean boss;

    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
複製程式碼

4、@PropertySource

@PropertySource:載入指定的配置檔案;

/**
 * 將配置檔案中配置的每一個屬性的值,對映到這個元件中
 * @ConfigurationProperties:告訴SpringBoot將本類中的所有屬性和配置檔案中相關的配置進行繫結;
 *      prefix = "person":配置檔案中哪個下面的所有屬性進行一一對映
 *
 * 只有這個元件是容器中的元件,才能容器提供的@ConfigurationProperties功能;
 *  @ConfigurationProperties(prefix = "person")預設從全域性配置檔案中獲取值;
 *
 */
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
//@Validated
public class Person {

    /**
     * <bean class="Person">
     *      <property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property>
     * <bean/>
     */

   //lastName必須是郵箱格式
   // @Email
    //@Value("${person.last-name}")
    private String lastName;
    //@Value("#{11*2}")
    private Integer age;
    //@Value("true")
    private Boolean boss;
複製程式碼

注:這裡不知為何讀取不了以.yml結尾的檔案

4、配置檔案佔位符

1、隨機數

${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
複製程式碼

2、佔位符獲取之前配置的值,如果沒有可以是用:指定預設值

person.last-name=張三${random.uuid}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
person.dog.age=15
複製程式碼

5、Profile

1、多Profile檔案

我們在主配置檔案編寫的時候,檔名可以是 application-{profile}.properties/yml

預設使用application.properties的配置;

2、yml支援多文件塊方式

server:
  port: 8081
spring:
  profiles:
    active: prod

---
server:
  port: 8083
spring:
  profiles: dev


---

server:
  port: 8084
spring:
  profiles: prod  #指定屬於哪個環境
複製程式碼

3、啟用指定profile

1、在配置檔案中指定  spring.profiles.active=dev

spring:
  profiles:
    active: dev

2、命令列:

	java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;

	可以直接在測試的時候,配置傳入命令列引數

3、虛擬機器引數;

	-Dspring.profiles.active=dev
複製程式碼

6、配置檔案載入位置

springboot 啟動會掃描以下位置的application.properties或者application.yml檔案作為Spring boot的預設配置檔案

–file:./config/

–file:./

–classpath:/config/

–classpath:/

優先順序由高到底,高優先順序的配置會覆蓋低優先順序的配置;

SpringBoot會從這四個位置全部載入主配置檔案;互補配置;

基於註解實現SpringBoot多資料來源配置

1、在application.yml中新增多資料來源配置

新增多個資料來源和mapper檔案路徑配置,此配置用於基於java的配置資料來源中使用。

spring:
  datasource:
    first:
       driver-class-name: com.mysql.jdbc.Driver
       jdbc-url: jdbc:mysql://localhost:3306/db_person
       username: root
       password: root
    second:
       driver-class-name: com.mysql.jdbc.Driver
       jdbc-url: jdbc:mysql://localhost:3306/webdb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
       username: root
       password: root
  mvc:
    view:
    # 定位模板的目錄
      prefix: classpath:/templates/
      # 給返回的頁面新增字尾名
      suffix: .html
mybatis:
#對映的檔案位置
  mapper-locations: classpath:mapper/*.xml
#  對映的實體位置
  type-aliases-package: com.example.demo.entity
複製程式碼

2、基於java的方式實現資料庫配置

其中DemoUserDbConfig類原始碼如下: 其中Configuration註解表識此類為Spring的配置類。 MapperScan註解中的basePackages、annotationClass、sqlSessionTemplateRef用於配置此資料庫連結掃描com.example包中所有註解為DemoUserMapper的介面。

@Configuration
@MapperScan(basePackages = {"com.example.demo.mapper"},annotationClass = DbSourceFirst.class,
        sqlSessionTemplateRef = "DbSourceFirstTemplate")
//@Component
public class FirstDbConfig {
    @Value("${spring.datasource.first.jdbc-url}")
    private String url;

    @Value("${spring.datasource.first.username}")
    private String userName;

    @Value("${spring.datasource.first.password}")
    private String password;

    @Value("${spring.datasource.first.driver-class-name}")
    private String driveClassName;

    @Value(value = "${mybatis.mapper-locations}")
    private String mapperLocation;


    @Bean(name = "dbFirst")
    @Primary     //需加這個註解否則會報錯
    @ConfigurationProperties(prefix = "spring.datasource.first")
    public DataSource firstDataSource() {
        ///return dataSourceFactory(driveClassName, url, userName, password);
        return DataSourceBuilder.create().build();
    }

//    public DataSource dataSourceFactory(String driveClassName, String url, String userName, String password) {
//        DruidDataSource datasource = new DruidDataSource();
//        datasource.setDriverClassName(driveClassName);
//        datasource.setUrl(url);
//        datasource.setUsername(userName);
//        datasource.setPassword(password);
//        datasource.setMaxActive(20);
//        datasource.setInitialSize(20);
//        return datasource;
//    }

    @Bean(name = "DbSourceFirstTemplate")
    @Primary
    public SqlSessionTemplate dbFirstSqlTemplate() throws Exception {
        return new SqlSessionTemplate((sqlSessionFactory(firstDataSource(), mapperLocation)));
    }

    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, String mapperLocation) throws Exception{
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        Resource[] resource= resourceResolver.getResources(mapperLocation);
        factoryBean.setMapperLocations(resource);
        return factoryBean.getObject();
    }

    @Bean
    @Qualifier("dbFirstTransaction")
    public PlatformTransactionManager demoUserTransaction() {
        return new DataSourceTransactionManager(firstDataSource());
    }
}
複製程式碼

使用相同的方法定義其他資料來源。

@Configuration
@MapperScan(basePackages = {"com.example.demo.mapper2"},annotationClass = DbSourceSecond.class,
        sqlSessionTemplateRef = "DbSourceSecondTemplate")
public class SecondDbConfig {
    @Value("${spring.datasource.second.jdbc-url}")
    private String url;

    @Value("${spring.datasource.second.username}")
    private String userName;

    @Value("${spring.datasource.second.password}")
    private String password;

    @Value("${spring.datasource.second.driver-class-name}")
    private String driveClassName;

    @Value(value = "${mybatis.mapper-locations}")
    private String mapperLocation;


    @Bean(name = "dbSecond")
    @ConfigurationProperties(prefix = "spring.datasource.second")
    public DataSource secondaryDataSource() {
        //return dataSourceFactory(driveClassName, url, userName, password);
        return DataSourceBuilder.create().build();
    }

//    public DataSource dataSourceFactory(String driveClassName, String url, String userName, String password) {
//        DruidDataSource datasource = new DruidDataSource();
//        datasource.setDriverClassName(driveClassName);
//        datasource.setUrl(url);
//        datasource.setUsername(userName);
//        datasource.setPassword(password);
//        datasource.setMaxActive(20);
//        datasource.setInitialSize(20);
//        return datasource;
//    }

    @Bean(name = "DbSourceSecondTemplate")
    public SqlSessionTemplate dbSecondSqlTemplate() throws Exception {
        return new SqlSessionTemplate((sqlSessionFactory(secondaryDataSource(), mapperLocation)));
    }

    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, String mapperLocation) throws Exception{
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        Resource[] resource= resourceResolver.getResources(mapperLocation);
        factoryBean.setMapperLocations(resource);
        return factoryBean.getObject();
    }

    @Bean
    @Qualifier("dbSecondTransaction")
    public PlatformTransactionManager demoUserTransaction() {
        return new DataSourceTransactionManager(secondaryDataSource());
    }
}
複製程式碼

3、定義介面和mapper檔案

(1)、以xml配置檔案的方案讀取

@Mapper
@DbSourceSecond
public interface NewsMapper {
    public List<News> findAll();
}
複製程式碼

mapper檔案如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper2.NewsMapper">
    <resultMap id="BaseResultMap" type="com.example.demo.entity.News">
    </resultMap>
    <select id="findAll" resultMap="BaseResultMap">
        select * from webdb.news
    </select>
</mapper>
複製程式碼

(2)、以註解的方式讀取資料

@Mapper
@DbSourceSecond
public interface NewsDao {
    @SelectProvider(type = NewsProvider.class,method = "GetTop1News")
    public News GetTop1News();
}

public class NewsProvider {
    public String GetTop1News(){
        return "select  * from webdb.news limit 1 ";
    }
}
複製程式碼

其他資料來源配置此處略去

4、定義註解

定義DbSourceFirst和DbSourceSecond註解,分別作為資料庫的表識。 定義程式碼如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@Mapper
public @interface DbSourceFirst {
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    String value() default "";
}


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@Mapper
public @interface DbSourceSecond {
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    String value() default "";
}
複製程式碼

5、使用單元測試驗證配置

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
   @Autowired
   private NewsDao newsDao;
   @Test
   public void contextLoads() {
      News news = newsDao.GetTop1News();
      String json= JSONObject.toJSON(news).toString();
      System.out.print(json);
   }
}
複製程式碼

spring boot簡單整合active mq訊息

pom檔案新增依賴

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
複製程式碼

yml檔案中新增屬性配置

spring:
    activemq:
      broker-url: tcp://localhost:61616
      in-memory: true
      pool:
        enabled: false
複製程式碼

生產者

/**
 * 生產者
 */
@Service("producer")
public class Producer {
    @Autowired
    private JmsMessagingTemplate template;

    //  目的地要傳送的訊息內容
    public void sendMessage(Destination destination, final String message) {
        template.convertAndSend(destination, message);  //呼叫convertAndSend方法,將資訊傳送進去
    }
}
複製程式碼

消費者

/**
 * 消費者
 */
@Service
public class Consumer {
    @JmsListener(destination="mytest.queue")   //使用JMSListener 監聽在Producer的訊息
    public void received(String message) {
        //列印接收到的資訊
        System.out.println("customer接收到的資訊為: " +message);
    }
}

/**
 * 消費者
 */
@Service
public class Consumer2 {
    @JmsListener(destination="mytest.queue")   //使用JMSListener 監聽在Producer的訊息
    public void received(String message) {
        //列印接收到的資訊
        System.out.println("customer2接收到的資訊為: " +message);
    }
}
複製程式碼

測試mq

@Autowired
private Producer producer;

/**
 * 測試mq
 * @throws InterruptedException
 */
@Test
public void  testActivemq()  throws InterruptedException{
    ActiveMQQueue activeMQQueue = new ActiveMQQueue("mytest.queue");   //設定目的地destination
    for(int i=0;i<10;i++) {
        producer.sendMessage(activeMQQueue, "xxh 是一個好人");
    }
}
複製程式碼

相關文章