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 是一個好人");
}
}
複製程式碼