springboot权限管理系统之基础搭建及知识准备

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.csdn.net/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属性字段映射的问题

  1. mapUnderscoreToCamelCase(驼峰命名方式转换)方式
mybatis:
  type-aliases-package: com.study.demo.model
  mapper-locations: classpath:/mybatis-mappers/*
  configuration:
    #驼峰方式配对
    mapUnderscoreToCamelCase: true
  1. 数据库字段别名的方式

  2. @Results注解方式
    在这里插入图片描述

  3. @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详解以及加与不加的区别参考博客:

https://blog.csdn.net/a447332241/article/details/76714148?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

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详细总结

http://blog.sina.com.cn/s/blog_6d3c1ec601018cx1.html

引入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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值