[Spring Boot 6]企业级开发

news2024/11/24 5:44:45

本节内容主要就几个常用的模块进行解读,在众多企业级开发中常常用得到
例如邮件发送这一功能其实非常常见,在一般的网站设计中会有对注册邮件的激活功能,此时就需要用到这个功能了,一般来说激活邮件需要设置UUID 来进行验证,如果用户点击了链接那么在数据库的状态就转换为1,表示已经激活了。
定时任务也用得比较多,常见于缓存进入数据库等,可以看我这篇关于高并发的章节 高并发Demo
这里讲的批处理主要是针对批量导入数据库的快捷方法(这样就不用进入excel一行一行导入了)
目前很多项目也是需要讲究前后端分离的,所以一个好的接口测试是非常有必要的,之前我们使用了Postman 来进行测试,不过依旧缺少点详细信息,这里使用Swagger2 来进行测试,不过值得一提的是,以前老的配置方法非常坑,我也是花了相当的时间才找到了一个合适的办法。

希望对你有所帮助!

目录

  • 邮件发送
    • 发送前的准备
    • 发送
  • 定时任务
    • @Scheduled
    • Quartz
  • 批处理
  • Swagger 2

邮件发送

邮件发送是非常常见的功能,注册时的身份验证、重要通知发送都会用到邮件发送。Sun公司提供了JavaMail,但是配置相当繁琐,Spring 提供了JavaMailSender来简化,Spring Boot则提供了MailSenderAutoConfiguration对邮件做了进一步简化。

发送前的准备

这里以QQ邮箱发送邮件为例:需要开通POP3/SMTP服务或者IMAP/SMTP服务。SMTP就是简单邮件传输协议,定义了邮件客户端和SMTP服务器之间、以及SMTP与SMTP之间的通信规则。也就是说aaa@qq.com 用户先将邮件投递给腾讯的SMTP,然后邮件投递到网易的SMTP服务器,此时SMTP服务器就是用来接收邮件的。POP3 邮局协议,定义了客户端与POP3 服务器之间的规则。

在这里插入图片描述

这样你就会得到一个授权码了。

发送

添加邮件依赖:

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

项目创建成功后,在application.properties 完成邮件基本信息配置:

spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username = 1739994163@qq.com
spring.mail.password=授权码
spring.mail.default-encoding=utf-8
spring.mail.properties.mail.smtp.socketFactory.class = javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.debug=true

这里配置了邮件服务器的地址、端口(可以是465或者587)、用户账号和密码以及默认编码、SSL连接配置,最后开启debug,这样方便开发者查看邮件发送日志。注意,SSL的配置可以在QQ邮箱官方文档查看

发送一个简单的邮件,创建一个MailService用来封装邮件的发送:

@Component
public class MailService {
    @Resource
// 在MailSenderPropertiesConfiguration类配置好的,该类在Mail 自动配置类导入,
// 只要注入就可以了
    JavaMailSender javaMailSender;
    public void sendSimpleMail(String from, String to, String cc, String subject, String content) {
// 5个参数,分别是邮件发送者,收件人,抄送人,邮件主题,邮件内容      
	SimpleMailMessage simpMsg = new SimpleMailMessage();
        simpMsg.setFrom(from);
        simpMsg.setTo(to);
        simpMsg.setCc(cc);
        simpMsg.setSubject(subject);
        simpMsg.setText(content);
        javaMailSender.send(simpMsg);
    }
}

配置完成后,可以在单元测试中写一个测试方法进行测试:

import com.example.testspringboot.service.MailService;
import org.junit.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 org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.annotation.Resource;
import java.io.File;

@RunWith(SpringRunner.class)
@SpringBootTest


public class SendmailApplicationTests {

    @Resource
    MailService mailService;
    @Test
    public void sendSimpleMail() {
        mailService.sendSimpleMail("1739994163@qq.com",
                "qazjou@163.com",
                "1739994163@qq.com",
                "测试邮件主题",
                "测试邮件内容");
    }
}

执行该方法,便可以看到邮件发送成功了:

在这里插入图片描述

发送带附件的邮件:
通过调用Attachment 方法即可添加附件,该方法调用多个方法添加附件,在MailService添加:

 public void sendAttachFileMail(String from, String to,
                                   String subject, String content, File file) {
        try {
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message,true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content);
            helper.addAttachment(file.getName(), file);
            javaMailSender.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

使用了MimeMessageHelper简化了配置,构造方法的第二个参数true 表示构造一个multipart message 类型的邮件,此类型的邮件包含了多个正文、附件等,最后在单元测试进行测试:

 @Test
    public void sendAttachFileMail() {
        mailService.sendAttachFileMail("1739994163@qq.com",
                "qazjou@163.com",
                "测试邮件主题",
                "测试邮件内容",
                new File("C:\\Users\\17399\\Desktop\\1.txt"));
    }

运行后可见发送成功了。

在这里插入图片描述

有的邮件正文可能要插入图片,此时就不是附件的内容了,使用FileSystemResource实现功能:

public void sendMailWithImg(String from, String to,
                                String subject, String content,
                                String[] srcPath,String[] resIds) {
        if (srcPath.length != resIds.length) {
            System.out.println("发送失败");
            return;
        }
        try {
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper =
                    new MimeMessageHelper(message,true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content,true);
            for (int i = 0; i < srcPath.length; i++) {
                FileSystemResource res =
                        new FileSystemResource(new File(srcPath[i]));
                helper.addInline(resIds[i], res);
            }
            javaMailSender.send(message);
        } catch (MessagingException e) {
            System.out.println("发送失败");
        }
    }

在发送邮件时候分别传入图片资源路径和资源id,构造静态资源,然后调用addInline方法将资源加入邮件对象中。注意,在调用setText方法时候,第二个参数true 表示邮件正文是HTML格式的,该参数默认为false。

@Test
    public void sendMailWithImg() {
        mailService.sendMailWithImg("1739994163@qq.com",
                "qazjou@163.com",
                "测试邮件主题(图片)",
                "<div>hello,这是一封带图片资源的邮件:" +
                        "这是图片1:<div><img src='cid:p01'/></div>" +
                        "这是图片2:<div><img src='cid:p02'/></div>" +
                        "</div>",
                new String[]{"C:\\Users\\17399\\Desktop\\2.png",
                        "C:\\Users\\17399\\Desktop\\3.png"},
                new String[]{"p01", "p02"});
    }

运行结果如图:

在这里插入图片描述

定时任务

@Scheduled

定时任务是企业开发最常见的功能之一,如定时统计订单数、数据库备份、定时发送短信和邮件、定时统计博客访客。简单的定时任务直接通过@Scheduled 注解来实现,复杂的定时任务集成Quartz来实现。关于Quartz 可以看我这篇博客,Quartz任务

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

开启定时任务:
在项目启动类添加注解开启定时任务:

@EnableScheduling
@SpringBootApplication

定时任务主要 @Component 来进行配置:

@Component
public class MySchedule {
// 来标注一个定时任务,其中fixedDelay=1000 表示在当前任务结束后1s开始另一个任务
// initialDelay=1000 表示首次执行的延迟时间
    @Scheduled(fixedDelay = 1000)
    public void fixedDelay() {
        System.out.println("fixedDelay:"+new Date());
    }
    @Scheduled(fixedRate = 2000)
    public void fixedRate() {
        System.out.println("fixedRate:"+new Date());
    }
    @Scheduled(initialDelay = 1000,fixedRate = 2000)
    public void initialDelay() {
        System.out.println("initialDelay:"+new Date());
    }
// 也可以使用cron 表达式,来表示任务每分钟执行一次
    @Scheduled(cron = "0 * * * * ?")
    public void cron() {
        System.out.println("cron:"+new Date());
    }
}

配置完成后启动项目即可,定时任务部分打印日志如下:
在这里插入图片描述

Quartz

是一个功能丰富的开源作业调度库,由Java写成,可以集成在任何Java程序中,使用Quartz可以创建简单或者复杂的执行任务,支持数据库、集群、插件等,并且支持cron 表达式,有很高的灵活性,Spring Boot集成Quartz和Spring 集成非常类似,主要提供三个Bean: JobDetail,Trigger以及SchedulerFactory.

创建依赖:

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

然后创建两个Job:
MyFirstJob.java:

import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyFirstJob {
    public void sayHello() {
        System.out.println("MyFirstJob:sayHello:"+new Date());
    }
}

MySecondJob.java:

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;

public class MySecondJob extends QuartzJobBean {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    @Override
    protected void executeInternal(JobExecutionContext context){
        System.out.println("hello:"+name+":"+new Date());
    }
}

Job可以一个普通的JavaBean,如果是普通的JavaBean,那么可以添加@Component注解将之注册到Spring 容器中。当然也可以使用继承抽象类QuartzJobBean,则需要实现该类的executeInternal 方法,在任务被调用时候使用。接下来创建对JobDetail和Trigger 进行配置:

@Configuration
public class QuartzConfig {
// 两种方法配置JobDetail,只需要指定Job的实例名和要调用的方法即可,
// 注册这种方法无法在创建JobDetail 传递参数
    @Bean
    MethodInvokingJobDetailFactoryBean jobDetail1() {
        MethodInvokingJobDetailFactoryBean bean =
                new MethodInvokingJobDetailFactoryBean();
        bean.setTargetBeanName("myFirstJob");
        bean.setTargetMethod("sayHello");
        return bean;
    }
// 指定JobClass 即可,通过JobDtaMap 传递参数到Job中,Job只需要提供属性名和set
    @Bean
    JobDetailFactoryBean jobDetail2() {
        JobDetailFactoryBean bean = new JobDetailFactoryBean();
        bean.setJobClass(MySecondJob.class);
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("name","sang");
        bean.setJobDataMap(jobDataMap);
        bean.setDurability(true);
        return bean;
    }
// Trigger 有不同的实现方法,这里展示常用的Trigger:
// 这里设置JobDetail ,通过setRepeatCount 配置任务循环次数
    @Bean
    SimpleTriggerFactoryBean simpleTrigger() {
        SimpleTriggerFactoryBean bean =
                new SimpleTriggerFactoryBean();
        bean.setJobDetail(jobDetail1().getObject());
        bean.setRepeatCount(3);
// 启动延迟时间
        bean.setStartDelay(1000);
// 任务时间间隔
        bean.setRepeatInterval(2000);
        return bean;
    }
// 主要配置JobDetail 和Cron 表达式
    @Bean
    CronTriggerFactoryBean cronTrigger() {
        CronTriggerFactoryBean bean =
                new CronTriggerFactoryBean();
        bean.setJobDetail(jobDetail2().getObject());
        bean.setCronExpression("* * * * * ?");
        return bean;
    }
    @Bean
    SchedulerFactoryBean schedulerFactory() {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        SimpleTrigger simpleTrigger = simpleTrigger().getObject();
        CronTrigger cronTrigger = cronTrigger().getObject();
        bean.setTriggers(simpleTrigger,cronTrigger);
        return bean;
    }
}

配置完成后,便可以启动项目了,此时需要将前面在启动类的注解删去:

在这里插入图片描述

批处理

Spring Batch 是一个开源的、全面的、轻量级的批处理框架,通过给此可以实现强大的批处理应用程序的开发。Spring Batch 提供了记录/追踪,事务管理,作业处理统计等,可以结合定时任务发挥更大的作用。
也提供了ItemReader、ItemProcessor和Item Writer 来完成数据的读取,可将执行状态持久化到数据库中。下面通过简单的数据复制展示Spring Batch。

创建依赖以及数据库相关依赖:

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

添加数据库依赖是为了将批处理的执行状态持久化到数据库中,配置相应的信息:

spring.datasource.type= com.alibaba.druid.pool.DruidDataSource
#spring.datasource.url=jdbc:mysql:///ay_user
#spring.datasource.url=jdbc:mysql:///jpa
spring.datasource.url = jdbc:mysql:///ay_user
spring.datasource.username= root
spring.datasource.password=q1
# 表示项目启动时候创建数据表的SQL 脚本位置(由batch提供)
spring.datasource.schema = classpath:/org/springframework/batch/core/schema-mysql.sql
#  表示在项目启动时执行建表SQL
spring.batch.jdbc.initialize-schema=always
# 不自动执行,需要用户手动触发执行
spring.batch.job.enabled=false

接着在项目启动类上添加注解:

@EnableBatchProcessing
@SpringBootApplication

然后配置批处理:

@Configuration
public class CsvBatchJobConfig {
// 注入三个对象进行备用,这里持久化的方案是JDBC
    @Resource
    JobBuilderFactory jobBuilderFactory;
    @Resource
    StepBuilderFactory stepBuilderFactory;
    @Resource
    DataSource dataSource;
// 配置ItemReader,Batch 提供了常用的ItemReader
    @Bean
    @StepScope
    FlatFileItemReader<MUser> itemReader() {
// 加载一个普通文件的ItemReader
        FlatFileItemReader<MUser> reader = new FlatFileItemReader<>();
// 第一行是标题所以要跳过
        reader.setLinesToSkip(1);
// 找到配置的位置,此时已经写了,放在classpath目录下了
        reader.setResource(new ClassPathResource("data.csv"));
// 设置每一行的信息
        reader.setLineMapper(new DefaultLineMapper<MUser>(){{
            setLineTokenizer(new DelimitedLineTokenizer(){{
                setNames("id","username","address","gender");
// 配置列与列之间的间隔符
                setDelimiter("\t");
            }});
// 设置要映射的实体属性
            setFieldSetMapper(new BeanWrapperFieldSetMapper<MUser>(){{
                setTargetType(MUser.class);
            }});
        }});
        return reader;
    }
// 数据的写出逻辑
    @Bean
    JdbcBatchItemWriter jdbcBatchItemWriter() {
        JdbcBatchItemWriter writer = new JdbcBatchItemWriter();
        writer.setDataSource(dataSource);
// 注意占位符的写法: :属性名
        writer.setSql("insert into muser(id,username,address,gender) " +
                "values(:id,:username,:address,:gender)");
        writer.setItemSqlParameterSourceProvider(
                new BeanPropertyItemSqlParameterSourceProvider<>());
        return writer;
    }
// 配置一个Step,通过get来获取一个StepBuilder
    @Bean
    Step csvStep() {
        return stepBuilderFactory.get("csvStep")
                .<MUser, MUser>chunk(2)
                .reader(itemReader())
                .writer(jdbcBatchItemWriter())
                .build();
    }
    @Bean
    Job csvJob() {
        return jobBuilderFactory.get("csvJob")
                .start(csvStep())
                .build();
    }
}

这里涉及了一个MUser实体类:

public class MUser {
    private Integer id;
    private String username;
    private String address;
    private String gender;
// 省略set 和get 方法
}

此时在classpath 下的data.csv文件如下:
在这里插入图片描述

现在创建一个Controller ,当用户发起请求时候触发批处理:

注意此时jobLauncher.run括号的第二个参数不要写出null 否则会报错,直接生成new对象即可。

@RestController
public class BatchController {
    @Resource
    JobLauncher jobLauncher;
    @Resource
    Job job;

    @GetMapping("/testbatch")
    public String batchtest() {
        try{
// 框架提供,job是刚刚配置的,调用run方法来批处理
            jobLauncher.run(job,new JobParameters());
        }catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }
}

最后运行以后,会在batch库中创建多个批处理相关的库,这些表是用来记录批处理的执行状态,此时data.csv数据成功插入user 表。:
在这里插入图片描述

此时查看数据,已经成功插入表中:
在这里插入图片描述

Swagger 2

前后端分离开发中,为了减少沟通成本,一般需要构建RESTful API 文档来描述所有的接口信息,但仍有一些弊端,说明:

  1. 接口众多,编写RESTful API 文档工作量大,因为要包含接口的基本信息,例如接口地址、接口请求参数、接口返回值等。
  2. 维护不方便,一旦接口发生了变化,需要修改文档
  3. 接口测试不方便,只能借助于第三方工具,例如Postman来测试。

Swagger 2是一个开源软件框架,可以帮助开发人进行设计、构建、记录和使用等,将代码和文档融为一体,可以完美解决上述问题,可以较好地集中在业务当中,同时也可以非常好整合在Spring Boot.

用常规的依赖太老了,不兼容我2.7版本的Spring Boot。

# 这个依赖更好,可以Swagger 2的兼容(可以比较方便的做升级了)
<dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
        </dependency>

创建Swagger2 的配置类:

还有一种办法就是可以在启动类上面,加@EnableOpenApi 就直接可以运行了。(这个方法非常简单)

@Configuration
@EnableSwagger2
public class SwaggerConfig  implements WebMvcConfigurer {
    @Bean
// Docket is a builder which is intended to be the primary interface into the swagger-springmvc framework. 
    Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.testspringboot.controller"))
                .paths(PathSelectors.any()) // 所有包
                .build().apiInfo(new ApiInfoBuilder()
                        .description("接口文档")
                        .contact(new Contact("Jacin","baidu.com","1@qq.com"))
                        .version("v1.0")
                        .title("测试文档")
                        .license("Apache")
                        .licenseUrl("apache.org")
                        .build());
    }
}

通过@EnableSwagge2 注解开启 Swagger 2 ,主要是配置一个Docket,通过apis方法配置要扫描的controller 位置,通过paths 方法配置路径。在apiInfo 构建文档的基本信息,例如描述、联系人信息、版本等。
Swagger 2配置完成后,接下来开发接口了:

@RestController
//用来描述整个Controller 信息,默认就是类名
@Api(tags="用户接口")
public class SwaggerController {
// 用在方法上,描述一个信息,value 是简单的描述,notes 用来备注方法的详细作用
    @ApiOperation(value = "查询用户",notes = "根据id")
// 方法上,描述方法的参数
// paramType 是方法参数的类型,可选值有path( PathVariable),query(@RequestParam)
// name 表示参数名称,和下面的参数变量对应
//  value 是参数描述信息,required 吧iOS是否必填   
 @ApiImplicitParam(paramType = "path",name="id",value = "用户id",required = true)
    @GetMapping("/user/{id}")
    public String getUserById(@PathVariable Integer id) {
        return "/user/"+ id;
    }
// 这个是响应结果的描述,code 是响应码,message是描述信息,有多个那么就放在一个里面
    @ApiResponses({
            @ApiResponse(code = 200,message = "删除成功"),
            @ApiResponse(code = 500,message = "删除失败")
    })
    @ApiOperation(value = "删除用户",notes = "id删除")
    @DeleteMapping("/user/{id}")
    public Integer deletUserById(@PathVariable Integer id) {
        return  id;
    }

    @ApiOperation(value = "添加用户",notes = "添加一个用户,传入用户名和地址")
    @ApiImplicitParams({
            @ApiImplicitParam (paramType = "query" ,name = "username",value = "用户名",required = true,defaultValue = "sang"),
            @ApiImplicitParam(paramType = "query" ,name = "address",value = "用户地址",required = true,defaultValue = "sang")
    })
    @PostMapping("/user")
    public String addUser(@RequestParam String username,@RequestParam String address) {
        return username + ":" + address;
    }
    @ApiOperation(value = "修改用户",notes = "修改用户,传入用户信息")
//    @PostMapping("/user")
//    public String updateUser(@RequestBody MUser user) {
//        return user.toString();
//    }
    @GetMapping("/ignore")
// 不对某个接口生成文档
    @ApiIgnore
    public void ingoreMethod() {

    }

    @GetMapping("tst")
    public String tst() {
        return "tst";
    }
}

此时输入:http://localhost:8081/swagger-ui/index.html# (注意新版的格式

这次更新,移除了原来默认的swagger页面路径:http://host/context-path/swagger-ui.html,新增了两个可访问路径:http://host/context-path/swagger-ui/index.html和http://host/context-path/swagger-ui/
在这里插入图片描述

不过我一开始使用的是依赖是 (以下方法不一定成功)

 <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>3.0.0</version>
        </dependency>

而且用的http://localhost:8081/swagger-ui/ 发现怎么都进不去,一直报error.
查看swagger-ui 位置:
在这里插入图片描述

此时构造一个Webconfig 来实现 WebMvcConfigurer 接口方法:

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 解决静态资源⽆法访问
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/");
        // 解决swagger⽆法访问
        registry.addResourceHandler("/swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        // 解决swagger的js⽂件⽆法访问
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

不过我这里确实没实现出来,应该是spring boot 版本问题。毕竟swagger2 太久没更新了,所以使用新版的swagger2。

以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/65632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python入门】Python入门必备思维导图,有方向的学习才是最实在的方法

前言 halo&#xff0c;包子们下午好 这篇文章主要是跟大家聊聊我们学习Python该怎么去学习 小编个人认为&#xff0c;学习Python一定要有一个好的学习方向&#xff0c;这样才知道自己该往什么方向去学习 今天小编主要给大家准备了我们学习Python的必备14张思维导图 赶快收藏起…

【SSM整合】SpringMVC + Spring + Mybatis整合详细教程

前言 本文为 SpringMVC Spring Mybatis整合教程 相关详细介绍&#xff0c;从数据库数据准备、相关依赖包添加、创建项目基本结构和配置框架&#xff0c;到Mybatis层的编写、Spring层的编写、SpringMVC层的编写&#xff0c;再到前端页面编写等&#xff0c;最后进行运行与测试&…

新生报到小程序毕业设计,新生报到管理系统设计与实现,毕设作品参考

功能清单 【后台管理员功能】 广告管理&#xff1a;设置小程序首页轮播图广告和链接 留言列表&#xff1a;所有用户留言信息列表&#xff0c;支持删除 录入资讯&#xff1a;录入资讯标题、内容等信息 管理资讯&#xff1a;查看已录入资讯列表&#xff0c;支持删除和修改 学校…

MySQL——表的约束

文章目录表的约束空属性默认值列描述zerofill主键&#x1f60a;自增长唯一键&#x1f60a;外键&#x1f60a;表的约束 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑…

前端给数字添加横线贯穿

效果 实现方法 1、给这个数字添加css属性&#xff1a;text-decoration: line-through; 2、给这个数字添加标签&#xff1a;del。如&#xff1a;<del>9999</del>。 text-decoration详解&#xff1a; text-decoration : none || underline || blink || overline |…

使用RMI实现RPC

1 RMI简介 RMI(Remote Method Invocation) 远程方法调用。 RMI是从JDK1.2推出的功能&#xff0c;它可以实现在一个Java应用中可以像调用本地方法一样调用另一个服务器中Java应用&#xff08;JVM&#xff09;中的内容。 RMI 是Java语言的远程调用&#xff0c;无法实现跨语言。…

LVGL自定义组件__页面指示器

前言 LVGL对硬件的要求非常低&#xff0c;使用其自带的组件能够搭建出精美的界面&#xff0c;动效也很棒。但是如过移植到Linux平台下&#xff0c;开发稍微复杂的应用项目&#xff0c;那些组件就远远不够用了。为此需要自己自定义一些组件&#xff0c;以方便实用。 效果 为此…

D-star Lite算法及相关思考

一、D-star Lite算法简介 1994年 Anthony Stentz在论文Optimal and Efficient Path Planning for Partially-known Environments中提出了D*算法 D * 算法采用逆向搜索&#xff0c;从目标点向起始点搜索规划&#xff0c;可以处理环境部分未知或全部未知以及动态障碍的一些情况。…

神经网络入门(一)

神经网络入门&#xff08;一&#xff09; 文章目录神经网络入门&#xff08;一&#xff09;1. 神经元2. 网络结构3. 激活函数2.1 激活函数的作用与性质2.2 Sigmoid型函数2.3 Hard-Logistic函数和 Hard-Tanh函数2.4 ReLU函数2.5 常见的激活函数及其导数4. 前馈神经网络4.1 前馈神…

DAY05-网页布局实战选择器CSS样式

文章目录网页布局实战一 CSS选择器a.基本选择器1.元素选择器2.类选择器3.id选择器b.复杂选择器c 伪选择器d 伪元素选择器二 CSS常用样式1.文本相关属性2.背景属性1)背景颜色2)背景图片3)背景平铺4)背景位置5)设置背景图片大小&#xff1a;6)background 背景复合属性案例1案例2案…

Session的使用

Session的使用Session概述Session特性Session原理Session对象操作获取Session对象-----getSession获取SessionID------getId向Session对象存储/修改数据-----setAttribute获取数据------getAttribute删除数据----removeAttributeSession对象失效Session概述 Session对象&#…

rust编程-rust所有权理解(chapter 4.2 引用实质是借用)

目录 2. 引用与借用 2.1 可变(mutable)引用 2.2 悬空&#xff08;dangling&#xff09;引用 2.3 引用的规则总结 2. 引用与借用 上一章节中提到&#xff0c;所有权在函数调用中的转移&#xff0c;函数返回必须同时返还所有权&#xff0c;才能使函数调用后能继续使用某个变量…

[附源码]计算机毕业设计勤工助学管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

jquery 登录-记住密码

jquery 登录-记住密码在登录时&#xff0c;添加记住密码功能&#xff1a;用的 localStorage 存储和获取登录信息 //存储 var username $("input[nameusername]").val(); var password $("input[namepassword]").val(); var validateCode $("inpu…

自动驾驶:2022 apollo day 观后感(一)

2022 apollo day 观后感&#xff08;一&#xff09;注&#xff1a; ppt来自apollo day&#xff0c;结合ppt&#xff0c;讲述一些自己的想法&#xff0c;欢迎批评指正&#xff01;Topic One -- 打造安全、智能、高效的自动驾驶技术体系&#xff08;陈竞凯&#xff09;无人驾驶技…

Ruby ERB模板注入检测

了解Ruby ERB模板注入,Ruby ERB模板注入检测。 ERB是Ruby自带的 <% 写逻辑脚本(Ruby语法) %><%= 直接输出变量值或运算结果 %>require erbtemplate = "text to be generated: <%= x %>" erb_object = ERB.new(template) x = 5 puts erb_object.r…

[附源码]JAVA毕业设计人才公寓管理系统(系统+LW)

[附源码]JAVA毕业设计人才公寓管理系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

Linux——Xshell、Xftp实现Linux远程登录与应用

目录 一、远程登录 1.1 SSH登录方式 二、Xshell远程连接 2.1 远程连接 2.2 设置粘贴复制 三、Xftp远程连接 3.1 远程连接 3.2 解决乱码 3.3 传输文件 一、远程登录 通常在工作过程中&#xff0c;公司中使用的真实服务器或者是云服务器&#xff0c;都不允许除运维人员 之…

微电网优化调度|农村农业区可再生能源微电网优化调度(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;主要研究方向是电力系统和智能算法、机器学…

软件测试就业现状分析,2023是卷还是润?

一、当前软件测试的现状是什么&#xff1f; 现状1、网络上大量人唱衰&#xff0c;测试就业不行了 2022年5月后&#xff0c;越来越多人网络发声“互联网行业不行了”。贴吧、知乎、小红书上&#xff0c;也有大量人反馈软件测试就业惨淡……&#xff0c;篇幅有限&#xff0c;仅…