SpringBoot03

SpringBoot应用开发入门

1、使用IDEA中提供的项目初始化器完成项目的创建

这里的视图采用JSP,SpringBoot已经不建议使用JSP

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yan</groupId>
    <artifactId>demo0111</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo0111</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.6</version>
        </dependency>
        <!-- 实际上由于JavaEE中的包名的变更已经有问题了 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2、使用yaml格式的配置文件,所以需要修改application.properties为application.yml

# 数据源的配置,这里没有引入druid,所以使用的是HikariCP连接池
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///test?serverTimezone=UTC
    username: root
    password: 123456

编写创建表所使用的sql语句和初始化表的数据

在resources目录下创建文件夹database,其中添加文件schema.sql其中包含创建表的sql语句,添加文件data.sql其中包含初始化表数据的sql语句

create table if not exists tb_users(
    id bigint primary key auto_increment,
    username varchar(32) not null unique,
    password varchar(32) not null,
    birth date,
    sex boolean default 1
)engine=innodb default charset utf8;

初始化表中的数据

delete from tb_users;

insert into tb_users values(1,'zhangsan','123456','1989-2-3',0);
insert into tb_users values(2,'lisi','123456','2017-4-5',0);
insert into tb_users values(3,'yanjun','123456','2018-4-8',1);

添加配置使SpringBoot启动时自动执行sql文件完成表的初始化操作

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///test?serverTimezone=UTC
    username: root
    password: 123456
  sql:
    init:
      mode: always
      schema-locations: classpath:database/schema.sql
      data-locations: classpath:database/data.sql

添加web应用相关的配置,例如视图解析器所使用的参数

spring:
  mvc:
    view:
      prefix: /WEB-INF/content/  # 视图解析器的前缀
      suffix: .jsp   # 视图解析器的后缀

在src/main目录下创建文件夹webapp用于存储jsp页面,静态资源遵循管理存储在resources/static目录下

添加控制器

@Controller
public class IndexController {
    @RequestMapping({"","/","index"})
    public String index(){
        return "index";
    }
}

如果页面显示正常,则表示开发环境已经搭建完毕

3、使用MyBatis的反向引擎生成对应的实体类、映射源文件和接口

需要引入反向映射的插件mybatis-generator-maven-plugin。需要注意一般在具体开发中为了避免在开发工程中引入额外的内容,所以一般是创建一个独立项目执行反向映射

针对反向映射生成的文件进行调整。

映射元文件

<?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.yan.dao.UserMapper">
    <resultMap id="BaseResultMap" type="com.yan.entity.User">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="username" jdbcType="VARCHAR" property="username"/>
        <result column="password" jdbcType="VARCHAR" property="password"/>
        <result column="birth" jdbcType="DATE" property="birth"/>
        <result column="sex" jdbcType="BOOLEAN" property="sex"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, username, password, birth, sex
    </sql>
    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from tb_users
        where id = #{id,jdbcType=BIGINT}
    </select>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
        delete
        from tb_users
        where id = #{id,jdbcType=BIGINT}
    </delete>
    <insert id="insertSelective" parameterType="com.yan.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into tb_users
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null">
                username,
            </if>
            <if test="password != null">
                password,
            </if>
            <if test="birth != null">
                birth,
            </if>
            <if test="sex != null">
                sex,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="username != null">
                #{username,jdbcType=VARCHAR},
            </if>
            <if test="password != null">
                #{password,jdbcType=VARCHAR},
            </if>
            <if test="birth != null">
                #{birth,jdbcType=DATE},
            </if>
            <if test="sex != null">
                #{sex,jdbcType=BOOLEAN},
            </if>
        </trim>
    </insert>
    <update id="updateByPrimaryKeySelective" parameterType="com.yan.entity.User">
        update tb_users
        <set>
            <if test="username != null">
                username = #{username,jdbcType=VARCHAR},
            </if>
            <if test="password != null">
                password = #{password,jdbcType=VARCHAR},
            </if>
            <if test="birth != null">
                birth = #{birth,jdbcType=DATE},
            </if>
            <if test="sex != null">
                sex = #{sex,jdbcType=BOOLEAN},
            </if>
        </set>
        where id = #{id,jdbcType=BIGINT}
    </update>
    <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.yan.entity.User">
        select
        <include refid="Base_Column_List"/>
        from tb_users where 1=1
        <if test="username != null">
            and username like #{username,jdbcType=VARCHAR}
        </if>
        <if test="password != null">
            and password = #{password,jdbcType=VARCHAR}
        </if>
        <if test="birth != null">
            and birth = #{birth,jdbcType=DATE}
        </if>
        <if test="sex != null">
            and sex = #{sex,jdbcType=BOOLEAN}
        </if>
        <if test="id!=null">
            and id = #{id,jdbcType=BIGINT}
        </if>
    </select>
</mapper>

实体类

@Data
public class User implements Serializable {
    private Long id;
    private String username;
    private String password;
    private Date birth;
    private Boolean sex;
    
}

新增一个mapper接口的通用接口

public interface SqlMapper<T extends Serializable,ID extends Serializable> {
    int deleteByPrimaryKey(ID id);
    int insertSelective(T record);
    T selectByPrimaryKey(ID id);
    int updateByPrimaryKeySelective(T record);
    List<T> selectByExample(T record);
}

修改生成的映射接口

@Repository   //添加这个注解的目的主要在于使IDEA不报错,实际上没有必要
public interface UserMapper extends SqlMapper<User,Long>{
}

添加MyBatis相关的配置

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   # 用于开发阶段在控制台上输出所指定的SQL语句
  mapper-locations: classpath:mapper/*.xml  # 用于注册映射元文件

修改主类,在主类上添加一个注解用于自动扫描mapper接口

@SpringBootApplication
@MapperScan(value = "com.yan.dao",markerInterface = SqlMapper.class)
public class Demo0111Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo0111Application.class, args);
    }

}

使用SpringBoot提供的单元测试检测配置是否有错误。在具体开发中一般不会针对dao进行测试,因为dao的具体实现实际上是依赖于MyBatis框架来实现的

Spring Boot的项目初始化器自动提供了一个测试主类,可以使用这个测试主类进行简单验证

@SpringBootTest
class Demo0111ApplicationTests {
    @Autowired
    private UserMapper userMapper;
    @Test
    void contextLoads() {
        System.out.println(userMapper);

        User user=new User();
        user.setUsername("zhaoliu");
        user.setPassword("666666");
        user.setBirth(new Date());
        user.setSex(false);
        int rows = userMapper.insertSelective(user);
        Assertions.assertEquals(1,rows);
    }

}

4、添加业务层,不要忘记在业务代码中引入声明式事务管理。从理论上来说,不应该使用实体类进行层与层之间的数据传递,但是为了简化开发,提高开发效率,可以使用实体类用于数据传递

首先定义接口,推荐使用的是JDK动态代理

public interface IUserServ {
    boolean create(User user);
    boolean exists(String username);
}

添加实现类

@Service
public class UserServImpl implements IUserServ{
    @Autowired
    private UserMapper userMapper;

    @Override
    public boolean create(User user) {
        Assert.notNull(user,"参数不允许为空!");
        Assert.hasText(user.getUsername(),"用户名称不允许为空!");
        Assert.hasLength(user.getPassword(),"用户口令不允许为空!");
        boolean bb=exists(user.getUsername());
        if(bb)
            throw new IllegalArgumentException("用户名称已经被占用!");
        int res=userMapper.insertSelective(user);
        return res>0;
    }

    @Override
    public boolean exists(String username) {
        Assert.hasText(username,"用户名称不能为空!");
        User tmp=new User();
        tmp.setUsername(username.trim());
        List<User> userList=userMapper.selectByExample(tmp);
        return userList!=null && userList.size()>0;
    }
}

进行单元测试,甚至有地方还使用TDD测试驱动开发方式

在具体开发中如果时间允许,实际上针对所有的可能都应该覆盖测试到

@SpringBootTest
class Demo0111ApplicationTests {
    @Autowired
    private IUserServ userService;
    @Test
    void contextLoads() {
    }
    @Test
    void testCreate(){
        User user=new User();
        user.setUsername("yan111");
        user.setPassword("111111");
        user.setSex(false);
        boolean res= userService.create(user);
        Assertions.assertTrue(res);
    }
}

从控制台上查看输出日志信息

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@16b7e04a] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@2042950203 wrapping com.mysql.cj.jdbc.ConnectionImpl@5a566922] will not be managed by Spring
==>  Preparing: select id, username, password, birth, sex from tb_users where 1=1 and username like ?
==> Parameters: yan111(String)
<==      Total: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@16b7e04a]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@b788dc2] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@172290043 wrapping com.mysql.cj.jdbc.ConnectionImpl@5a566922] will not be managed by Spring
==>  Preparing: insert into tb_users ( username, password, sex ) values ( ?, ?, ? )
==> Parameters: yan111(String), 111111(String), false(Boolean)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@b788dc2]

这里开发发现使用的是non transactional SqlSession,没有使用事务

修改主类添加注解@EnableTransactionManagement使声明式事务生效

@SpringBootApplication
@MapperScan(value = "com.yan.dao",markerInterface = SqlMapper.class)
@EnableTransactionManagement
public class Demo0111Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo0111Application.class, args);
    }

}

修改业务实现类,添加事务相关特性的声明注解

@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
@Service
public class UserServImpl implements IUserServ{
    @Autowired
    private UserMapper userMapper;

    @Transactional(readOnly = false,propagation = Propagation.REQUIRED)
    public boolean create(User user) {
        Assert.notNull(user,"参数不允许为空!");
        Assert.hasText(user.getUsername(),"用户名称不允许为空!");
        Assert.hasLength(user.getPassword(),"用户口令不允许为空!");
        boolean bb=exists(user.getUsername());
        if(bb)
            throw new IllegalArgumentException("用户名称已经被占用!");
        int res=userMapper.insertSelective(user);
        return res>0;
    }

    @Override
    public boolean exists(String username) {
        Assert.hasText(username,"用户名称不能为空!");
        User tmp=new User();
        tmp.setUsername(username.trim());
        List<User> userList=userMapper.selectByExample(tmp);
        return userList!=null && userList.size()>0;
    }
}

删除刚刚插入的数据,重新执行单元测试,查看控制器输出

Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1]
JDBC Connection [HikariProxyConnection@1133655596 wrapping com.mysql.cj.jdbc.ConnectionImpl@668cc9a2] will be managed by Spring
==>  Preparing: select id, username, password, birth, sex from tb_users where 1=1 and username like ?
==> Parameters: yan111(String)
<==      Total: 0
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1] from current transaction
==>  Preparing: insert into tb_users ( username, password, sex ) values ( ?, ?, ? )
==> Parameters: yan111(String), 111111(String), false(Boolean)
<==    Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ceb68a1]

从这里可以看到事务生效transactional SqlSession

5、编写表现层相关代码,从而总结出业务层需要提供的方法

修改控制器,如果用户已经登录则直接进入显示所有用户信息【分页】,如果用户没有登录则字节打开登录输入页面

@Controller
public class IndexController {
    @RequestMapping({"","/","index"})
    public String index(HttpServletRequest request){
        HttpSession session= request.getSession();
        Object obj=session.getAttribute("userInfo");
        if(obj!=null && obj instanceof User)
            return "redirect:/admin/user/show";
        else
            return "redirect:/user/login";
    }
}

注意这里的request类型为jakarta.servlet.http.HttpServletRequest,不是原来的包名称。这是因为这里使用的是Tomcat10,支持的是JavaEE9,包名称统一进行了调整

定义用户相关的控制器,因为有登录后和没有登录之分,所以建议使用两个不同的控制器进行处理。非使用一个控制器实际上也可以,但是判断是否需要登录不够方便。

@Controller
@RequestMapping("/user")
public class UserController {
}

需要登录后才能访问的控制器

@Controller
@RequestMapping("/admin/user")
public class AdminUserController {
}

继续修改UserController处理登录流程

@Controller
@RequestMapping("/user")
public class UserController {
    @GetMapping("/login")
    public String login(Model model){
        User user=new User();
        model.addAttribute("user",user);
        return "user/login";
    }
}

在content目录下创建登录输入页面,记住由于包名称的变化,所以前面添加的jstl依赖是有问题的,因为它支持的原始的包名称java…

<form:form action="user/login" method="post"  modelAttribute="user">
    <table>
        <tr>
            <td><form:label path="username">用户名称:</form:label></td>
            <td><form:input path="username"/></td>
            <td><form:errors path="username" /></td>
        </tr>
        <tr>
            <td><form:label path="password">用户口令:</form:label></td>
            <td><form:password path="password"/></td>
            <td><form:errors path="password"/></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="登录系统"/>
                <input type="reset" value="重置数据"/>
            </td>
        </tr>
    </table>
</form:form>

页面正常显示表示显示登录页面的流程正确

定义控制器接收用户提交数据,使用模型驱动的方式接收数据。由于在业务中已经添加了创建方法,所以先调用业务中的创建进行流程的验证

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserServ userService;
    @GetMapping("/login")
    public String login(Model model){
        User user=new User();
        model.addAttribute("user",user);
        return "user/login";
    }
    @PostMapping("/login")
    public String login(User user, Errors errors)throws Exception{
        try {
            boolean res = userService.create(user);
            return "redirect:/admin/user/show";
        } catch (Exception e){
            errors.rejectValue("msg",null,e.getMessage());
            return "user/login";
        }
    }
}

修改jsp页面进行报错信息显示

<form:form action="user/login" method="post" modelAttribute="user">
    <table>
        <caption><form:errors path="msg" cssClass="error"/></caption>
        <tr>
            <td><form:label path="username">用户名称:</form:label></td>
            <td><form:input path="username"/></td>
            <td><form:errors path="username"  cssClass="error"/></td>
        </tr>
        <tr>
            <td><form:label path="password">用户口令:</form:label></td>
            <td><form:password path="password"/></td>
            <td><form:errors path="password" cssClass="error"/></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="登录系统"/>
                <input type="reset" value="重置数据"/>
            </td>
        </tr>
    </table>
</form:form>

在控制器中添加服务器端数据校验

@PostMapping("/login")
    public String login(@Validated User user, Errors errors)throws Exception{
        try {
            boolean res = userService.create(user);
            return "redirect:/admin/user/show";
        } catch (Exception e){
            errors.rejectValue("msg",null,e.getMessage());
            return "user/login";
        }
    }

修改实体类添加验证规则

@Data
public class User implements Serializable {
    private Long id;
    @NotBlank(message = "用户名称不能为空!",groups = UserGroup.LoginFirstGroup.class)
    @Size(min = 6,max = 20,message = "用户名称应该是{min}到{max}个字符",groups = UserGroup.LoginSecondGroup.class)
    private String username;
    @NotBlank(message = "用户口令不能为空!",groups = UserGroup.LoginFirstGroup.class)
    @Size(min = 6,max = 20,message = "用户口令应该是{min}到{max}个字符",groups = UserGroup.LoginSecondGroup.class)
    private String password;

使用校验分组实现第一个校验通过才执行第二个校验,不会所有校验都要执行

public interface UserGroup {
    @GroupSequence({LoginFirstGroup.class,LoginSecondGroup.class})
    public interface LoginGroup{}
    public interface LoginFirstGroup{}
    public interface LoginSecondGroup{}
}

修改控制器的方法添加配置指定执行的验证分组

    @PostMapping("/login")
    public String login(@Validated(UserGroup.LoginGroup.class) User user, Errors errors)throws Exception{
        if(errors.hasErrors())
            return "user/login";
        try {
            boolean res = userService.create(user);
            return "redirect:/admin/user/show";
        } catch (Exception e){
            errors.rejectValue("msg",null,e.getMessage());
            return "user/login";
        }
    }

5、web显示相关配置

默认情况下当SpringBoot应用启动时会在控制台显示一个logo

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.1)

这个logo可以关闭显示

  • 方法1:在配置文件中进行设置 spring.main.show-banner: off,这种方式已经不再推荐使用

  • 方法2:在主类中使用编程的方式关闭logo的显示

SpringApplication application = new SpringApplication(Demo0111Application.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);

setBannerMode参数为Mode,其中可以配置的的值为Mode的枚举类型值,OFF关闭banner显示,CONSOLE默认值表示在控制台上显示logo,LOG表示在日志记录中显示logo

允许用户自定义修改所显示的logo
仅需要创建一个banner.txt文件,放到项目resources目录就行了,banner.txt里的内容,就会显示到logo位置

  • 文字图标在线生成https://bootschool.net/ascii
  • 图片文本生成https://www.degraeve.com/img2txt.php

自定义时需要注意banner.txt的编码字符集问题,可以将txt文件的编码字符集设置为UTF-8即可

允许修改txt文件读取时所识别的编码字符集
spring.banner.charset: UTF-8

日志系统

日志对于应用程序的重要性不言而喻,不管是记录运行情况还是追踪线上问题,都离不开对日志的分析,
在Java领域里存在着多种日志框架,如JUL、Log4j、Log4j2、Commons Loggin、Slf4j和Logback

在开发的时候不应该直接使用日志实现类,应该使用日志的抽象层
Logger logger = LoggerFactory.getLogger(getClass());

控制器编程使用日志系统输出调试信息

private static Logger logger= LoggerFactory.getLogger(UserController.class);
    @PostMapping("/login")
    public String login(@Validated(UserGroup.LoginGroup.class) User user, Errors errors, Model model) throws Exception {
        logger.info("登录系统,提交数据为:"+user);
        if (errors.hasErrors())
            return "user/login";
        try {
            boolean res = userService.login(user);
            if (res) {
                model.addAttribute("userInfo", user);
                logger.debug("登录成功");
                return "redirect:/admin/user/show";
            }else{
                logger.debug("登录失败,重新登录");
                errors.rejectValue("msg",null,"登录失败!请重新登录");
                return "user/login";
            }
        } catch (Exception e) {
            errors.rejectValue("msg", null, e.getMessage());
            return "user/login";
        }
    }

日志级别从小到大为 trace < debug < info < warn < error ,Spring Boot 默认日志级别为 INFO

  • trace含义为追踪,用于指明程序运行轨迹
  • debug意思是调试,实际应用中一般将其作为最低级别,而trace则很少使用
  • info意思是输出重要的信息,使用较多
  • warn意思是警告,使用较多
  • error意思是错误信息,使用较多

如果使用log4j输出日志信息,可以在resources目录下添加log4j.properties

log4j.rootLogger = debug,stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

如果需要log4j日志框架无效,只要把resources目录下的log4j.properties移动到其它位置即可。

SpringBoot默认采用logback日志框架,可以配置采用其他框架。可以直接在application.yml配置,例如日志级别、输出文件等
配置信息
logging.level.root=info
logging.file=./logs/log.log #输出位置
logging.config=classpath:logback-spring.xml配置中指定了配置文件,在项目resources文件夹下。也可以不指定,只要配置文件名按默认风格命名即可

6、登录成功后显示用户信息

分页相关参数的传递

@Data
public class PageBean implements Serializable {
    private int pageNum;//当前页码值
    private int rowsNum;//总行数
    private int maxPage;//最大页码值
    private int rowsPerPage;//每页行数
}

首先控制台结束分页相关数据,如果没有对应的数据,则会使用RequestParam中定义的默认值

实际上分页处理是依靠pagehelper插件实现的,并不是使用MyBatis的rowbound进行分页的

  • 逻辑分页和物理分页的问题

使用pagehelper是物理分页,需要添加依赖pagehelper-spring-boot-starter

配置相关的参数

pagehelper:
  helper-dialect: mysql  # 告知插件所使用的数据库方言,因为不同的数据库平台使用的不同的关键字实现分页处理。例如mysql使用limit,oracle使用rownum
  reasonable: true # 设置分页插件针对分页参数进行合理化处理,例如没有-1页

业务类中查询操作

    public List<User> getByPage(PageBean pages) {
        Page<Object> pageInfo = PageHelper.startPage(pages.getPageNum(), pages.getRowsPerPage());
        List<User> res=userMapper.selectByExample(null);
        pages.setPageNum(pageInfo.getPageNum());
        pages.setRowsNum(pageInfo.getTotal());
        pages.setMaxPage(pageInfo.getPages());
        return res;
    }

控制器调用业务方法执行查询,并将查询结果存储到model中,转向显示页面进行数据显示

@Controller
@RequestMapping("/admin/user")
public class AdminUserController {
    @Autowired
    private IUserServ userService;
    /**
     * 用于分页显示所有的用户信息
     *
     * @param page 需要显示的页码值
     * @param size 每行行数
     * @return 逻辑地址名,需要在页面上显示的数据会通过方法参数model进行数据传递
     */
    @RequestMapping("/show")
    public String show(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "15") int size, Model model) {
        PageBean pages=new PageBean();
        pages.setPageNum(page);
        pages.setRowsPerPage(size);
        List<User> userList=userService.getByPage(pages);
        model.addAttribute("userList",userList);
        model.addAttribute("pages",pages);
        return "user/show";
    }
}

页面显示报错,原因是jstl 1.2中的包名称和当前的包名称不一致,应该修改对应的依赖

        <dependency>
            <groupId>jakarta.servlet.jsp.jstl</groupId>
            <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>jakarta.servlet.jsp.jstl</artifactId>
            <version>3.0.1</version>
        </dependency>

显示数据的jsp页面

<table border="1" width="60%">
    <thead>
    <tr>
        <th>用户编号</th>
        <th>用户名称</th>
        <th>用户口令</th>
        <th>出生日期</th>
        <th>性别</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    <c:forEach items="${userList}" var="user">
        <tr>
            <td>${user.id}</td>
            <td>${user.username}</td>
            <td>${user.password}</td>
            <td><fmt:formatDate value="${user.birth}" pattern="yyyy年M月d日"/></td>
            <td>${user.sex?"男":"女"}</td>
            <td>
                <c:url var="u1" value="/admin/user/remove">
                    <c:param name="id" value="${user.id}"/>
                </c:url>
                <a href="${u1}">删除</a>
                <c:url var="u1" value="/admin/user/load">
                    <c:param name="id" value="${user.id}"/>
                </c:url>
                <a href="${u1}">修改</a></td>
        </tr>
    </c:forEach>
    </tbody>
    <tfoot>
    <c:if test="${(not empty pages) and (pages.maxPage gt 1)}">
        <tr>
            <td colspan="6" align="right">
                <c:url var="u1" value="/admin/user/show">
                    <c:param name="page" value="1"/>
                    <c:param name="size" value="${pages.rowsPerPage}"/>
                </c:url>
                <a href="${u1}">第一页</a>
                <c:if test="${pages.lastPage gt 0}">
                    <c:url var="u1" value="/admin/user/show">
                        <c:param name="page" value="${pages.lastPage}"/>
                        <c:param name="size" value="${pages.rowsPerPage}"/>
                    </c:url>
                    <a href="${u1}">上一页</a>
                </c:if>
                <c:if test="${pages.nextPage gt 0}">
                    <c:url var="u1" value="/admin/user/show">
                        <c:param name="page" value="${pages.nextPage}"/>
                        <c:param name="size" value="${pages.rowsPerPage}"/>
                    </c:url>
                    <a href="${u1}">下一页</a>
                </c:if>
                <c:url var="u1" value="/admin/user/show">
                    <c:param name="page" value="${pages.maxPage}"/>
                    <c:param name="size" value="${pages.rowsPerPage}"/>
                </c:url>
                <a href="${u1}">末尾页</a>
            </td>
        </tr>
    </c:if>
    <tr>
        <td colspan="3"><a href="/admin/user/toadd">添加新用户</a> </td>
    </tr>
    </tfoot>
</table>

静态资源处理

存放静态资源的位置
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
自定义静态资源目录
优先级顺序/META-INF/resources>resources>static>public,一般使用static目录存放静态资源
不想使用默认静态资源文件夹,允许创建静态资源文件夹进行放置静态资源
spring.web.resources.static-locations= classpath:/test11/
配置了自定义文件夹以后,原有的静态资源文件夹就失效了,想要继续使用,可以把原文件夹也写入该配置中
注意测试中缓存问题,需要进行一下缓存清除。最简单的办法就是使用另一个浏览器进行访问就可以了
代码配置
少量的简单配置可以完全依赖于application.properties或者application.yaml进行配置。如果比较复杂的配置可以考虑使用JavaConfig配置类来实现

@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/test11/"
);
}
}

addResourceHandler针对静态资源的路径,表示访问当前应用根目录下的所有静态资源
addResourceLocations设置对应的存放位置
例如访问/images/29.jpg则对应的存放位置为/test11/images/29.jpg
应用的logo图标
只需要将ico图标放置在静态文件夹根路径下即可,不是images目录下,默认图标的命名favicon.ico
可以在阿里巴巴矢量图标库寻找合适的图标地址https://www.iconfont.cn/

使用js文件

自定义的js可以直接存储在静态文件夹的默认路径下即可使用相对路径引用

<script src="jslib/test1.js"></script>

WebJars是将web前端资源打成jar包文件。借助版本管理工具(Maven、gradle等)进行版本管理,保证这些Web资源版本唯一性。
避免了文件混乱、版本不一致等问题。实际上一般的方法是从官方下载对应的js文件,拷贝到statis/jslib/目录下即可,不是由maven管理
在pom.xml添加依赖

<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>

在页面中引入对应的js文件
页面中使用webjars

<script src="/webjars/jquery/3.5.1/jquery.min.js"></script>
<script>
$(function(){
alert('DOM加载完毕!');
});
</script>

为了隐藏版本号可以使用依赖webjars-locator-core
对应的pom.xml

<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
<version>0.52</version>
</dependency>

webjars对前端依赖进行统一管理
1、静态资源版本化:传统的静态资源需要自行维护,资源种类繁多,使得项目后期越来越臃肿,维护版
本升级也变得困难,
而使用webjars方式进行管理后,版本升级问题迎刃而解
2、提升编译速度:使用webjars的方式管理依赖可以给项目的编译速度带来2-5倍的速度提升专门写的js文件或者下载好的静态资源也可以使用webjar的方式发布到公司私服仓库
新建一个springboot项目,创建目录META-INF/resources/ ,将静态资源完整复制进去,然后发布公司maven私服即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值