美文网首页
SpringBoot开发单体应用(三)

SpringBoot开发单体应用(三)

作者: 勿念及时雨 | 来源:发表于2020-04-01 00:53 被阅读0次

    单体开发进阶

    SpringBoot的Web开发之路

    众所周知,CRUD是每个程序员的必经之路。作为一个初级程序员,只要能够独立开发出一个简单的CRUD系统即可,例如OA系统、CRM管理系统、基于表单的CRUD系统等。
    简单来说,分布式开发是后端提供接口,前端接收信息进行渲染;单体开发是后端提供数据,前端获取数据进行渲染。
    从开发流程来说,单体开发和分布式开发本质上并没有区别,只是用到的技术和思想上略有不同。
    分布式开发的本质:网络是不可靠的,我们需要解决服务之间的通信问题,例如服务崩溃了的解决方案,客户端如何去进行访问,服务的注册和发现等。

    Swagger

    什么是Swagger?

    在前后端分离的项目当中,开发人员的分工大概如下:

    • 前端主要专注于控制层和视图层,由专业的前端团队进行开发。
    • 后端主要专注于后端控制层、服务层和数据访问层,由专业的后端团队进行开发。

    那么问题来了,前后端的交互一般都是通过API来进行的,关于API的约定应该如何处理呢?
    在早期的时候,一般由后端编写协同文档,前端根据文档解析接口然后渲染视图。但问题也很明显,前端和后端之间无法做到及时协商,最终可能导致问题集中爆发或者项目延时。
    为了解决这些问题,Swagger应运而生。
    什么是Swagger?
    Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful风格的Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。
    Swagger的作用主要有两点:接口的文档在线自动生成和功能测试。
    现在的开源项目中,都会集成Swagger。
    Swagger的主要特点

    • 号称世界上最流行的API框架。
    • Restful API自动生成文档,和代码对应。
    • 直接运行测试接口,不需要下载postman了。
    • 支持多种语言,如Java、Php等。

    Swagger官网地址::https://swagger.io/

    Swagger官网

    集成Swagger

    基础集成

    1、导入Swagger2依赖。

    <!--swaggger2依赖-->
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
    

    2、编写Swagger配置类,注册Docket到Spring容器中,在主启动类上添加@EnableSwagger2开启配置使Swagger生效。

    //Swagger配置类
    @Configuration 
    public class SwaggerConfig { 
    
        // 注册bean Docket 
        @Bean 
        public Docket docket(){ 
            return new Docket(DocumentationType.SWAGGER_2); 
        } 
    }
    
    //主启动类
    @SpringBootApplication
    @EnableSwagger2 //使swagger生效,默认不开启
    public class SpringbootPlusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootPlusApplication.class, args);
        }
    
    }
    

    3、启动项目进行测试,Swagger API文档默认访问地址:http://localhost:8080/swagger-ui.html

    Swagger API 文档页面
    注意在Controller的方法请求路径映射不要使用@RequestMapping来配置,应该直接使用具体请求方法的注解,如@GetMapping@PostMapping等。因为使用@RequestMapping配置在API文档上一个方法默认会生成七种请求的API文档信息,如下图所示。
    一个方法的七种请求的API文档信息

    配置Swagger

    1、在SwaggerConfig类中配置文档信息,构造ApiInfo对象。

    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())//配置文档信息
    }
        
    //配置文档信息apiInfo
    private ApiInfo apiInfo(){
    
        Contact contact = new Contact("wunian", "https://www.jianshu.com/u/5e27029e243f", "1796608443@qq.com");
    
        return new ApiInfo(
                "SpringBoot-Plus接口文档信息",//文档标题
                "所有的测试请求地址",//文档描述
                "v1.0",//文档版本
                "https://www.jianshu.com/u/5e27029e243f",//组织链接
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList<>());
    }
    

    2、配置哪些接口需要被扫描到文档中。

    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wunian.controller"))//设置扫描指定包下的类
                .build();
    }
    

    RequestHandlerSelectors类的所有静态方法说明:

    • any():扫描所有,项目的所有接口都会被扫描。
    • none():不扫描接口。
    • basePackage():根据包路径扫描。
    • withMethodAnnotation(GetMapping.class):通过方法注解扫描。
    • withClassAnnotation(Controller.class):通过类上的注解扫描。
    RequestHandlerSelectors类的所有静态方法

    3、设置哪些接口不被扫描。

    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wunian.controller"))//设置扫描指定包下的类
                //配置path过滤请求,只扫描以/kuang开头的请求
                .paths(PathSelectors.ant("/kuang/**")) 
                .build();
    }
    

    PathSelectors类的所有静态方法说明:

    • ant(String antPattern):只扫描指定的路径下的请求。
    • any():任何请求都会被扫描。
    • none():不扫描请求。
    • regex(String pathRegex):通过正则表达式来匹配请求。
    PathSelectors类的所有静态方法.png

    配置Swagger开关

    如果我们要让swagger-ui页面只在test和dev环境下显示,prod环境不显示就需要配置enable(false)方法。

    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(false)//如果是false就无法在浏览器中访问swagger-ui.html
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wunian.controller"))//
                .paths(PathSelectors.ant("/kuang/**")) 
                .build();
    }
    
    swagger-ui.html页面无法访问

    不过直接在enable方法中传入false也不合适,它应该是一个变量,我们可以通过Profiles类来获取限定的开发环境,并且调用Environment对象的acceptsProfiles方法来判断当前环境是否是限定的开发环境。

    @Bean
    public Docket docket(Environment environment){
        //设置要显示的swagger环境
        Profiles of = Profiles.of("dev", "test");
        //判断是否处于该环境
        boolean isEnable=environment.acceptsProfiles(of);
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(isEnable) //如果是false,就无法在浏览器中访问
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wunian.controller"))
                .paths(PathSelectors.ant("/kuang/**")) 
                
                .build();
    }
    

    配置API分组

    这个配置只需要了解即可,以后可以使用MyBatis-Plus一键生成。

    @Bean
    public Docket docket1(){
        return new Docket(DocumentationType.SWAGGER_2).groupName("group1");
    }
    
    @Bean
    public Docket docket2(){
        return new Docket(DocumentationType.SWAGGER_2).groupName("group2");
    }
    
    @Bean
    public Docket docket3(){
        return new Docket(DocumentationType.SWAGGER_2).groupName("group3");
    }
    
    API分组

    实体配置

    1、新建一个实体类,类上使用@ApiModel注解来添加该实体类的描述信息,此注解其实就是注释,只不过它会被Swagger识别。

    //和注释差不多,但是会被swagger识别
    @ApiModel("用户实体")  //实体类的描述信息
    public class User {//只有在controller中返回值用到,这个类才会显示在swagger中
    
        @ApiModelProperty("用户名")
        private String username;
        @ApiModelProperty("密码")
        private String password;
    }
    

    2、请求的接口配置,如果要在swagger-ui.html中看到实体类的配置,那么这个实体类一定是在请求的返回值或者泛型中,只有这样它才会被映射。

    //只有返回值用到才会显示
    @GetMapping("/getUser")
    public User getUser(){
        return new User();
    }
    
    实体类的配置

    接口上的配置

    在类上使用@Api注解来添加类的描述信息,在方法上使用@ApiOperation注解来添加方法的描述信息,在方法的形参上使用@ApiParam注解来添加参数的描述信息。

    @Api(tags="Hello测试类")   //类的描述信息
    @RestController
    public class HelloController {
    
        @GetMapping("/kuang/hello")
        public String hello(){
            return "Hello Swagger";
        }
    
        //只有返回值用到才会显示
        @GetMapping("/getUser")
        public User getUser(){
            return new User();
        }
       // 后面我们自己开发项目的时候,主要是写方法注释和参数注释!
        @ApiOperation("coding的接口") //方法和接口的描述信息
        @PutMapping("/coding")
        public String coding(@ApiParam("用户名") String username){//参数的描述信息
            return username;
        }
    }
    

    Swagger接口文档的皮肤包(扩展)

    1、默认的皮肤包: http://localhost:8080/swagger-ui.html

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
    
    默认皮肤包

    2、bootstrap-ui: http://localhost:8080/doc.html

    <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui --> 
    <dependency> 
        <groupId>com.github.xiaoymin</groupId> 
        <artifactId>swagger-bootstrap-ui</artifactId> 
        <version>1.9.6</version> 
    </dependency>
    
    bootstrap-ui
    3、ui-layer:http://localhost:8080/docs.html
    <!-- https://mvnrepository.com/artifact/com.github.caspar-chen/swagger-ui-layer - ->
    <dependency> 
        <groupId>com.github.caspar-chen</groupId> 
        <artifactId>swagger-ui-layer</artifactId> 
        <version>1.1.3</version> 
    </dependency>
    
    ui-layer

    4、mg-ui: http://localhost:8080/document.html

    <dependency> 
        <groupId>com.zyplayer</groupId> 
        <artifactId>swagger-mg-ui</artifactId> 
        <version>1.0.6</version> 
    </dependency>
    
    mg-ui

    异步任务

    我们可以来模拟一个延时的异步处理请求。
    1、在业务方法中添加@Async注解,此注解相当于告诉Spring这是一个异步任务,默认使用线程池开启异步任务,效率很高。

    @Service
    public class AsyncService {
    
        //模拟一个延时的后台方法
        @Async //告诉spring这是一个异步方法,默认使用了线程池
        public void hello(){
            try{
                Thread.sleep(3000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("数据处理中......");
        }
    }
    

    2、在主启动类上添加@EnableAsync开启异步任务支持。

    @SpringBootApplication
    @EnableSwagger2 //使swagger生效,默认不开启
    @EnableAsync //开启异步任务的支持
    public class SpringbootPlusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootPlusApplication.class, args);
        }
    }
    

    3、启动项目进行测试,页面如果出现秒级刷新,说明测试成功。

    定时任务

    Cron表达式

    计划任务,是指任务在约定的时间执行已经计划好的工作,这只是表面的意思。在Linux中,我们经常用到Cron服务器来完成这项工作。Cron服务器可以根据配置文件约定的时间来执行特定的任务。


    Cron基本格式

    实际上在分前面还有秒,范围在0-59之间。
    一些特殊符号的含义:

    • *表示任意时间。
    • -表示区间。
    • L表示最后。
    • ?用在日或星期上,表示不确定值、不限制值。
    • W表示工作日,这里的工作日指的是朝九晚五,双休和节假日。
    • #用于星期上,#后面的数字表示第几周,如果不存在这个周的值,则不执行。

    这些特殊符号其实不用刻意去记忆,我们想要编写Cron表达式只需要去找在线生成Cron表达式的网站去生成就好了。
    常用的Cron表达式:

    (1)0/2 * * * * ? 表示每2秒 执行任务 
    (1)0 0/2 * * * ? 表示每2分钟 执行任务 
    (1)0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务 
    (2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业 
    (3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作 
    (4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 
    (5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 
    (6)0 0 12 ? * WED 表示每个星期三中午12点 
    (7)0 0 12 * * ? 每天中午12点触发 
    (8)0 15 10 ? * * 每天上午10:15触发 
    (9)0 15 10 * * ? 每天上午10:15触发 
    (10)0 15 10 * * ? 每天上午10:15触发 
    (11)0 15 10 * * ? 2005 2005年的每天上午10:15触发 
    (12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发 
    (13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发 
    (14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 
    (15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发 
    (16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发 
    (17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发 
    (18)0 15 10 15 * ? 每月15日上午10:15触发 
    (19)0 15 10 L * ? 每月最后一日的上午10:15触发 
    (20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发 
    (21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发 
    (22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
    

    编写定时任务

    1、编写Service,在定时方法上添加@Scheduled注解,该注解传入一个cron参数,此参数传入一个Cron表达式。

    @Service //放到spring容器中
    public class ScheduledService {
    
        //工作中的定时任务都可以使用这样的方法
        //秒 分 时 日 月 周几
        @Scheduled(cron = "0 * * * * 0-7")  //每天每时每分整点执行
        public void hello(){
            System.out.println("Hello  Scheduled");
        }
    }
    

    2、在主启动类上添加@EnableScheduling注解开启定时任务支持。

    @SpringBootApplication
    @EnableSwagger2 //使swagger生效,默认不开启
    @EnableAsync //开启异步任务的支持
    @EnableScheduling//开启定时任务支持
    public class SpringbootPlusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootPlusApplication.class, args);
        }
    }
    

    3、启动项目进行测试,可以看到每一分钟的整点都会输出"Hello Scheduled"。

    邮件任务

    所有的网站,几乎都有邮件收发功能。我们这里以QQ邮箱为例,基于SpringBoot来实现发送邮件功能。

    邮箱的开发者权限获取

    相比于其他邮箱,QQ邮箱较为复杂,因为它有安全验证。我们需要登录QQ邮箱去开启一下SMTP服务,具体操作步骤如下图所示。


    QQ邮箱开启SMTP服务

    然后我们就可以获得一个授权码了,它相当于我们的邮箱账号的密码,通过它可以登录我们的邮箱。


    获得授权码
    对于不同的邮箱系统,网站服务器地址是不一样的:
    • QQ邮箱:smtp.qq.com
    • 网易邮箱:smtp.163.com
    • 新浪邮箱:smtp.sina.com
    SMTP服务器地址

    测试

    1、导入启动器,java中发送邮件的包是javax.mail,但是SpringBoot启动器中导入的包是jakarta.mail。

    <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-mail</artifactId> 
    </dependency>
    

    2、配置发件人信息,详细的配置项可以查看MailProperties配置类。

    #发件人的信息
    spring.mail.username=1234508334@qq.com
    spring.mail.password=mxhdobcadmkudacd
    spring.mail.host=smtp.qq.com
    #QQ比较特殊,需要配置ssl安全连接,其他邮箱不需要配置
    spring.mail.properties.mail.smtp.ssl.enable=true
    

    4、测试发送简单邮件和复杂邮件。

    @SpringBootTest
    class SpringbootPlusApplicationTests {
    
        @Autowired
        JavaMailSender sender;
    
        @Test
        void sendMailTest(){//测试简单的邮件发送
            //发送简单的邮件
            SimpleMailMessage message = new SimpleMailMessage();//简单的邮件消息
            message.setSubject("你收到了一份面试邀请");//邮件主题
            message.setText("才怪,测试邮件哦!");
    
            message.setFrom("1234508334@qq.com");//发件人
            message.setTo("1234508334@qq.com","abcd@163.com");//收件人
            sender.send(message); //发送邮件
        }
    
        @Test
        void sendMimeMailTest() throws MessagingException {//测试复杂的邮件发送
            //复杂的邮件,通过一个辅助类来完成 MimeMessageHelper
            MimeMessage mimeMessage = sender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            //基本信息
            helper.setSubject("支付宝到账提醒:尊敬的客户,您3月份...");
            helper.setText("<h1 style='color:red'>支付宝到账1,0000,000元,请注意查收!</h1>",true);
            //发送附件
            helper.addAttachment("1.jpg",new File("C:\\Users\\Administrator\\Desktop\\1.jpg"));
    
            helper.setFrom("1234508334@qq.com");
            helper.setTo(new String[]{"1234508334@qq.com","abcd@163.com"});
            sender.send(mimeMessage);  //发送邮件
        }
    }
    

    相关文章

      网友评论

          本文标题:SpringBoot开发单体应用(三)

          本文链接:https://www.haomeiwen.com/subject/cztwuhtx.html