前面的很多文章都是对主流的一些框架进行了详细的解析,在最近发现SpringBoot , SpringCloud这样的微服务不断的兴起,所以也就特别的进行了一点学习,在学习之后感觉,SpringBoot确实减少了开发人员的工作,并且开发更加的快速,但是里面包含的内容确实很经典的,所以,特别的用这篇文章来对SpringBoot的内容进行说明一下。
一:Spring Boot 特点
(1)使编码变简单
(2)使配置变简单
(3)使部署变简单
(4)使监控变简单
二:Spring Boot缺点
(1)依赖太多,随便的一个Spring Boot项目都是几十M
(2)缺少服务的注册和发现等解决方案
(3) 缺少监控集成方案,安全管理方案
(4)中文的文档和资料太少且不够深入
三:Spring Boot的应用场景
(1)Spring能够应用的场景
(2)java Web应用
(3)微服务
OK。开始进入正题~~~~~~~
四:搭建SpringBoot的开发环境
搭建环境:
(1)maven-3.5.2
(2)JDK1.8
(3)编辑器IDEA--------------------当然这个根据自己习惯,喜欢用什么编辑器就用什么就可以了,只是说如果采取IDEA编辑器的话,那么创建项目的时候,很多配置文件就会自动写入,而采取Eclipse的话,就需要手动写一下关于SpringBoot的Maven配置而已,也不麻烦,所以没关系
步骤:(简单暴力,直接用图来进行)
1:
2:
3:
4:
5:直接next就生成了如下的项目结构,并且将多余的内容删除掉
6:进入src,可以看到自动生成了一个文件
7:OK,到这里,一个SpringBoot项目就已经创建成功了。
8:进行访问url来测试项目。首先创建一个Controller类,如下:
9:这时候,就直接运行,DemoApplication的那个Main函数,就可以了(注意,这个就和一般的网页项目不一样,原因,在后面我会不断的进行讲解)
10:如下所示:::访问正常,就说明构建好了
注意:有很多小伙伴在按照我上面的步骤进行配置之后,访问URL,却是一直提示404的界面如下所示:
哇塞,啥情况,怎么回事呢?其实很简单,就是因为SpringBoot没有扫描到Controller的注解,所以找不到对应的URL地址。解决方法:就是将DemoApplication类与controller类放在同一个包下面即可。出现上面的原因是因为,在默认生成的Demoappliction包是扫描不到我们写的controller注解类的,所以根据上面的修改就可以啦!!当然还可以在main函数的类中通过注解@SpringBootApplication(scanBasePackage="com.hnu.scw.controller")进行设置扫描的包也可以
贴一下,关于SpringBoot的pom.xml文件,因为这样方法有些人使用Eclipse进行开发的人。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.scwspringboot</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
五:项目属性配置
(1)修改项目启动的端口和跟路径
小知识:SpringBoot项目,它会自动扫描application.properties或者application.yml(这也是一种配置文件的后缀)这名字的配置文件
结构如下:
application.propertes:
server.port=8088
server.context-path=/first
这样的话,访问地址就变为了:
对应的application.yml为:(看起来方便点,个人喜欢用这样的结构)
server:
port: 8088
context-path: /first
那么访问路径还是和上面的配置的一样的哦!!
六:配置数据库
(1)添加JPA和Mysql的依赖
<!--添加数据库的jpa开始-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--数据库依赖结束-->
(2)修改application.yml(或者application.properties也可以)
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springboot
username: root
password: mao15897610067
jpa:
hibernate:
ddl-auto: update
show-sql: true
database-platform: org.hibernate.dialect.MySQL5Dialect
(3)创建实体
package com.hnu.scw.controller;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author scw
* @create 2017-12-26 14:54
* @desc 人的实体类
**/
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
/**
* 必须有一个空参构造
*/
public Person(){
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(4)启动项目。就可以看到对应的数据库中有对应实体类的字段,即为成功
OK,上面进行了配置信息,那么就必不可少的要进行实例来进行测试了(只能说SpringBoot进行数据库操作非常非常简单)
首先 ,写一个接口
package com.hnu.scw.controller;
import com.hnu.scw.controller.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* 进行数据库操作的dao
*/
@Transactional
public interface PersonDao extends JpaRepository< Person , Long> {
/**
* 据姓名进行查询数据
*/
public Person findPersonByName(String name);
}
写一个Controller类(这里就不写service层了,直接在Controller层调用dao的方法)
package com.hnu.scw.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author scw
* @create 2017-12-26 15:17
* @desc 进行数据库操作的测试
**/
@RestController
public class PersonController {
/**
* 进行数据库操作的dao,这里省略了service层,因为只是为了测试数据库操作
*/
@Autowired
private PersonDao personDao ;
/**
* 获取到所有的数据信息
* @return
*/
@RequestMapping(value = "findpersons")
public List<Person> findAllPerson(){
return personDao.findAll();
}
/**
* 保存一条数据
* @return
*/
@RequestMapping(value = "saveperson")
public String savePerson(){
Person person = new Person();
person.setName("小黑");
personDao.save(person);
return "success";
}
/**
* 获取到某个id的数据信息
* @return
*/
@RequestMapping(value = "getperson")
public Person getPersonByid(Long id){
return personDao.findOne(id);
}
/**
* 根据某个字段来进行查询数据(这里根据姓名来进行测试)
* @return
*/
@RequestMapping(value = "findperson")
public Person getPersonByName(String name){
return personDao.findPersonByName(name);
}
/**
* 更新某个id的数据信息
* @return
*/
@RequestMapping(value = "updateperson")
public Person updatePersonByid(Long id){
Person person = new Person();
person.setId(id);
person.setName("我是更新了的小白哦!");
//调用save方法,它会根据主键字段进行对应的数据的更新
return personDao.save(person);
}
/**
* 删除数据
* @return
*/
@RequestMapping(value = "deleteperson")
public String deletePersonByid(Long id){
personDao.delete(id);
return "success";
}
}
七:统一异常的处理
功能:当我们写项目的时候,肯定碰到过在项目进行抛出多种异常,然后进行捕获,然后分别进行处理的情况。但是,有时候,我们需要在前台根据后台抛出的不同异常,来显示不同的提示信息,或者超链接到不同的页面中,那么这样的需求的话,我们就可以根据后台的异常信息,来进行封装,从而以JSON的格式返回到前台,然后前台再根据JSON数据进行相应的解析和处理即可。
(1)自定义一个异常类
package com.hnu.scw.controller;
/**
* @author scw
* @create 2017-12-26 16:58
* @desc 自定义异常
* 继承RuntimeException比直接继承Exception好些
**/
public class MyException extends RuntimeException {
/**
* 设置异常码和异常信息
*/
private Integer code;
private String msg;
public MyException(MyExceptionEnum myExceptionEnum){
this.code = myExceptionEnum.getCode();
this.msg = myExceptionEnum.getMsg();
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
(2)自定义,返回的JSON格式内容
package com.hnu.scw.controller;
/**
* @author Administrator
* @create 2017-12-26 17:14
* @desc 用于返回到页面数据的封装类(主要是方便显示异常的信息)
**/
public class ResultContent<T> {
private Integer code;
private String msg;
private T data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
(3)定义一个返回JSON内容的工具类
package com.hnu.scw.controller;
/**
* @author scw
* @create 2017-12-26 17:19
* @desc 返回结果的工具类
**/
public class ResultUtils {
/**
* 操作成功,返回的json数据,进行封装
* @param object
* @return
*/
public static ResultContent successOp(Object object){
ResultContent resultContent = new ResultContent();
resultContent.setCode(MyExceptionEnum.SUCCESS.getCode());
resultContent.setMsg(MyExceptionEnum.SUCCESS.getMsg());
resultContent.setData(object);
return resultContent ;
}
/**
* 重载一个返回成功的方法,因为有时候可能操作后,不需要返回什么数据到json里面
* @return
*/
public static ResultContent successOp() {
return successOp(null);
}
/**
* 操作失败,返回的json数据,进行封装
* @return
*/
public static ResultContent errorOp(Integer code , String msg){
ResultContent resultContent = new ResultContent();
resultContent.setCode(code);
resultContent.setMsg(msg);
resultContent.setData(null);
return resultContent ;
}
}
(4)自定义一个枚举类,为了存储不同异常的显示信息----------------当然也可以不写,这个根据需要来吧!
package com.hnu.scw.controller;
/**
* 自定义一个枚举类,主要就是对于异常的信息进行管理,
* 要不然等项目内容多了,就对于提示的异常就不方便进行管理和调试
*/
public enum MyExceptionEnum {
UNKNOW_EXCEPTION(-1 , "未知异常"),
LOW_EXCEPTION( 201 , "ID数值小于0异常"),
BETWEEN_EXCEPTION( 202 , "ID数值小于10大于0异常"),
SUCCESS(200 , "操作成功");
private Integer code;
private String msg;
MyExceptionEnum(Integer code , String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
(5)编写测试Controller类
package com.hnu.scw.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author scw
* @create 2017-12-26 11:24
* @desc 测试SpringBoot的环境搭建
**/
@RestController
public class HelloWordTest {
/**
* service处理类
*/
@Autowired
private ExceptionService exceptionService;
/**
* 进行异常统一管理的处理
* 这样的好处在于,可以对于项目中的所有异常进行统一化的处理,而不至于把异常显示给用户
* @param id 传入的id
* 这里的话,假设传入的id在不同的大小范围,我就抛出不同的异常来演示项目开发中遇到的多种异常问题
*/
@RequestMapping(value = "/excptiontest")
public ResultContent exceptionManageTest(Long id) throws Exception{
exceptionService.showDifferenceExceptionSitution(id);
return ResultUtils.successOp(); //这里根据需要,看是否需要把对象数据显示到JSON格式中,在相应调用success的两个重载方法即可
}
}
(6)编写测试service类
package com.hnu.scw.controller;
import org.springframework.stereotype.Service;
/**
* @author scw
* @create 2017-12-26 16:52
* @desc 演示service层进行业务逻辑处理,而碰到的可能出现的异常问题
**/
@Service
public class ExceptionService {
/**
* 演示进行业务逻辑的处理,而碰到多种不同的异常的情况
* 参数ID,只是用于演示传入的参数而造成异常而已,以后在项目根据需要进行修改即可
*/
public void showDifferenceExceptionSitution(Long id) throws Exception{
/**
* 这里模拟,几种不同情况的方法
* 1:当传入的id小于0,抛出一种异常
* 2:当传入的id大于0小于10,又抛出一种
* 3:当传入的id大于10,则表示是正常的数据
*/
if(id < 0){
throw new MyException(MyExceptionEnum.LOW_EXCEPTION);
}
else if( 0 < id && id < 10){
throw new MyException(MyExceptionEnum.BETWEEN_EXCEPTION);
}
else if( id > 10){
/**
* 这里就模拟数据正确的接下来的操作就可以了,这里的话,就模拟返回成功了
*/
}
}
}
(7)编写异常接受处理类
package com.hnu.scw.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author scw
* @create 2017-12-26 17:10
* @desc 接受异常处理类
**/
@ControllerAdvice
public class ExceptionHandler {
/**
* 进行log日志的打印,这样就方便在控制台进行查看
*/
private final static Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);
//需要进行捕获的异常
@org.springframework.web.bind.annotation.ExceptionHandler(value = Exception.class)
//注意下面的内容,是返回为json的格式,这样的话,就可以在前台进行判断,然后进行相应的提示框进行提示内容了
@ResponseBody
public ResultContent handle(Exception e){
if(e instanceof MyException){
MyException myException = (MyException) e;
return ResultUtils.errorOp(myException.getCode() , myException.getMsg());
}else{
/**
* 非自定义的异常的时候,那么就返回是未知的异常内容即可(这个在自定义的异常枚举类中进行了设置)
*/
logger.error("系统异常{}",e);
return ResultUtils.errorOp(MyExceptionEnum.UNKNOW_EXCEPTION.getCode() , MyExceptionEnum.UNKNOW_EXCEPTION.getMsg());
}
}
}
(8)进行测试
情况1:
情况2:
情况3:
OK,这样的话,再前台在进行判断一下不就可以了嘛~!多么好的一个封装处理异常。。。。。。。。。。。。
九:详解@Enable注解
引入知识点:
(1)获取配置文件中的内容(主要讲解两种方式)
配置文件内容如下:
方法一:通过@Value注解的形式
package com.hnu.scw.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author Administrator
* @create 2017-12-27 9:14
* @desc 获取配置文件中的内容
* @ConfigurationProperties注解会默认去找application.xml或者application.yml配置文件
**/
@Component
@ConfigurationProperties(prefix = "scw")
public class PropertiesContent {
@Value("${scw.name}")
private String name ;
@Value("${scw.sex}")
private String sex ;
@Override
public String toString() {
return "PropertiesContent{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
方法二:通过set()和get()方法
package com.hnu.scw.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author Administrator
* @create 2017-12-27 9:14
* @desc 获取配置文件中的内容
* @ConfigurationProperties注解会默认去找application.xml或者application.yml配置文件
**/
@Component
@ConfigurationProperties(prefix = "scw")
public class PropertiesContent {
private String name ;
private String sex ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "PropertiesContent{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
测试代码:
package com.hnu.scw.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext contex = SpringApplication.run(DemoApplication.class, args);
//测试是否获取到的配置文件的内容信息
System.out.println(contex.getBean(PropertiesContent.class));
contex.close();
}
}
解析:其实,之所以能够这样获取到配置文件中的内容的关键就在于,@SpringBootApplication注解里面有@EnableConfiggurationProperties注解的原因。。
(2)如何进行异步处理异步处理代码:
package com.hnu.scw.controller;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @author Administrator
* @create 2017-12-27 9:34
* @desc 测试springboot中的异步注解的使用
**/
@Component
public class AsycTest implements Runnable {
//注意看下面的注解
@Async
@Override
public void run() {
try {
for (int i =0 ; i < 5 ; i++){
System.out.println("-------------" + i);
TimeUnit.SECONDS.sleep(1);
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
进行测试:--------(注意看注解)
package com.hnu.scw.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync //--------------这里很关键
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext contex = SpringApplication.run(DemoApplication.class, args);
//测试异步进行操作
contex.getBean(Runnable.class).run();
System.out.println("同步的话,我是最后执行,如果是异步,那么我就是第一执行的了!");
contex.close();
}
}
解析:这里就是关于@EnabkeAsync注解的作用,如果不加的话,就还是执行同步处理,大家可以试试
通过上面的两个例子,可以得到一些总结:我们通过查看@Enable开头的注解,可以看源码里面,都是如下的类似的形式:
@Enable的功能,其实就是进行一些特性功能的装配处理,而之所以能够实现这样的作用,关键在于@Import注解,简单点说,@Import注解就是添加引用类的里面函数功能;所以就实现了一种特性功能,这样的话,如果想实现某种特性功能,那么只需要在@Import注解把对应的类引用进去就可以了,这样是不是就是一种动态处理呢?
OK,用一个例子来说明一个问题:
比如现在有一个需求,就是说,我想手动实现只装配我填写的一些包下面的bean类,而我没有填入的就不进行产生bean到Spring容易中进行管理。(注意:对于这个问题,其实springboot里面有一个注解也是类似这样的功能,就是@condition)
编写代码:
1:创建Bean时候的回调类
package com.hnu.scw.controller;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.util.List;
/**
* @author scw
* @create 2017-12-27 10:19
* @desc 判断当前的Bean是否是填入的包下面的类,如果是,那么就进行装配到Spring容器,否则不进行装配
**/
public class MyPackageBeanFactory implements BeanPostProcessor {
private List<String> packages ;
public List<String> getPackages() {
return packages;
}
public void setPackages(List<String> packages) {
this.packages = packages;
}
/**
* 这个方法是在进行bean生成之前会调用的方法
* @param object
* @param s
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object object, String s) throws BeansException {
for(String pack : packages){
if(object.getClass().getName() .startsWith(pack)){
//就是简单进行输出一下就好了
System.out.println("装配了bean:" + object.getClass().getName());
}
}
return object;
}
/**
* 这个方法是bean生成之后调用的方法
* @param s
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object object, String s) throws BeansException {
return object;
}
}
2:编写动态注入的类
package com.hnu.scw.controller;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author scw
* @create 2017-12-27 10:24
* @desc 进行动态装配Bean
**/
public class MyPackageBeanAnnotation implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//获取到注解类中的属性内容
Map<String, Object> attrs = annotationMetadata.getAnnotationAttributes(EnablePackUtils.class.getName());
//获取属性是packages的内容,因为我定义了的是数组,所以这里就需要强转为数组
String[] packAttr = (String[]) attrs.get("packages");
List<String> packages = Arrays.asList(packAttr);
System.out.println("packages : " + packages);
//进行动态注入的处理
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(MyPackageBeanFactory.class);
//将属性注入到里面
bdb.addPropertyValue("packages" , packages);
//进行注入
beanDefinitionRegistry.registerBeanDefinition(MyPackageBeanFactory.class.getName() , bdb.getBeanDefinition());
}
}
3:注解接口
package com.hnu.scw.controller;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* 演示,自己手动写一个动态装配Bean到Spring容器的注解
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
@Documented
@Import(MyPackageBeanAnnotation.class)
public @interface EnablePackUtils {
String[] packages() ;
}
4:随便写两个不同包下的类,类似如下:
package com.hnu.scw.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
@EnablePackUtils(packages = {"com.hnu.scw.dao","com.hnu.scw.service"})
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext contex = SpringApplication.run(DemoApplication.class, args);
//测试是否获取到的配置文件的内容信息
System.out.println(contex.getBean(PropertiesContent.class));
//测试异步进行操作
contex.getBean(Runnable.class).run();
System.out.println("同步的话,我是最后执行,如果是异步,那么我就是第一执行的了!");
contex.close();
}
}
十:事件监听
事件流程:
(1)自定义事件:一般是继承ApplicationEvent抽象类
package com.hnu.scw.dao;
import org.springframework.context.ApplicationEvent;
/**
* @author scw
* @create 2017-12-27 12:47
* @desc
**/
public class MyApplication extends ApplicationEvent {
public MyApplication(Object source) {
super(source);
}
}
(2)定义事件器:一般是实现ApplicationListener接口
package com.hnu.scw.dao;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
/**
* @author scw
* @create 2017-12-27 12:45
* @desc 对事件进行监听
**/
public class MyApplicationListener implements ApplicationListener<MyApplication> {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println("我监听到了事件");
}
}
(3)配置监听器
方法一:通过容器的add()方法(下面代码就是利用这种方法)
方法二:在监听事件类上加注解@Component ----------这个方法比较常用
方法三:在application.properties配置文件中写context.listener.classes = 对应监听器类的绝对路径)---------实现原理的话,就看看DelegationApplicationListener这个类就明白
方法四:编写事件监听的处理类-----------这种主要是针对不同的事件进行不同的处理
如下:
package com.hnu.scw.controller;
import com.hnu.scw.dao.MyApplication;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* @author scw
* @create 2017-12-27 13:01
* @desc 对不同事件进行监听处理
**/
@Component
public class MyApplicationHandler {
@EventListener
public void event(Object object) {
//在这里面对不同类型的监听进行不同的处理操作即可实现对监听的统一管理,我这里自定义的监听是MyApplication类
if(object instanceof MyApplication){
System.out.println("我监听到了事件");
}
}
}
(4)发布事件
package com.hnu.scw.controller;
import com.hnu.scw.dao.MyApplication;
import com.hnu.scw.dao.MyApplicationListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext contex = SpringApplication.run(DemoApplication.class, args);
//添加事件到容器中
contex.addApplicationListener(new MyApplicationListener());
//发布监听
contex.publishEvent(new MyApplication(new Object()));
contex.close();
}
}
十一:当Spring容器加载完成之前,可以通过如下的几个接口进行一些相应的处理
(1)ApplicationContextInitializer接口:是在spring容器执行refreshed之前的一个回调
使用步骤:
1:写一个类,实现ApplicationContextInitializer接口
2:注册ApplicationContextInitializer
注册的方法:
1:SpringApplication.addInitializers()方法来进行
2:通过配置context.initializer.classes 指定,可以指定多个参数,只需要用“逗号”隔开即可
3:可以通过spring.factories机制(注册事件监听也可以用这个方法)--------------就是在resource文件夹下创建一个META-INF文件夹,然后创建一个spring.factories.properties配置文件,并添加org.springframework.context.ApplicationContextInitializer = "对应实现该接口的类名即可"
(2)CmmandLineRunner:是在容器启动成功后的最后一步回调(类似开机自启动)
使用步骤:
1:写一个类实现CmmandLineRunner接口
2:把该类放入到spring容器中,即通过注解@Component
注意:可以通过注解@Order来对该类的方法启动顺序进行控制(数值越大越顺序启动越在后面)
(3)ApplicationRunner:是在容器启动成功后的最后一步回调(类似开机自启动)
使用步骤:
1:写一个类实现ApplicationRunner接口
2:把该类放入到spring容器中,即通过注解@Component
注意:可以通过注解@Order来对该类的方法启动顺序进行控制(数值越大越顺序启动越在后面)
分析:CmmandLineRunner 和 ApplicationRunner的区别:
1:CmmandLineRunner 接口的参数是最原始的参数,没有进行处理
2:ApplicationRunner方法中接口的参数是ApplicationArguments,是对原始参数做了进一步的封装,是对main方法中的参数进行了封装,我们可以通过解析--name = value的形式,可以通过name来获取value值
十二:控制SpringBoot开启的图标不在控制台进行输出-------------这个只是好玩而已,没啥特别作用
方法:在main函数中,
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication aa = new SpringApplication(DemoApplication.class);
aa.setBannerMode(Banner.Mode.OFF);
ConfigurableApplicationContext applicationContext = aa.run(args);
}
自定义启动输出的文字内容:
方法一:在resource文件夹下面,创建一个banner.txt文件
方法二:在配置文件中,通过banner.loaction = 对应的resource文件名字即可
自定义启动殊荣出的图片内容:支持jpg,png,gif
方法一:在resource文件夹下面,创建一个banner.jpg文件
方法二:在配置文件中,通过banner。image。loaction = 对应的resource文件名字即可
------------------下面的知识点,可是对于Web开发很实用的知识哦!!好好看看!!!!!!!!------------------------------------------
十三:SpringBoot的运行流程----------查看SpringApplication类的源码即可
1:判断是否是web环境
2:加载所有classpath下面的META-INF/spring。factories -------也就是ApplicationContextInitializer
3:加载所有classpath下面的META-INF/spring。factories ---------也就是还有ApplicationListener
4:推断main方法所在的类
5:开始执行main方法
6:设置java。awt。headless系统变量
7:加载所有classpath下面的META-INF/spring。factories ----------也就是还有加载ApplicationRunlistener
8:执行所有SpringApplicationRunlistener的started方法
9:实例化ApplicationArguments对象
10:创建environment
11:配置environment,主要是把run方法的参数配置到environment
12:执行所有SpringApplicationRunlistener的environmentPropered方法
13:如果不是web环境,但是是配置了web环境的environment,则将这个web的environment转换成标准的web的environment
14:打印Banner
15:初始化applicationContext,如果是web环境,则实例化AnnotationConfigEmbedWebApplicationContext对象,否则实例化AnnotationConfigApplication对象
16:如上beanNameGenerator不为空,就把beanNameGenerator对象注入到context属性中
17:回调所有的ApplicationContextInitializer方法
18:执行所有SpringApplicationRunlistener的contextProper方法
19:依次往Spring容器中注入:ApplicationAryguments,Banner
20:加载所有的源到context里面
21:执行所有SpringApplicationRunlistener的contextLoaded方法
22:执行context的refresh方法,并且调用context的registerShutdownHook方法
23:回调:获取容器所有的ApplicationRunnuer,commandLineRunlistener,然后排序,依次调用
24:执行所有SpringApplicationRunlistener的finish方法
25:SpringBoot加载完成
注意:真正的加载还有些步骤的,但是我这里只是把主要的内容写出来了,但是我们可以根据SpringApplication这个类的源码进行跟踪就可以大致了解加载的流程,这个对我们以后的学习还是很有帮助的。
十四:配置Controller层返回JSP的配置文件
(1)首先是需要在pom.xml中添加依赖
<dependency>
<groupId>org.apcahce.tocat.embed</groupId>
<artifactID>tomcat-embed-jasper</artifactID>
</dependency>
(2)在application.xml配置文件中,加入如下代码:
spring.mvc.view.prefix=/WEB-INF/jsp/ //前缀 ,如果这里最后添加了一个/,那么就不需要在controller的函数返回的时候加/,否则就需要添加,一般都是在这里添加
spring.mvc.view.suffix=.jsp //后缀
其实这个和我们之前用springmvc的配置一样,只需要在springmvc的配置文件中进行配置的内容大体一致,只是形式改变了一点而已。
十五:使用freemarker模板(一般在项目中,要么使用模板,要么就使用JSP就好了,最好别混杂用)
步骤:(1)导入依赖
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
(2)在resource文件下(即classpath:),创建一个templates的文件夹(专门用于存放模板,因为这个文件目录名是Springboot默认寻找的)
(3)在templates文件下,创建模板JSP文件,后缀是以ftl结尾
(4)使用模板,这个就是在controller层调用JSP的方法是一样的。
(5)如果想自定义寻找templates的位置,就可以在application.properties中配置如下:(可以配置多个,即在路径用逗号分割即可)
spring.freemarker.templateLoaderPath=classpath:/自定义文件名即可
十六:更换SpringBoot的容器(一般就是tomcat和Jetty进行切换)
切换成Jetty:(在pom.xml文件中添加依赖)
(1)排除tomcat的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
(2)添加Jetty依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
十七:访问静态资源的方式
方式一:可以直接访问src/main/webapp目录下的内容
方法二:可以把静态资源放入到resource文件目录下:默认的有以下几个路径:
1:/META-INF/resources/
2:/resources/
3:/static/
4;/public
比如:
方法三:自定义静态资源访问路径
在配置文件application.properties中添加:
spring.resources.staticLocations = '再加自定义的路径'
比如spring.resources.staticLocations = classpth:/html
十八:拦截器
编写自定义拦截器步骤:
(1)编写实现拦截器接口的类
package com.hnu.scw.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author scw
* @create 2018-01-01 13:59
* @desc 编写一个拦截器
**/
public class MyInterceptor implements HandlerInterceptor{
/**
* controller调用方法之前
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @return 如果返回false,那么就不会执行后面的操作了,返回true就相当于放行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("=======我是执行之前======");
return true;
}
/**
* controller调用方法之后,而渲染页面之前
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("=======我是渲染页面之前======");
}
/**
* 整个请求完成之后(这个一般都是进行回收垃圾之类的操作)
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param e
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("=======我是请求完成执行之后======");
}
}
(2)将自定义拦截器添加到容器配置中
package com.hnu.scw;
import com.hnu.scw.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author scw
* @create 2018-01-01 14:01
* @desc 编写一些容器配置类
**/
@Configuration
public class configuration extends WebMvcConfigurerAdapter{
//需要重写这个方法,把自定义的拦截器添加进去
@Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
//添加自定义的拦截器
registry.addInterceptor(new MyInterceptor());
}
}
(3)配置完成,这样的话,访问controller方法就会去调用自定义的拦截器方法了
十九:针对不同请求返回状态码和抛出异常而进行显示不同的页面
功能:类似,我们原来都是在web.xml文件中,进行相应的配置,比如404,500,这些状态码返回对应的页面,这样就不会让用户看到错误信息,那么在SpringBoot怎么实现,就看下面的代码即可:
步骤:
(1)编写状态码控制显示类
package com.hnu.scw.interceptor;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.boot.web.servlet.ErrorPageRegistrar;
import org.springframework.boot.web.servlet.ErrorPageRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* @author scw
* @create 2018-01-01 14:16
* @desc 管理不同网页请求码
**/
@Component
public class ErrorPageManager implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
//添加对不同状态码所进行的不同页面显示内容(主要是ErrorPage对象有两个不同的构造函数,这个可以看源码可以知道)
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND , "/404.html");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR , "/500.html");
//配置当发生“空指针”异常而跳转的页面内容
ErrorPage errorPageNull = new ErrorPage(NullPointerException.class , "/nullpage.html");
errorPageRegistry.addErrorPages(errorPage404 , errorPage500);
}
}
(2)编写对应的页面内容(直接用图了,因为没什么内容,都是上面提到过的)
二十:针对不同的Controller进行显示不同的异常信息(这个就只能在当前的controller类中进行控制)
示例代码如下:--------------------一看就明白了
package com.hnu.scw.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author scw
* @create 2017-12-26 11:24
* @desc 测试SpringBoot的环境搭建
**/
@RestController
public class HelloWordTest {
@ExceptionHandler(value = Exception.class) //关键在于这里
public String errorDeal(Exception e){
return "当前发生了:"+ e.getMessage();
}
@RequestMapping(value = "/exception1")
public String exception1(){
throw new NullPointerException("空指针了呀!");
}
@RequestMapping(value = "/exception2")
public String exception2(){
throw new ClassCastException("类转换错误了呀!");
}
咳咳,,注意了,此处有干货:
上面既然是对某些controller自定义的处理方式,那么如果我想配置对所有的controller中的异常都是某种处理方法呢?那怎么进行配置呢!!!?(当然,这个可以通过前一个的知识点进行编写),下面介绍另外一种方式:
package com.hnu.scw.interceptor;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author scw
* @create 2018-01-01 14:39
* @desc 处理全局的异常
**/
@ControllerAdvice
public class MyExceptionAdvice {
//这里可以针对不同的异常进行不同的处理,就只需要改下面的异常类就可以了,这里处理的是对所有的异常处理
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String definitionMyException(Exception e){
return "发生的异常为:" + e.getClass().getName();
}
}
二十一:JDBC的相关知识
知识点一:配置DataSource
步骤:(1)添加JDBC的依赖和mysql的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
(2)在配置文件application.yml中添加数据库的信息
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springboot
username: root
password: *********
(3)配置完成,进行测试
package com.hnu.scw;
import com.hnu.scw.controller.EnablePackUtils;
import com.hnu.scw.controller.PropertiesContent;
import com.hnu.scw.dao.MyApplication;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import javax.sql.DataSource;
@EnablePackUtils(packages = {"com.hnu.scw.dao","com.hnu.scw.service"})
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext contex = SpringApplication.run(DemoApplication.class, args);
System.out.println(contex.getBean(DataSource.class));
打印处理可以看到,这个数据源是Tomcat的,那么如果想使用其他的数据源又该如何进行呢?
知识点二:使用SpringBoot默认支持的其他类型数据源
方法一:在pom.xml文件中,添加其他的数据源的依赖,并将Tomcat进行排除(比如我这里换成HikariCP数据源)
方法二:不在pom.xml文件中排除Tomcat,在配置文件中进行处理
spring.datasource.type = com.zaxxer.hikari.HikariDataSource
注意:SpringBoot默认只支持:tomcat-jdbc ; Hikari ; dbcp ; dbcp2 这四种数据源,就按上面的方法进行配置修改即可。
那么问题来了,如果我就是想配置其他的数据源那又该如何做呢?
知识点三:自定义数据源配置(比如配置阿里的druid)
步骤:(1)添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.25</version>
</dependency>
(2)编写数据源类
@SpringBootConfiguration
public class DBConfiguration{
@Autowired
private Enviroment env; //从配置文件中读取数据库相关的内容
@Bean
public DataSource createDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUrl(env.getProperty("sping.datasource.url"));
ds.setUsername(env.getProperty("sping.datasource.username"));
ds.setPassword(env.getProperty("sping.datasource.password"));
ds.setDriverClassName(env.getProperty("sping.datasource.driverClassName"));
return ds;
}
}
知识点四:事务
开启事务:在main函数上,添加@EnableTransactionManagement,然后在对应需要进行事务控制的方法上面添加@Transactional注解即可
二十二:AOP的知识点---------------这个和Spring中的内容基本一致,所以可以看看我前面关于Spring的内容
使用步骤:(1)添加一个AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)编写切面类
/*
主要就是一个示例代码,对于service层下的子类的所有方法都进行aop管理
*/
@Aspect
@Component
public class LogAspect{
@Before("execution(* com.hnu.scw.service..*.*(..))") //切入点
public void log(){
System.out.println("我是记录日志");
}
}
注意:默认的是使用JDK的动态代理,如果代理类没有实现接口,那么就会采用cglib的动态代理。
二十三:日志
主要就是通过Logger这个类对象:
比如下面这个代码:
@Component
public class PersonDao{
private Logger log = LoggerFactory.getLogger(PersonDao.class);
public void log(){
log.debug("我是debug级别");
log.info("我是info级别");
log.warn("我是warn级别");
log.error("我是error级别");
}
}
知识点2:指定日志以文件的形式进行输出,而不是在控制台进行显示
在配置文件application.properties中配置:
logging.file = e:/temp/my.log---------------
或者使用logging.path = e:/temp/mylog -------------这个日志文件的名字会默认为spring.log
知识点3:指定日志显示的格式:
logging.pattern.console = %-20(%d{yyyy-MM-dd }[%thread])%-5level %logger{80} -%msg%n -------------------控制在控制台显示的格式
logging.file.console =%-20(%d{yyyy-MM-dd HH:mm:ss.SSS}[%thread])%-5level %logger{80} -%msg%n ----------------控制在文件中的显示的格式
知识点4:使用其他的日志组件
步骤:(1)首先排除默认的日志
(2)添加新的日志组件的依赖,比如log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
(2)在classpath下添加日志模板,比如:
二十四:SpringBoot的监控和度量知识点-------------------主要就是对项目中的一些内容进行审查,从而可以进行优化
步骤:(1)添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
(2)运行项目(必须基于WEB环境,才有这些监控信息)
就会看到很多这样的信息,这些就是SpringBoot的一些监控,
我们可以通过范文url,从而来查看里面内容信息。比如,查看所有的url映射信息,就可以通过:
localhost:8080/mappings -------------------------这样就可以看到所有的mapping信息了,其他的也类似,大家可以进行查看一下。
OK,先把最基本的一些内容先介绍好了,接下来,我将写一篇关于微服务的开发文章(可以先了解一些SpringCloud,当然我也会进行讲解),如果有兴趣的朋友,可以关注我哦。这样有新文章出来就可以得到提醒啦~!