百日筑基第十一天-看看SpringBoot

百日筑基第十一天-看看SpringBoot

创建项目

Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目。网址如下:

https://start.spring.io/

打开后的界面如下:

可以将 Spring Initializr 看作是 Spring Boot 项目的初始化向导,它可以帮助开发人员在一分钟之内创建一个 Spring Boot 骨架,非常的傻瓜式。

来解释一下 Spring Initializr 初始化界面中的关键选项。

1)Project:项目的构建方式,可以选择 Mavenopen in new window(安装方式可以戳链接) 和 Gradle(构建脚本基于 Groovy 或者 Kotlin 等语言来编写,而不是传统的 XML)。编程喵默认采用的 Maven。

2)Language:项目的开发语言,可以选择 Java、Kotlin(JetBrains开发的可以在 JVM 上运行的编程语言)、Groovy(可以作为 Java 平台的脚本语言来使用)。默认 Java 即可。

3)Spring Boot:项目使用的 Spring Boot 版本。默认版本即可,比较稳定。

4)Project Metada:项目的基础设置,包括包名、打包方式、JDK 版本等。

  • Group:项目所属组织的标识符,比如说 top.codingmore;
  • Artifact:项目的标识符,比如说 coding-more;
  • Name:默认保持和 Artifact 一致即可;
  • Description: 项目的描述信息,比如说《编程喵实战项目(Spring Boot+Vue 前后端分离项目)》;
  • Package name:项目包名,根据Group和Artifact自动生成即可。
  • Packaging: 项目打包方式,可以选择 Jar 和 War(SSM 时代,JavaWeb 项目通常会打成 War 包,放在 Tomcat 下),Spring Boot 时代默认 Jar 包即可,因为 Spring Boot 可以内置 Tomcat、Jetty、Undertow 等服务容器了。
  • Java:项目选用的 JDK 版本。

5)Dependencies:项目所需要的依赖和 starter。如果不选择的话,默认只有核心模块 spring-boot-starter 和测试模块 spring-boot-starter-test。

生成zip文件解压:

重要的包

spring-boot-devtools 热部署

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

整合Lombok简化代码

xml 复制代码
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.6</version>
	<scope>provided</scope>
</dependency>

然后就可以这样用了:

java 复制代码
@Getter
@Setter
class CmowerLombok {
	private int age;
	private String name;
	private BigDecimal money;
}

其中 scope=provided,就说明 Lombok 只在编译阶段生效。也就是说,Lombok 会在编译期静悄悄地将带 Lombok 注解的源码文件正确编译为完整的 class 文件。

SpringBoot 2.1.x 版本后不需要再显式地添加 Lombok 依赖了。之后,还需要为 Intellij IDEA 安装 Lombok 插件,否则 Javabeangetter / setter 就无法自动编译,也就不能被调用。不过,新版的 Intellij IDEA 也已经内置好了,不需要再安装。

@Getter / @Setter 用起来很灵活,比如说像下面这样:

java 复制代码
class CmowerLombok {
	@Getter @Setter private int age;
	@Getter private String name;
	@Setter private BigDecimal money;
}

@ToString 注解可以生成 toString 方法:

java 复制代码
@ToString
class CmowerLombok {
	private int age;
	private String name;
	private BigDecimal money;
}

@Data 注解可以生成 getter / setterequalshashCode,以及 toString,是个总和的选项。

java 复制代码
@Data
class CmowerLombok {
	private int age;
	private String name;
	private BigDecimal money;
}

@Slf4j 可以用来生成注解对象,你可以根据自己的日志实现方式来选用不同的注解,比如说:@Log@Log4j@Log4j2@Slf4j 等。

java 复制代码
@Slf4j
public class Log4jDemo {
    public static void main(String[] args) {
        log.info("level:{}","info");
        log.warn("level:{}","warn");
        log.error("level:{}", "error");
    }
}

@Builder 注解可以用来通过建造者模式来创建对象,这样就可以通过链式调用的方式进行对象赋值,非常的方便。

java 复制代码
@Builder
@ToString
public class BuilderDemo {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        BuilderDemo demo = BuilderDemo.builder().age(18).name("沉默王二").build();
        System.out.println(demo);
    }
}

Lombok 还提供了同步注解 @Synchronized、自动抛出异常注解 @SneakyThrows、不可变对象 @Value、自动生成 hashCode 和 equals 方法的注解 @EqualsAndHashCode等等,

Lombok 用起来虽然爽,但需要团队内部达成一致,就是要用大家都用,否则有些用了有些没用就会乱成一锅粥,很影响代码的整体风格。

整合 MySQL 和 Druid

Mysql

对应pom.xml文件中的代码:

xml 复制代码
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

在 application.yml 文件中添加数据库链接驱动信息:

xml 复制代码
spring:
  datasource:
    username: codingmore-mysql
    password: YyfR4TDxCwrjZ2Fs
    url:jdbc: mysql://118.190.99.232:3306/codingmore-mysql?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
java 复制代码
@SpringBootTest
@Slf4j
class CodingmoreMysqlApplicationTests {
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() {
        String sql ="select * from user";
        List<User> users = jdbcTemplate.query(sql, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                User user = new User();
                user.setId(rs.getInt(1));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setPassword(rs.getString("password"));
                return user;
            }
        });
        log.info("查询成功");
        log.info("用户{}",users);
    }
}

Spring Boot 的测试类主要放置在 src/test/java 目录下面,项目创建成功后,Spring Boot 会根据项目名称自动为我们生成测试类。

比如说本次项目名为 codingmore-mysql,那么测试类名为 CodingmoreMysqlApplicationTests。

@SpringBootTest 注解能够测试我们的项目主类,该项目为 CodingmoreMysqlApplication。

@Test 注解是 junit 单元测试的注解,表示该方法为测试方法。

JdbcTemplate 一个通过 JDBC 连接数据库的工具类,spring-boot-starter-jdbc 依赖中包含了该类。

@Resource 注解会帮我们在 Spring Boot 启动的时候注入一个 JdbcTemplate 的对象。

jdbcTemplate.query() 方法通过 SQL 语句和匿名内部类参数的形式,执行 SQL 并查询结果集。

RowMapper 就是查询到的每一行数据对象,我们可以通过重写 mapRow 方法将数据结果集封装到 User 对象上。

Druid

Druid 是阿里巴巴开源的一款数据库连接池,结合了C3P0、DBCP 等 DB 池的优点,同时还加入了日志监控。

Druid 包含了三个重要的组成部分:

  • DruidDriver,能够提供基于 Filter-Chain 模式的插件体系;
  • DruidDataSource,高效可管理的数据库连接池;
  • SQLParser,支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL 等。

第一步,在 pom.xml 文件中添加 Druid 的依赖,官方已经提供了 starter,我们直接使用。

text 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.23</version>
</dependency>

第二步,在 application.yml 文件中添加 Druid 配置。

text 复制代码
spring:
  datasource:    
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      #初始化连接池大小
      initial-size: 5
      #配置最小连接数
      min-idle: 5
      #配置最大连接数
      max-active: 200
      #配置连接等待超时时间
      max-wait: 60000
      #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      #配置一个连接在池中最小生存的时间,单位是毫秒
      min-evictable-idle-time-millis: 300000
      #测试连接
      validation-query: SELECT 1 FROM DUAL
      #申请连接的时候检测,建议配置为true,不影响性能,并且保证安全
      test-while-idle: true
      #获取连接时执行检测,建议关闭,影响性能
      test-on-borrow: false
      #归还连接时执行检测,建议关闭,影响性能
      test-on-return: false
      #是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
      pool-prepared-statements: false
      #开启poolPreparedStatements后生效
      max-pool-prepared-statement-per-connection-size: 20
      #配置扩展插件,常用的插件有=>stat:监控统计  log4j:日志  wall:防御sql注入
      filters: stat,wall,slf4j
      #打开mergeSql功能;慢SQL记录
      connection-properties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      #配置DruidStatFilter
      web-stat-filter:
        enabled: true
        url-pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      #配置DruidStatViewServlet
      stat-view-servlet:
        url-pattern: "/druid/*"
        #登录名
        login-username: root
        #登录密码
        login-password: root

第三步,启动项目。在日志信息里可以看到 Druid 的初始化信息。

第四步,通过 http://localhost:9002/druid/ 地址就可以在浏览器访问 Druid 的监控页面了,用户名和密码是我们在配置文件里指定的 root 和 root,登录后是这样的。

Spring 对事务的支持

Spring 支持两种事务方式,分别是编程式事务和声明式事务,后者最常见,通常情况下只需要一个 @Transactional 就搞定了(代码侵入性降到了最低),就像这样:

java 复制代码
@Transactional
public void savePosts(PostsParam postsParam) {
  // 保存文章
  save(posts);
  // 处理标签
  insertOrUpdateTag(postsParam, posts);
}

1)编程式事务

编程式事务是指将事务管理代码嵌入嵌入到业务代码中,来控制事务的提交和回滚。

你比如说,使用 TransactionTemplate 来管理事务:

java 复制代码
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}

再比如说,使用 TransactionManager 来管理事务:

java 复制代码
@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

就编程式事务管理而言,Spring 更推荐使用 TransactionTemplate。

在编程式事务中,必须在每个业务操作中包含额外的事务管理代码,就导致代码看起来非常的臃肿,但对理解 Spring 的事务管理模型非常有帮助。

2)声明式事务

声明式事务将事务管理代码从业务方法中抽离了出来,以声明式的方式来实现事务管理,对于开发者来说,声明式事务显然比编程式事务更易用、更好用。

当然了,要想实现事务管理和业务代码的抽离,就必须得用到 Spring 当中最关键最核心的技术之一,AOP,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的

Spring Boot 对事务的支持

只需要在业务层添加事务注解(@Transactional)就可以快速开启事务。

也就是说,我们只需要把焦点放在 @Transactional 注解上就可以了。

@Transactional 的作用范围

  • 类上,表明类中所有 public 方法都启用事务
  • 方法上,最常用的一种
  • 接口上,不推荐使用

@Transactional 的常用配置参数

虽然 @Transactional 注解源码中定义了很多属性,但大多数时候,我都是采用默认配置,当然了,如果需要自定义的话,前面也都说明过了。

@Transactional 的使用注意事项总结

1)要在 public 方法上使用,在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。

2)避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效。

过滤器、拦截器、监听器

  • 过滤器(Filter):当有一堆请求,只希望符合预期的请求进来。
  • 拦截器(Interceptor):想要干涉预期的请求。
  • 监听器(Listener):想要监听这些请求具体做了什么。
过滤器
  • 过滤敏感词汇(防止sql注入)
  • 设置字符编码
  • URL级别的权限访问控制
  • 压缩响应信息

添加一个过滤器 MyFilter :

java 复制代码
@WebFilter(urlPatterns = "/*", filterName = "myFilter")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long start = System.currentTimeMillis();
        chain.doFilter(request,response);
        System.out.println("Execute cost="+(System.currentTimeMillis()-start));
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

@WebFilter 注解用于将一个类声明为过滤器,urlPatterns 属性用来指定过滤器的 URL 匹配模式,filterName 用来定义过滤器的名字。

MyFilter 过滤器的逻辑非常简单,重写了 Filter 的三个方法,在 doFilter 方法中加入了时间戳的记录。然后我们在项目入口类上加上 @ServletComponentScan 注解,这样过滤器就会自动注册。

拦截器
  • 登录验证,判断用户是否登录
  • 权限验证,判断用户是否有权限访问资源,如校验token
  • 日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量
  • 处理cookie、本地化、国际化、主题等
  • 性能监控,监控请求处理时长等
java 复制代码
@Slf4j
public class LoggerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("preHandle{}...",request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

一个拦截器必须实现 HandlerInterceptor 接口,preHandle 方法是 Controller 方法调用前执行,postHandle 是 Controller 方法正常返回后执行,afterCompletion 方法无论 Controller 方法是否抛异常都会执行。

只有 preHandle 返回 true 的话,其他两个方法才会执行。

如果 preHandle 返回 false 的话,表示不需要调用Controller方法继续处理了,通常在认证或者安全检查失败时直接返回错误响应。

再来一个 InterceptorConfig 对拦截器进行配置:

java 复制代码
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggerInterceptor()).addPathPatterns("/**");
    }
}

@Configuration 注解用于定义配置类,干掉了以往 Spring 繁琐的 xml 配置文件。

编写一个用于被拦截的控制器 MyInterceptorController:

java 复制代码
@RestController
@RequestMapping("/myinterceptor")
public class MyInterceptorController {
    @RequestMapping("/hello")
    public String hello() {
        return "沉默王二是傻X";
    }
}

@RestController 注解相当于 @Controller + @ResponseBody 注解,@ResponseBody 注解用于将 Controller 方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区,通常用来返回 JSON 或者 XML 数据,返回 JSON 数据的情况比较多。

相关推荐
LYFlied7 分钟前
在AI时代,前端开发者如何构建全栈开发视野与核心竞争力
前端·人工智能·后端·ai·全栈
用户479492835691516 分钟前
我只是给Typescript提个 typo PR,为什么还要签协议?
前端·后端·开源
Surpass余sheng军23 分钟前
AI 时代下的网关技术选型
人工智能·经验分享·分布式·后端·学习·架构
JosieBook26 分钟前
【Spring Boot】Spring Boot调用 WebService 接口的两种方式:动态调用 vs 静态调用 亲测有效
java·spring boot·后端
a程序小傲28 分钟前
京东Java面试被问:Spring拦截器和过滤器区别
java·面试·京东云·java八股文
喵个咪1 小时前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:基于 GORM 从零实现新服务
后端·go·orm
2401_871260022 小时前
Java学习笔记(二)面向对象
java·python·学习
是梦终空2 小时前
计算机毕业设计252—基于Java+Springboot+vue3+协同过滤推荐算法的农产品销售系统(源代码+数据库+2万字论文)
java·spring boot·vue·毕业设计·源代码·协同过滤算法·农产品销售系统
丿BAIKAL巛2 小时前
Java前后端传参与接收全解析
java·开发语言
计算机毕设VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue服装商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·课程设计