springboot定制动态banner
banner动态生成工具
http://www.network-science.de/ascii/
springboot整合Lombok
(1)Lombok能以简单的注解形式来简化Java代码,提高开发人员的开发效率
(2)Lombok能通过@Data注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、 hashCode、toString方法
在idea中引入lombok框架,可以使用@Slf4j注解,在编译时动态成功日志调用实例。
开发步骤:
(一)idea 下安装Lombok插件
(1)File---->Settings---->Plugins---->选择Marketplace---->搜索lombok---->点击Install
(2)注意:安装完毕后开启注解权限才能正常使用:
setting—–>Build,Execution,Deployment—–>Compiler—–>Annontation
Processors –—>勾选Enable annotation processing–> Apply(3)重启idea
安装成功如下图:
(二)引入org.projectlombok
<!--引入lombok-->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<optional>true</optional>
</dependency>
Springboot实现logback日志
SpringBoot默认的日志实现是使用slf4j+logback,这种实现类似于JDBC + 数据库驱动(统一接口+实现类)。
slf4j叫做日志门面,是一个统一的日志接口层,各种具体的日志实现都可以通过slf4j来实现,比如logback就是一个具体的日志门面的实现。
由于Springboot默认引用了logback日志框架,所以无需再引入logback 相关jar包,只需要配置logback-spring.xml实现
logback-spring.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<!--<property name="log.path" value="D:/bmslog" />-->
<springProperty scope="context" name="log.path" source="logging.path" defaultValue="/logging/ning/logs/bms"/>
<contextName>logback</contextName>
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 DEBUG 日志 -->
<!--RollingFileAppender的作用是滚动记录文件,先将日志记录到指定文件,当符合某个条件时再将日志记录到其他文件-->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_debug.log</file>
<!--日志文件输出格式-->
<encoder>
<!--
日志输出格式:
%d表示日期时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,%n是换行符
-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy>的作用是当发生滚动时,定义RollingFileAppender的行为,其中TimeBasedRollingPolicy是最常用的滚动策略,它根据时间指定滚动策略,既负责滚动也负责触发滚动-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<!-- <onMatch>:用于配置符合过滤条件的操作 -->
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 WARN 日志-->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
-->
<!--<logger name="org.springframework.web" level="info"/>-->
<!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
-->
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->
<!-- 多环境配置管理 -->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<root level="info">
<!--<logger name="com.nmys.view" level="debug"/>-->
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
<!--线上环境:-->
<springProfile name="prod">
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
<!--测试环境:-->
<springProfile name="test">
<root level="info">
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
</configuration>
application.yml配置
yml格式检查工具
http://www.bejson.com/validators/yaml_editor/
spring:
profiles:
active: dev
logging:
path: F:\springBootProject\backStageManagement\demolog
其他关于日志的详细分析:https://blog.youkuaiyun.com/qq_42145871/article/details/89077537
日志配置文件的名称:logback-spring.xml或者logback.xml,推荐使用logback-spring.xml,因为配置成logback-spring.xml是由SpringBoot自己加载,不是由日志框架直接加载,可以使用高级特性:,通过这个特性,可以配置不同环境配置不同的日志级别,比如开发环境使用debug,生产环境使用info。
Springboot整合Mybatis
(一)引入mysql和mybatis能力
<!-- 引入mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 引入mysql数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
(二)application.yml配置mysql数据源和mybatis
#配置MySQL数据库
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/friday?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
username: root
password: 123456
platform: mysql
#配置mybatis
mybatis:
type-aliases-package: com.study.demo.model
mapper-locations: classpath:/mybatis-mappers/*
configuration:
#驼峰方式配对
mapUnderscoreToCamelCase: true
(三)编写实体类
package com.study.demo.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data //lombok插件中@Data标签自动实现SetGet方法
@EqualsAndHashCode(callSuper = true)//
public class SysUser extends BaseEntity<Long> {
private static final long serialVersionUID = -6525908145032868837L;
private String username;
private String password;
private String nickname;
private String headImgUrl;
private String phone;
private String telephone;
private String email;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
private Integer sex;
private Integer status;
private String intro;
public interface Status {
int DISABLED = 0;
int VALID = 1;
int LOCKED = 2;
}
}
(四)编写mapper映射接口
package com.study.demo.dao;
import com.study.demo.model.SysUser;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
//mybatis generator 代码自动配置
public interface UserDao {
@Select("select * from sys_user t where t.username = #{username}")
SysUser getUser(String username);
}
(五)编写测试类(检验以上操作是否成功)
package com.study.demo;
import com.study.demo.dao.UserDao;
import com.study.demo.model.SysUser;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.Date;
@RunWith(SpringRunner.class)
@SpringBootTest
class DemoApplicationTests {
@Resource
UserDao userDao;
@Test
void contextLoads() {
System.out.println(userDao.getUser("ning"));
}
}
@Mapper
(1)把mapper这个Dao交给Spring管理,
(2)为了不再写mapper映射文件
(3)添加@Mapper 注解的接口生成一个实现类
@Select
@Select("select * from sys_user t where t.username = #{username}")
SysUser getUser(String username);
考虑表字段和Java属性字段映射的问题
- mapUnderscoreToCamelCase(驼峰命名方式转换)方式
mybatis:
type-aliases-package: com.study.demo.model
mapper-locations: classpath:/mybatis-mappers/*
configuration:
#驼峰方式配对
mapUnderscoreToCamelCase: true
-
数据库字段别名的方式
-
@Results注解方式
-
@Results注解方式
动态sql:#{}和${}
select *from user where name=#{name};
select *fromuser where name=${name};
两者解析结果相同;name=ning
预编译中处理的不一样,#为占位符,$为字符串拼接
@Insert注解
不需要返回主键
@Insert("insert into sys_role_user(userId, roleId) values(#{userId}, #{roleId})")
int save(SysRoleUser sysRoleUser);
返回自增主键@Option
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into sys_user(username, password, nickname, headImgUrl, phone, telephone, email, birthday, sex, status, createTime, updateTime) values(#{username}, #{password}, #{nickname}, #{headImgUrl}, #{phone}, #{telephone}, #{email}, #{birthday}, #{sex}, #{status}, now(), now())")
int save(SysUser user);
返回非自增主键
@SelectKey(statement= "SELECT LAST_INSERT_ID()", keyProperty = "id", resultType=Long.class, before=false)
@Insert("insert into sys_user(username, password, nickname, headImgUrl, phone, telephone, email, birthday, sex, status, createTime, updateTime) values(#{username}, #{password}, #{nickname}, #{headImgUrl}, #{phone}, #{telephone}, #{email}, #{birthday}, #{sex}, #{status}, now(), now())")
int save(SysUser user);
@Delete注解
@Delete("delete from sys_user where id = #{id}")
int deleteUser(Long id);
@Update注解
@Update("update sys_user t set t.password = #{password} where t.id = #{id}")
int changePassword(@Param("id") Long id, @Param("password") String password);
mybatis中文文档
https://mybatis.org/mybatis-3/zh/index.html
http://www.mybatis.cn/archives/789.html
(1)编写抽象方法
int updateUser(SysUser user);
(2)配置XML扫描路径
application.yml文件中配置mapper扫描路径:
mapper-locations:classpath:/mybatis-mappers/*
(3)编写mapper.xml
<?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.study.demo.dao.UserDao">
<update id="updateUser">
update sys_user t
<set>
<if test="username != null">
username = #{username},
</if>
<if test="nickname != null">
nickname = #{nickname},
</if>
<if test="headImgUrl != null">
headImgUrl = #{headImgUrl},
</if>
<if test="phone != null">
phone = #{phone},
</if>
<if test="telephone != null">
telephone = #{telephone},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="status != null">
status = #{status},
</if>
updateTime = #{updateTime}
</set>
where t.id = #{id}
</update>
</mapper>
@Configguration注解是告诉spring这个类是一个配置类,相当于我们的xml文件 @ComponentScan则是指定需要spring来扫描的包,相当于xml中的context:component-scan属性。
@Bean后边的initMethod和destroyMethod就是在声明这是一个bean的同时指定了init和destroy方法
@Mapper 注解,相当于mapper.xml中的mapper标签
推荐一个方便在mapper接口方法和mapper XML文件之间来回切换的插件
安装步骤:
(1)File–>Settings–>Plugins–>选择Marketplace–>搜索free mybatis plugin–>点击Install
(2)重启idea
idea下配置Mapper自动提示
File---->Settings---->Language&Frameworks---->选择Schemas and DTDs ---->配置URL及dtd文件所在的Location
URL: http://mybatis.org/dtd/mybatis-3-mapper.dtd
Location:【dtd文件所在的地址】
mybatis-3-mapper.dtd文件云盘地址:
链接: https://pan.baidu.com/s/1qh2l0cmXm_UgqfMFxufU0A 提取码: wyay
整合Druid连接池
(1)引入Druid连接池启动器
<!-- 引入druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
(2)application.yml配置Druid数据源(放到MySQL数据源配置下面)
#连接池指定Springboot2.0版本默认使用HikariCP,此处要替换成Druid
#下面为连接池的补充设置,应用到上面所有数据源中
type: com.alibaba.druid.pool.DruidDataSource
#初始化大小,最小,最大
initialSize: 1
minIdle: 3
maxActive: 20
#配置获取连接等待超时的时间
maxWait: 60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
#配置一个连接在池中的最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 30000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#是否缓存PreparedStatements,打开PSCache,并且指定每个连接上PSCache的大小。官方建议MYSQL下关闭,如果用SQL防火墙建议打开
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
(3)检验配置是否生效
在 System.out.println(dataSource.getClass());句加断点debug模式启动应用
package com.study.demo;
import com.study.demo.dao.UserDao;
import com.study.demo.model.SysUser;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Date;
@RunWith(SpringRunner.class)
@SpringBootTest
class DemoApplicationTests {
@Autowired
DataSource dataSource;
@Resource
UserDao userDao;
@Test
void contextLoads() {
System.out.println(dataSource.getClass()); //该句加断点debug模式启动应用
System.out.println(userDao.getUser("ning"));
}
}
找到下图位置查看配置数据是否成功:
解决方案:添加 druid Java注解配置
package com.study.demo.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author ning
* @date
*/
@Configuration //配置类注解,被自动扫描发现
public class DruidConfig {
@Bean(destroyMethod = "close",initMethod = "init")
//@PropertySource("classpath:application.yml") //多文件指明配置源文件位置
@ConfigurationProperties(prefix = "spring.datasource") //指明前缀
public DataSource getDataSource(){
return new DruidDataSource();
}
}
注意:
(1)当application.properties和yml文件在并存时(同一目录下),application.properties优先级更好,会先读它,若它没有,再去读yml中的值
(2)如果有多个properties文件存在,同样可以使用@ConfigurationProperties注解来加载指定配置文件
@ConfigurationProperties(prefix = “book”,locations = {“classpath:book.properties”}) //book.properties文件在/src/main/resources目录下
整合Junit单元测试
<!--引入测试启动类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--引入junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProjectFontControllerTest {
}
@RunWith(SpringRunner.class)注解的作用:
让测试在Spring容器环境下执行。如测试类中无此注解,将导致service,dao等自动注入失败。
整合Thymeleaf模板引擎依赖
<!--Thymeleaf模板依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--引入web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.properties配置thymeleaf
#thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.contentType=text/html
spring.thymeleaf.cache=false
application.yml配置thymeleaf
thymeleaf:
cache: false #关闭缓存
mode: HTML5 #设置模板类型
encoding: utf-8 #设置编码
prefix: classpath:/templates/ #默认路径
suffix: .html #默认模板类型
模板引擎
模板引擎包括两部分:模板和数据
通过模板引擎定义整个项目的模板结构(本次项目是基于HTML5结构),通过模板引擎把数据填充到对应的模板中,绘制输出结果页
相关语法推荐博客:
https://www.jianshu.com/p/908b48b10702
SpringMVC常用注解:
@Controller
(1)在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。
(2)Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象,它们可以通过Controller 的方法参数灵活的获取到。
(3)@Controller用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。
(4) @Controller只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。
@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@ResponseBody(一般返回json数据)
作用:
该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
@Resource和@Autowired
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
public class TestServiceImpl {
// 下面两种@Autowired只要使用一种即可
@Autowired
private UserDao userDao; // 用于字段上
@Autowired
public void setUserDao(UserDao userDao) { // 用于属性的方法上
this.userDao = userDao;
}
}
(2)@Resource
@Resource默认按照ByName自动注入,需要导入包javax.annotation.Resource。
@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
public class TestServiceImpl {
// 下面两种@Resource只要使用一种即可
@Resource(name="userDao")
private UserDao userDao; // 用于字段上
@Resource(name="userDao")
public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
this.userDao = userDao;
}
}
注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。
@Resource装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
映射方法入参
@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。如:
@Controller
public class TestController {
@RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)
public String getLogin(@PathVariable("userId") String userId,
@PathVariable("roleId") String roleId){
System.out.println("User Id : " + userId);
System.out.println("Role Id : " + roleId);
return "hello";
}
}
@requestParam
@requestParam主要用于在SpringMVC后台控制层获取参数,然后映射请求参数。
即用于将请求参数区数据映射到功能处理方法的参数上
它有三个常用参数:defaultValue= “0”, required = false, value = “isApp”;
defaultValue 表示设置默认值,required 该参数是否必需。默认为true,value 值即请求参数名。
required设置成false的参数对应的处理函数的参数类型必须是对象类型,否则回报错500!
@RequestMapping("class")
@Controller
public class TestController{
@RequestMapping("student")
public String TestController(@RequestParam(value="username") String un, @RequestParam(value="age",required=false) int age)
{
System.out.println("a student's username: "+un+", age: "+age);
return "success";
}
}
结果会报错,因为age虽然设置了required为false,请求参数可以不包含age,但是在处理函数中对应的参数类型是int,int是基本数据类型,无法为空,所以报错。解决办法是将int改为Integer。
如果我们仍然想用基本类型作为参数类型,那么可以用到@RequestParam注解的defaultValue属性来指定默认值。
/**
*http://localhost:8080/class/student?username=admin&age=11
* @RequestParam("id") 带参映射
* @param id
* @return
*/
@Controller
@RequestMapping("class")
public class TestController{
@RequestMapping("student")
//required = false时,若url中param参数不存在,则param变量赋值为null,但如果param的类型不是引用类型,则会报错。
//required = true,当defaultValue = "0"时,若url中param参数不存在,则默认为0,若无defaultValue = "0"则报错
public String TestController(@RequestParam(value="username") String un, @RequestParam(value="age",required=false, defaultValue="0") int age)
{
System.out.println("a student's username: "+un+", age: "+age);
return "success";
}
@RequestParam详解以及加与不加的区别参考博客:
pojo对象绑定请求参数
将前端传入的属性值映射到一个对象中作为入参
处理模型数据
ModelAndView
ModelAndView类别提供实作View接口的对象来作View的参数:
ModelAndView(View view)
ModelAndView(View view, Map model)
ModelAndView(View view, String modelName, Object modelObject)
public ModelAndView xxxxmethod(String someparam)
{
ModelAndView mv=new ModelAndView();
//ModelAndView mv=new ModelAndView("welcome");
mv.setViewName("welcome"); //返回的文件名
mv.addObject("message","hello kitty");
//类
User user = new User();
user.setAge(20);
user.setName("nihao");
mv.addObject("user",user);
//List
List<String> list = new ArrayList<String>();
list.add("java");
list.add("c++");
list.add("oracle");
mv.addObject("bookList", list);
//Map
Map<String,String> map = new HashMap<String,String>();
map.put("zhangsan", "北京");
map.put("lisi", "上海");
map.put("wangwu", "深圳");
mv.addObject("map",map);
return mv;
}
【方法使用】:给ModelAndView实例设置view的方法有两个:setViewName(String viewName) 和setView(View view)。
前者是使用viewName,后者是使用预先构造好的View对象。其中前者比较常用。事实上View是一个接口,而不是一个可以构造的具体类,我们只能通过其他途径来获取View的实例。对于viewName,它既可以是jsp的名字,也可以是tiles定义的名字,取决于使用的ViewNameResolver如何理解这个view name。
对应如何给ModelAndView实例设置model则比较复杂。有三个方法可以使用:
addObject(Object modelObject);
addObject(String modelName, Object modelObject);
addAllObjects(Map modelMap);
通过ModelAndView构造方法可以指定返回的页面名称,也可以通过setViewName()方法跳转到指定的页面 ,
使用addObject()设置需要返回的值,addObject()有几个不同参数的方法,可以默认和指定返回对象的名字。
总结来看ModelAndView对象有两个作用:
作用一: 设置转向地址,如下所示(这是ModelAndView和ModelMap的主要区别)
ModelAndView的实例是由用户手动创建的,这也是和ModelMap的一个区别。
public ModelAndView xxxxmethod(String someparam)
{
//省略方法处理逻辑若干
//构建ModelAndView实例,并设置跳转地址
ModelAndView view = new ModelAndView("path:handleok");
//将数据放置到ModelAndView对象view中,第二个参数可以是任何java类型
view.addObject("key",someparam);
......
//返回ModelAndView对象view
return view;
}
作用二 :用于传递控制方法处理结果数据到结果页面,也就是说我们把需要在结果页面上需要的数据放到ModelAndView对象中即可
他的作用类似于request对象的setAttribute方法的作用,用来在一个请求过程中传递处理的数据。通过以下方法向页面传递参数:
addObject(String key,Object value);
在这些构造函数中最简单的ModelAndView是持有View的名称返回,之后View名称被view resolver,也就是实作org.springframework.web.servlet.View接口的实例解析,
例如: InternalResourceView或JstlView等等:ModelAndView(String viewName);
(1)如果您要返回Model对象,则可以使用Map来收集这些Model对象,然后设定给ModelAndView,使用下面这个版本:
ModelAndView:ModelAndView(String viewName, Map model)
Map对象中设定好key与value值,之后可以在视图中取出
(2)如果您只是要返回一个Model对象,则可以使用下面这个 ModelAndView版本:ModelAndView(String viewName, String modelName, Object modelObject),其中modelName,您可以在视图中取出Model并显示
Model
Model 是一个接口, 其实现类为ExtendedModelMap,继承了ModelMap类。
public class ExtendedModelMap extends ModelMap implements Model
ModelMap
public class ModelMap extends LinkedHashMap<String, Object>
ModelMap对象主要用于传递控制方法处理数据到结果页面, 也就是说我们把结果页面上需要的数据放到ModelMap对象中即可,他的作用类似于request对象的setAttribute方法的作用,用来在一个请求过程中传递处理的数据。
ModelMap或者Model通过addAttribute方法向页面传递参数,其中addAttribute方法参数有多种方式:
public ModelMap addAttribute(String attributeName, Object attributeValue){...}
public ModelMap addAttribute(Object attributeValue){...}
public ModelMap addAllAttributes(Collection<?> attributeValues) {...}
public ModelMap addAllAttributes(Map<String, ?> attributes){...}
在页面上可以通过el变量方式${key}或者bboss的一系列数据展示标签获取并展示modelmap中的数据。
modelmap本身不能设置页面跳转的url地址别名或者物理跳转地址,那么我们可以通过控制器方法的返回值来设置跳转url地址别名或者物理跳转地址。 比如:
public String xxxxmethod(String someparam,ModelMap model)
{
//省略方法处理逻辑若干
//将数据放置到ModelMap对象model中,第二个参数可以是任何java类型
model.addAttribute("key",someparam);
......
//返回跳转地址
return "path:handleok";
}
ModelMap的实例是spirng mvc框架自动创建并作为控制器方法参数传入,用户无需自己创建。
public String xxxxmethod(String someparam,ModelMap model)
{
//省略方法处理逻辑若干
//将数据放置到ModelMap对象model中,第二个参数可以是任何java类型
model.addAttribute("key",someparam);
......
//返回跳转地址
return "path:handleok";
}
@SessionAttribute
(1)@SessionAttribute 指的是 springmvc 的 session。向其中添加值得时候,同时会向 http session中添加一条。
(2)在 sessionStatus.setComplete();的时候,会清空 sprinmvc 的 session,同时清除对应键的 http session内容 但是通过,request.getSession.setAttribute() 方式添加的内容不会被清除掉。
(2)其他情况下,springmvc session 和 http session使用情况相同。
关于@SessionAttribute详细总结
引入Xadmin以及静态依赖依赖
Xadmin官网 http://x.xuebingsi.com/
(一)引入所需资源文件
(二)修改路径
(1)引入thymeleaf模板引擎
<html class="x-admin-sm" xmlns:th="http://www.thymeleaf.org">
(2)修改静态文件路径
header.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link rel="stylesheet" th:href="@{/xadmin/css/font.css}" />
<link rel="stylesheet" th:href="@{/xadmin/css/xadmin.css}" />
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript"src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
<script th:src="@{/xadmin/lib/layui/layui.js}" charset="utf-8"></script>
<script type="text/javascript" th:src="@{/xadmin/js/xadmin.js}"></script>
<script type="text/javascript" th:src="@{/ztree/jquery.ztree.all-3.5.min.js}"></script>
<script type="text/javascript" th:src="@{/ztree/ztree-menu.js}"></script>
<script type="text/javascript" th:src="@{/my/js/cookie.js}"></script>
<script type="text/javascript" th:src="@{/my/js/permission.js}"></script>
<!-- 让IE8/9支持媒体查询,从而兼容栅格 -->
<!--[if lt IE 9]>
<script src="https://cdn.staticfile.org/html5shiv/r29/html5.min.js"></script>
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
</html>
首页面引入header.html
<head>
<meta charset="UTF-8">
<title>Friday-System</title>
<header th:replace="header::html"></header>
</head>
登录页面引入header.html
<header th:replace="header::html"></header>
<link rel="stylesheet" th:href="@{/xadmin/css/login.css}" />
</head>
有需要解答代码问题或者项目需求请联系我~~~QQ:2624939202