springboot agile development notes 2

4 添加接口扫描

@SpringBootApplication
@MapperScan("com.aaa.haha.mapper")
public class App {
    
    

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

3.2 easycode

IDEA的插件

1 idea下载插件
2 使用idea连接mysql数据库
3 使用idea打开数据库 选中数据库表 右键 选择 easycode  生成代码

其他的使用方式 例如:定制模板
    
基本上已经过时了  现在都是低代码平台开发 

Four boot integration with others

4.1 Integrated Timers

定时任务 quartz : 
    有的时候 我们的业务流程 需要在某个时间之后 或者 周期性调用某些功能  此时就需要使用到 定时任务

我们现在搞一波 springboot自带的任务调度
使用方式:
1 开启定时任务
@SpringBootApplication
@EnableScheduling
public class App 
{
    
    
    public static void main( String[] args ){
    
    
        SpringApplication.run(App.class , args);
    }
}
2 创建一个配置类 配置定时任务干什么
@Component
public class MySchudule {
    
    

    @Scheduled(fixedDelay = 10000)
    public void haha(){
    
    
        System.out.println("你好 该吃饭了");
    }

} 

此时就有一个cron表达式 可以让我们任意设定时间
@Component
public class MySchudule {
    
    

    @Scheduled(cron = "25 * * * * ? ")
    public void haha(){
    
    
        System.out.println("你好 该吃饭了");
    }

}    

4.2 Asynchronous tasks

异步编程首先在启动类添加注解:@EnableAsync
在service的函数上面添加异步注解    
@Service
public class StudentServiceImpl implements StudentService {
    
    


    @Override
    @Async
    public void sendCode() {
    
    

        System.out.println("开始发送");
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发送成功");
    }
}    
此时在controller中调用异步的业务函数

@RestController
@RequestMapping("stu")
public class StudentController {
    
    

    @Autowired
    private StudentService  studentService;


    @RequestMapping("add")
    public String  add(){
    
    

        studentService.sendCode();
        return "hello  boot";
    }

}

insert image description here

4.3 Integration of transactions

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

事务 和  克  天 小时  克拉  一样 都是单位名词 
从开始到结束之间的所有操作 称之为一个事务单元   遵循ACID四大特性 

SSM事务 使用AOP切面  在boot中使用注解  事务添加到业务层

springboot中事务操作非常简单 只需要两步走:
     <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
1 在启动类开启事务操作
@SpringBootApplication
@EnableScheduling
@EnableAsync
@EnableTransactionManagement
public class App
{
    
    
    public static void main( String[] args ){
    
    
        SpringApplication.run(App.class , args);
    }
}
2 在需要事务操作的业务层上面添加事务注解
@Service
public class StudentServiceImpl implements StudentService {
    
    
    
    @Override
    @Async
    @Transactional
    public void sendCode() {
    
    
        System.out.println("开始发送");
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发送成功");
    }
    
}
@Transactional这注解如果只添加 所有的操作都走默认 可以自己指定
    
 @Service
public class StudentServiceImpl implements StudentService {
    
    

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void sendCode() {
    
    

        System.out.println("开始发送");
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发送成功");
    }
}

面试题: 事务失效的场景有哪些?

Transaction rollback rollbackFor connection database play a role

4.4 Integrating AOP


AOP 面向切面编程。防止代码侵入  解决重复代码问题 
    
AOP面向切面编程,项目中AOP使用场景: 1 访问日志 2 权限 3 操作日志
以前我们整合的时候 使用的是 xml配置 其实也可以使用注解配置
在springboot中没有xml 所以就用直接配置
    
1 我们需要在pom中导入 aop
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>


2 创建一个切面类 添加注解
@Component
@Aspect
public class MyAop {
    
    

      @Pointcut("execution(*  com.aaa.boot.controller.*.*(..))")
      public void hehe(){
    
    }

      @Before("hehe()")
      public void foo(){
    
    
          System.out.println("之前的aop");
      }

      @After("hehe()")
      public void bar(){
    
    
          System.out.println("之后的aop");
      }

}

4.5 Front-background separation and cross-domain

以前的开发模式: SSM+JSP  /  SSM+模板引擎 
   使用框架完成业务 JSP负责试图的展示 并且 框架和JSP之前的关系是请求转发/重定向 
   当前没有前端工程(或者说有前端但是比重不大 1  门户网站 2 网页  交给后台工程师 。后台工程师 将静态网页 复制到JSP中  需要改成动态的内容后台负责改 )

后来啊 为了用户体验提升   ajax技术(异步无刷新  用户体验更好) 
      [ajax请求到数据时候  使用 传统的DOM操作完成数据渲染 ]
      VUE VUECLI [MVVM]
    
所以 现在基本上玩的都是前后台分离  后端写好的称之为  接口
                             前端ajax调用接口  获取数据 渲染数据 构建试图      -- 前后台联调 

AJAX跨域问题:  
   域:  IP地址+端口号+项目名  
   跨域:  IP地址+端口号+项目名   这三个其中一个不一样 就称之为 跨域
    
我们模拟一下跨域 :
    
前端项目: http://127.0.0.1:8848/09.30/index.html
发送ajax请求 
     <script>
			
			$.get("http://127.0.0.1:8090/boot/student/list",function(backData){
      
      
				
				
			});
			
		</script>

此时请求的是 后台服务器 http://127.0.0.1:8090/boot/student/list
通过浏览器的 网络查看 请求200 成功 也有回传数据  证明 ajax发送成功 并且响应成功
但是控制台输出 :
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8090/boot/student/list 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。状态码:200

说明 浏览器阻断了 ajax的数据获取 因为浏览器有一个 同源策略 

所以我们需要解决跨域问题:
1 jsonp   2 cors    3 Nginx配置统一网关

1 jsonp 
  AJAX请求的数据  被浏览器阻断了  前端除了 ajax 还能有别的技术发送请求吗?
  
   <a href=""></a>                     x
   <form action=""></form>             x
   <img  src="" />                     x
   <link href="" />                    x
   <script  src="" ></script>

<script>
			function  hehe(a){
      
      
				alert(a);
			}	
</script>
		
<script src="http://127.0.0.1:8090/boot/haha/hehe?callback=hehe"></script>
@RestController
@RequestMapping("haha")
public class HahaController {


    @RequestMapping("hehe")
    public String   test(String  callback){
        return  callback+" (666)";
    }

}
cors

第一种方式
   只需要在springboot中添加一个注解即可
@RestController
@RequestMapping("stu")
@CrossOrigin("http://127.0.0.1:8848")
public class StudentController {
    
    }


第二种方式 拦截器统一放行
package com.aaa.boot.configration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CrosOriginConfig implements WebMvcConfigurer {
    
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    
    

        //  代表当前类中所有的请求 都允许跨域访问
        registry.addMapping("/**")
                 // 配置哪个域 允许跨域访问
                .allowedOrigins("http://127.0.0.1:8848")
                // 允许所有的请求方式
                .allowedMethods("*")
                // 允许任意请求头
                .allowedHeaders("*");
    }
}  

此时其实浏览器发送两次请求 一次是option请求 用来询问服务器是否要管闲事 一次是正常的ajax请求


4.6 POSTMAN

Official website: Apipost
https://www.apipost.cn/?utm_source=10124&bd_vid=11408968972694612491

前后台分离的时候 后端写好的接口 需要自己测试 所以就需要测试工具:  POSTMAN

4.7 Restful style

RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。 

RESTful CRUD

@RequestMapping: By setting the CRUD of the method attribute, the same URL can be mapped to different HandlerMethod methods.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping annotations are the same as the method attribute settings of @RequestMapping annotations.
Reference: What is RESTful style?
https://www.cnblogs.com/aaaazzzz/p/13357601.html

4.8 Interface Documentation

接口文档是后端人员 写好了接口之后  。 
将自己写的接口整理成一个文档,交给前端人员 ,前端根据接口文档发送请求 获取数据 进行调试。

insert image description here

insert image description here

4.9 Knife4j

因为我们的接口文档 可以随时变更 所以我们需要实时更新  但是每次都更新太麻烦了  
所以就有一个技术 实现动态的接口文档 swagger  现在有一个更新 [Knife4j]

            
1 在maven项目的pom.xml中引入Knife4j的依赖包,代码如下:
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.7</version>
</dependency>
2 创建Swagger配置依赖,代码如下:

@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
    
    

    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
    
    
        Docket docket=new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        //.title("swagger-bootstrap-ui-demo RESTful APIs")
                        .description("# swagger-bootstrap-ui-demo RESTful APIs")
                        .termsOfServiceUrl("http://www.xx.com/")
                        .contact("[email protected]")
                        .version("1.0")
                        .build())
                //分组名称
                .groupName("2.X版本")
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.aaa.boot"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
}

3 在我们的controller上添加注解 标明接口信息
@RestController
@RequestMapping("stu")
@Api(tags = "学生管理模块")
public class StudentController {
    
    


    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation(value = "获取所有学生")
    @RequestMapping("list")
    public Map  getStu(){
    
    

        Map data = new HashMap();

        data.put("name","123");

        return  data;
    }


}
4 此时运行我们的项目
访问 http://localhost:8090/boot/doc.html

4.10 Input parameter verification

我们为了保证数据的准确性 前端都会添加 表单验证
但是表单验证 防君子不防小人 
    
 所以无论前端是否添加了表单验证 后台在接受到参数之后 都需要对参数进行合法性校验

第一种方式在业务层进行 判断  
    
第二种入参框架
1 导入jar包
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.3.4.Final</version>
    </dependency>

2 添加注解
@Data
public class Student implements Serializable {
    
    
    private static final long serialVersionUID = -61691708382537408L;

    private Integer id;

    @NotBlank(message = "对不起 名字必须传递")
    private String name;

    @Min(0)
    @Max(value = 150,message = "最大年龄不能超过150")
    private Integer age;

    @Length(min = 2,max = 4,message = "地址长度必须为2-4")
    @Pattern(regexp = "^1[0-9][a-z][A-Z]$")
    private String address;

    private Integer gid;

}
 @GetMapping("add")
    public AjaxResult  add( @Validated Student  student   ){
    
    
        System.out.println(student);
        return  AjaxResult.success();
    }




4.11 Consolidating global exceptions

我们后台 无论出现什么问题  返回的数据都应该是统一的 
所以我们需要配置一个全局异常处理器 用来捕获整个项目中 所有的异常 

package com.aaa.boot.exception;


import com.aaa.boot.result.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    


    /**
     * 处理所有不可知异常
     *
     * @param e 异常
     * @return json结果
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e) {
    
    
        // 打印异常堆栈信息
        log.error(e.getMessage(), e);
        return AjaxResult.defeated(e.getMessage());
    }

    /**
     * 处理所有业务异常
     *
     * @return json结果
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public AjaxResult validationBodyException(MethodArgumentNotValidException exception) {
    
    
        AjaxResult AjaxResult = printMessage(exception);
        return AjaxResult;
    }

    @ExceptionHandler(BindException.class)
    public AjaxResult validationBodyException(BindException exception) {
    
    
        AjaxResult AjaxResult = printMessage(exception);
        return AjaxResult;
    }


    private   AjaxResult   printMessage(BindException  e){
    
    

        BindingResult result =    e.getBindingResult();
        String message = "";
        if (result.hasErrors()) {
    
    
            List<ObjectError> errors = result.getAllErrors();
            if (errors != null) {
    
    

                errors.forEach(p -> {
    
    
                    FieldError fieldError = (FieldError) p;
                    log.error("Data check failure : object{" + fieldError.getObjectName() + "},field{" + fieldError.getField() +
                            "},errorMessage{" + fieldError.getDefaultMessage() + "}");

                });

                if (errors.size() > 0) {
    
    
                    FieldError fieldError = (FieldError) errors.get(0);
                    message = fieldError.getDefaultMessage();
                }
            }
        }

        String s = "".equals(message) ? "请填写正确信息" : message;

        return AjaxResult.defeated(s);
    }

}




4.12 Integrate logback logs

boot推荐的日志是 logback 


我们使用的是springboot ,最适合的 logback 日志    
1 创建一个logbak.xml 文件 导入内容
    
<?xml version="1.0" encoding="UTF-8"?>
<!-- logging.config=classpath:logback.xml
logback.path=. -->
<!-- 日志级别从低到高分为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 resource="./config/application.properties" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logback.path}/log/loginfo.log</file>
        <append>true</append>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>${logback.path}/log/loginfo.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>



    <logger name="com.baomidou.mybatisplus.samples.pagination" level="info"/>
    <logger name="com.**.monitor" level="debug"/>
    <logger name="org.mybatis" level="debug"/>
    <logger name="org.apache.ibatis" level="info"/>
    <logger name="java.sql.Connection" level="debug"/>
    <logger name="java.sql.Statement" level="debug"/>
    <logger name="java.sql.PreparedStatement" level="debug"/>
    <logger name="com.alibaba.druid" level="ERROR"/>

    <!-- 日志输出级别 -->
    <root level="info">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

在配置文件中配置
 在applicaiton.propertis 中添加配置信息

logging.config=classpath:config/logback.xml
logging.level.com.aaa.haha=debug
logback.path=.

4.13 Integrating Interceptors

我们在servlet中学习过 过滤器 在boot中依然可以使用 Filter 但是我们一般不使用。因为其还是servlet的东西
我们现在使用的是框架,整合请求流程都是交给框架去处理,所以框架推出了一个 在请求到达 controller之前 可以让我们去处理某些事情
    
这个操作就是 boot中的拦截器

@Configuration
public class MyIntercepterConfig implements WebMvcConfigurer {
    
    

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    

        registry.addInterceptor(
                new HandlerInterceptor() {
    
    
                    @Override
                    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
                        System.out.println("你好 世界  拦截了");
                        return true;
                    }
                }
        ).order(1).addPathPatterns("/stu/add").excludePathPatterns("/stu/delete");

    }
}

4.14 Integrating Template Engines (Understanding)

模板引擎,这是以前用来替代jsp的一种技术。jsp技术有一个问题 需要先转换成java代码再转换成字节码才能运行 所以性能不好。
当年就推出了一套 模板引擎 技术代替jsp。模板引擎 其实就是在html中 使用类似于 c:forEach 【jstl 只能在jsp中使用 但是在html中无法使用 】

模板引擎 有很多:FreeMarker  thymeleaf

thymeleaf 严格的html格式 如果不严谨 就报错  例如 <br> 报错  <br /> 
    
1 导入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
    
2 在application.properties 配置模板信息
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.cache=false    
    
3 controller中跳转
 @Controller
public class UserController {

    @RequestMapping("/heihei")
    public  String  listUser(Model m){

        m.addAttribute("name","张三");
        return "/user.html";
    }

}
 4 在resources中创建一个 templates 文件夹  里面添加一个 user.html的网页   
<!DOCTYPE html>
<html lang="zh-cn"  xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>Title</title>
</head>
<body>
<h1 th:text="${name}" ></h1>
</body>
</html>   



Guess you like

Origin blog.csdn.net/Liu_wen_wen/article/details/127232220