所有 /webjars/**
,都去 classpath:/META-INF/resources/webjars/
找资源; webjars
文件代表以jar包的形式去引入静态资源,具体见webjars;
webjars
使用方式很简单,以 jquery
引入为例:
// pom.xml <!-- 引入jQuery -->在访问的时候只需要写**/webjars/文件夹下面的资源名称即可 <dependency> <groupId></groupId> <artifactId>jquery</artifactId> <version>3.3.1</version> </dependency> // 访问http://localhost:8080/webjars/jquery/3.3.1/jquery.js即可访问到导入静态资源文件夹中的jquery.js文件 复制代码
所有访问 /**
访问当前项目的静态资源(css,img,js等文件),都会去以下路径查找是否含有该文件
"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", // 习惯使用这个 **/src/main/resources/static/ "classpath:/public/", // 习惯使用这个 **/src/main/resource/public/ "/" // 当前项目跟路径 // 访问http://localhost:8080/assets/css/signin.css => 其实是访问/src/main/resource/static/assets/css/signin.css 复制代码
访问 /
默认显示的页面,也叫欢迎页面,都会去静态资源所在的路径寻找 index.html
文件显示出来
private Optional<Resource> getWelcomePage() { String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations()); return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst(); } § private Resource getIndexHtml(String location) { return this.resourceLoader.getResource(location + "index.html"); } 复制代码
访问 /favicon.ico
会直接映射到 classpath:/public|static|resources/favicon.ico
classpath:/resources/
下的application.yml文件中配置数据库连接信息 spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/jdbc driver-class-name: com.mysql.cj.jdbc.Driver // <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> mysql驱动类mysql-connector-java版本在6以上,那么 driver-class-name => com.mysql.cj.jdbc.Driver; mysql驱动类mysql-connector-java版本6以下,那么 driver-class-name => com.mysql.jdbc.Driver; 复制代码
默认数据源:
SpringBoot创建默认DataSource时,规则如下:
参考资料:
深入理解Spring Boot数据源与连接池原理
SpringBoot 中使用 JDBC Templet
引入druid,切换数据源为druid数据源
type: com.alibaba.druid.pool.DruidDataSource // 指定为druid数据源 // 其他属性 #上半区公共部分对应的是 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 中的属性 #下半区属性对应的是 com.alibaba.druid.pool.DruidDataSource 中的属性,Spring Boot 默认是不注入不了这些属性值的,需要自己绑定 #druid 数据源专有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority #则导入log4j依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 复制代码
配置druid数据源自动装配
// SpringBoot启动类SpringBootDataJdbcApplication的同级文件夹中创建config.DruidConfig.java文件 package com.dyh.www.springbootjdbc.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Configuration public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druidDataSource() { return new DruidDataSource(); } // 配置druid的监控 // 配置一个管理后台的servlet @Bean public ServletRegistrationBean statViewServlet() { ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername", "admin"); initParams.put("loginPassword", "123456"); initParams.put("allow", ""); initParams.put("deny", ""); bean.setInitParameters(initParams); return bean; } // 配置一个web监控的拦截filter @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js, *.css, /druid/*"); // 静态资源js,css文件不拦截过滤 bean.setInitParameters(initParams); bean.setUrlPatterns(Arrays.asList("/*")); return bean; } } 复制代码
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> 复制代码
// 这是操作数据库的mapper @Mapper // 数据库与数据模型的映射注解,如果这里没有,则需要在SpringBootApplication启动类上添加扫描全局mapper的注解,并指定相应的扫描路径 public interface DepartmentMapper { // 数据库的增删改查sql接口数据映射 @Select("select * from department") public List<Department> getDepartmentList(); @Select("select * from department where id=#{id}") public Department getDepartmentById(Integer id); @Delete("delete from department where id=#{id}") public int deleteDepartmentById(Integer id); @Options(useGeneratedKeys = true, keyProperty = "id") // 指定主键 @Insert("insert into department(departmentName) values(#{departmentName})") public int insertDepartment(Department department); @Update("update department set departmentName=#{departmentName} where id=#{id}") public int updateDepartment(Department department); } // 这里的 departmentName 对应数据库的 departmentName 字段 // 如果这里的 department_name 相对应数据库的 departmentName 字段 转换成驼峰命名方式,则可以增加转换的自动配置 复制代码
import com.dyh.springbootmybatis.bean.Department; import com.dyh.springbootmybatis.mapper.DepartmentMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; // web API接口 @RestController public class DepartmentController { @Autowired DepartmentMapper departmentMapper; @RequestMapping(value = "/dept/{id}", method=RequestMethod.GET) public Department getDepartmentById(@PathVariable("id") Integer id) { return departmentMapper.getDepartmentById(id); } @RequestMapping(value = "/dept/list", method=RequestMethod.GET) public List<Department> getDepartmentList() { return departmentMapper.getDepartmentList(); } @RequestMapping(value = "/dept", method = RequestMethod.GET) public Department insertDepartment(Department department) { departmentMapper.insertDepartment(department); return department; } } 复制代码
转换的自动配置
在config/文件夹下创建自定义mybatis的映射规则, 给Spring容器添加一个ConfigurationCustomizer组件即可
@org.springframework.context.annotation.Configuration // 表明这是一个自动配置类 public class MybatisConfig { @Bean // 表明这是一个Spring容器组件 public ConfigurationCustomizer configurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { configuration.setMapUnderscoreToCamelCase(true); } }; } } 复制代码
使用 MapperScan
批量扫描mapper文件
@MapperScan(value = "com.dyh.springbootmybatis.mapper") // 这里批量扫描mapper文件,并且使用value指定路径 @SpringBootApplication public class SpringBootMybatisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootMybatisApplication.class, args); } } 复制代码
以Employee对象为例,首先定义数据实体类,与数据库表字段一一对应
public class Employee { private Integer id; private String lastName; private String email; private Integer gender; private Integer departmentId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public Integer getDepartmentId() { return departmentId; } public void setDepartmentId(Integer departmentId) { this.departmentId = departmentId; } } 复制代码
建立操作数据库表接口的mapper,并统一在SpringBoot的主配置类中配置扫描mapper的
import com.dyh.springbootmybatis.bean.Employee; import java.util.List; // API接口 public interface EmployeeMapper { public Employee getEmployeeById(Integer id); public List<Employee> getEmployeeList(); public Integer deleteEmployeeById(Integer id); } 复制代码
配置接口mapper与数据库表操作对应的sql语句xml文件
配置application.yml文件指定xml文件所在路径
# 引入mybatis配置版文件 mybatis: config-location: classpath:config/mybatis-config/global.xml mapper-locations: classpath:config/mybatis-config/mapper/*.xml 复制代码
配置全局mybatis的配置文件xml
<!-- global.xml文件 --> <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration> 复制代码
配置mybatis操作数据库表的sql语句,与接口API的方法行成映射,具体配置见文档
<!-- Employee.xml --> <!-- 接口API映射配置文件 --> <?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.dyh.springbootmybatis.mapper.EmployeeMapper"> <!-- 唯一标识记号id值为API接口的对应方法 --> <select id="getEmployeeById" resultMap="employeeMap"> select * from employee where id = #{id} </select> <select id="getEmployeeList" resultMap="employeeMap"> select * from employee </select> <delete id="deleteEmployeeById"> delete from employee where id = #{id} </delete> <resultMap id="employeeMap" type="com.dyh.springbootmybatis.bean.Employee"> <!-- 数据库表列字段与实体类的实例属性映射规则 --> <id property="id" column="id" /> <result property="lastName" column="lastName"/> <result property="gender" column="gender"/> <result property="email" column="email"/> <result property="departmentId" column="departmentId"/> </resultMap> </mapper> 复制代码
JPA是作为Spring Data一部分,用于简化数据库的curd和分页列表获取等数据操作的持久化框架,通过配置继承封装好数据操作的接口,为我们提供统一的API。
JPA(ORM): Object Relational Mapping
编写一个实体类(entity.User),将数据表的字段和实体类的字段进行映射,同时配置好映射规则(主键、自增、别名等)
import javax.persistence.*; // 使用JPA注解配置映射关系 @JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" }) // 标记不生成json对象的属性 @Entity @Table(name = "tbl_user") // @Table指定该实体类的数据跟哪个数据表对应,如果省略,默认数据库表名为该类名小写user public class User { @Id // 这是一个主键 @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "last_name", length = 50) // 代表这是和数据库表对应的一个列 private String lastName; @Column // 省略则默认列名就是属性名 private String email; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } 复制代码
编写一个Dao接口来操作实体类对应的数据表,称为Repository,包含常见数据的增删改查,分页等操作
import com.dyh.springbootjpa.entity.User; import org.springframework.data.jpa.repository.JpaRepository; // 继承JpaRepository来继承对数据的操作接口 public interface UserRepository extends JpaRepository<User, Integer> { } 复制代码
application.yml基本配置
# jpa关于sql相关配置 spring: jpa: hibernate: # 更新或创建数据表 ddl-auto: update # 在每次执行sql语句时打印出执行的sql语句 show-sql: true 复制代码
接口文件
import com.dyh.springbootjpa.entity.User; import com.dyh.springbootjpa.repostitoy.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class UserController { @Autowired UserRepository userRepository; @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public User getUserById(@PathVariable("id") Integer id) { User user = userRepository.getOne(id); return user; } @RequestMapping(value = "/user/save", method = RequestMethod.GET) public User insertUser(User user) { User newUser = userRepository.save(user); return newUser; } @RequestMapping(value = "/user/all", method = RequestMethod.GET) public List<User> getUserList() { List<User> userList = userRepository.findAll(); return userList; } @RequestMapping(value = "/user/test", method = RequestMethod.GET) public String userTest() { long count = userRepository.count(); return "success:" + count; } } 复制代码
SpringBoot底层是Spring框架, Spring框架默认使用JCL作为日志抽象层;
SpringBoot选用SLF4作为日志抽象层,logback作为日志实现层;
Jul - Java util logging
java.util.logging包下面的一款日志框架,属于JDK自带的日志实现;
Log4j
Apache的一个开源项目,可以不需要依赖第三方技术,直接记录日志;
Log4j2
log4j2和log4j是同一个作者开发,只不过log4j2是重新架构的一款日志组件,抛弃了log4j的不足,以及吸取了优秀的logback的设计重新推出的一款新组件;
Jcl - Jakarta Commons Logging
jcl是apache公司开发的一个 抽象日志通用框架 ,本身不实现日志记录,但是提供了记录日志的抽象接口方法(info,debug,error…)。jcl默认使用jul打印日志,如果项目依赖了log4j jar包,则使用log4j打印日志;
Logback
Logback是由log4j创始人设计的一个开源日志组件。LogBack被分为3个组件,logback-core, logback-classic 和 logback-access;
Slf4j - Simple Logging Facade for Java
Java的简单日志门面(SLF4J)用作 各种日志框架的简单门面或抽象 ,像jul、logback和log4j等。SLF4J允许最终用户在部署时插入所需的日志框架,如果在项目中使用slf4j必须加一个依赖jar,即slf4j-api-xxx.jar。slf4j本身不记录日志,通过绑定器绑定一个具体的日志框架来完成日志记录。
日志抽象层 | 日志实现层 |
---|---|
Jcl、Slf4j、Jboss-logging | Log4j、 Jul、Log4j2、Logback |
SLF4j的使用
日志记录方法的调用,不应该直接调用日志的实现类,而是应该调用日志抽象层里面的方法。
自己的应用(slf4j+logback), spring(commons-logging), Hibernate(jboss-logging), Mybatis各框架都使用了不同的日志框架,如何统一日志记录,使得所有的框架都使用slf4j输出日志,这是需要解决的问题。
将系统中其他日志框架先排除掉,
用中间包替换掉原有被排除的日志框架
再导入slf4j实现统一的日志输出
底层依赖的日志框架
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>2.1.6.RELEASE</version> <scope>compile</scope> </dependency> 复制代码
依赖关系
总结:
SpringBoot使用slf4j+logback方式进行日志记录;
SpringBoot也把其他日志换成了slf4j;
引入其他日志框架,必须把本框架下的默认依赖的日志框架移除掉(如果有的话);
// 低版本的Spring-core移除commons-logging日志框架 // 高版本的Spring-core默认不集成commons-logging日志框架 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> 复制代码
日志级别
Logger logger = LoggerFactory.getLogger(SpringbootLoggingApplication.class); // 日志的级别 // 由低到高 trace < debug < info < warn < error // 在配置文件中可以调整输出的日志级别,日志就会在该级别及以上的高级别生效输出日志 logger.trace("这是trace信息..."); logger.debug("这是debug信息..."); // 本版本的SpringBoot默认输出info级别及以上的日志, 配置文件中没有指定级别的话默认使用info级别,也叫root级别 - logging.level.root logger.info("这是info信息..."); logger.warn("这是warn信息..."); logger.error("这是error信息..."); 复制代码
日志输出配置文件— application.properties
# application.properties #日志输出级别 logging.level.com.dyh = trace # 日志输出路径 logging.path=/Users/dyh/IdeaProjects/mvn/springboot-logging # 日志输出至文件名, 当然可以直接指定带路径的文件名,这样日志输出路径可不指定 logging.file=springboot-logging.log # 控制台输出日志的格式 logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n # 文件中输出日志的格式 logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} === [%thread] %-5level === %logger{50} === %msg%n 复制代码
日志输出格式说明
%clr(%5p) 表示输出日志的颜色(根据日志级别)
%d 表示日期时间
%thread 表示线程名
%-5level 表示日志级别level向左对齐显示五个字符宽度
%logger{50} 表示日志名字logger最长50个字符(日志所在的包名),否则按句点分割
%msg 日志输出消息
%n 换行符
日志输出配置文件— logback.xml || logback-spring.xml
虽然application.properties能进行日志的配置,但是为了更方便、独立地配置日志,我们一般使用单独的日志配置文件来进行日志配置,如下:
Logging System | Customization |
---|---|
Logback | logback-spring.xml , logback-spring.groovy , logback.xml , or logback.groovy |
Log4j2 | log4j2-spring.xml or log4j2.xml |
JDK (Java Util Logging) | logging.properties |
SpringBoot
应用会默认识别 logback.xml
文件作为日志的配置文件,但是在 logback.xml
中无法使用 SpringBoot
配置的高级功能,如 spring.profiles.active=dev
, 即在不同环境中使用不同的日志配置文件,因此一般使用 logback-spring.xml
作为指定的日志配置文件
<!-- logback-spring.xml --> <?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--自定义属性,可以在之后使用;定义日志文件的存储地址 勿在LogBack的配置中使用相对路径--> <property name="LOG_HOME" value="/Users/dyh/IdeaProjects/mvn/springboot-logging" /> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符, %yellow() 定义某个输出信息的颜色--> <pattern>%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS}) %highlight([%thread] %-5level) %green(%logger{50}) - %cyan(%msg%n)</pattern> </encoder> </appender> <!-- 按照每天生成日志文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/TestWeb-%d{yyyy-MM-dd}.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--日志文件最大的大小--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 日志输出级别 --> <!--根据不同的环境输出不同级别的日志信息--> <springProfile name="dev"> <!-- 在application.properties文件中指定 spring.profiles.active=dev --> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </springProfile> <springProfile name="prod"> <!-- 在application.properties文件中指定 spring.profiles.active=prod --> <root level="WARN"> <appender-ref ref="STDOUT" /> </root> </springProfile> </configuration> 复制代码
按照日志适配图进行切换即可