常用快捷键 CTRL+D 当前行内容快速向下复制 CTRL+F3 查找当前文件的所有引用 Alt+ins 快速生成setter、getter,构造方法 CTRL+ALT+L 代码格式化 CTRL+SHIFT+o 刷新Maven依赖 源码页面Ctrl + H可以看接口的层次结构 ctrl+alt+T 包围代码try-catch等
命名空间开启 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:property-placeholder location ="jdbc.properties" />
jdbc.properties文件 注意:如果出现以下报错:
Error querying database. Cause: java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mysql.jdbc.Driver
MySQL JDBC驱动从8.0版本开始,驱动类名从 com.mysql.jdbc.Driver 更改为 com.mysql.cj.jdbc.Driver。如果您使用的是MySQL 8.0或更高版本的JDBC驱动,则需要在您的配置文件中将驱动类名更改为新的名称。
1 2 3 4 jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/spring_db1 jdbc.username =root jdbc.password =123456
springboot版本问题 注意:由于版本问题,系统自带的版本可能低于当前支持的版本,会导致报错产生,如果你的版本报错,可以参照我下面的版本进行修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <?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.1.7</version > <relativePath /> </parent > <groupId > com.example</groupId > <artifactId > demo3</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > demo3</name > <description > demo3</description > <properties > <java.version > 17</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > com.mysql</groupId > <artifactId > mysql-connector-j</artifactId > <scope > runtime</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.5</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.16</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
构建Config的时候出现无法解析jdbc.properties 这个问题原则上不影响使用,但是看起来让人不舒服,解决办法,在前面加上 classpath
1 2 3 4 5 @Configuration @ComponentScan("Test.Config") @PropertySource("classpath:jdbc.properties") public class Config {}
快速整合druid数据源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class jdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DruidDataSource druidDataSource () { DruidDataSource dataSource = new DruidDataSource (); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
这里要想不写improt导入,需要在头上写@Configuration,否则就需要在SpringConfig中加上@Import({jdbcConfig.class})
快速整合mybatis 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactoryBean (DataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean (); sqlSessionFactoryBean.setTypeAliasesPackage("Test.domain" ); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer () { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer (); mapperScannerConfigurer.setBasePackage("Test.Dao" ); return mapperScannerConfigurer; } }
AOP需要的坐标 1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.4</version > </dependency >
因为spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop 导入AspectJ的jar包,AspectJ是AOP思想的一个具体实现,Spring有自己的AOP实现,但是相比于AspectJ来说比较麻烦,所以我们直接采用Spring整合ApsectJ的方式进行AOP开发。
编写测试类时出现@RunWith注解找不到 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 3.8.1</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.16</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.46</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 1.3.0</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.2.10.RELEASE</version > </dependency > </dependencies >
上述代码中,第一个junit是初始化的时候自带的,经过测试想要解决上述问题,只需要删除第一个junit依赖,同时加上
1 2 3 4 5 6 <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > compile</scope > </dependency >
在加载外部配置文件jdbc.properties时出现下面的错误 明明配置文件 配置得没有错误,到运行的时候出现以下错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 严重: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@7cbd213e] to prepare test instance [Test.AccountServiceTest@130d63be] java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132 ) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123 ) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118 ) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83 ) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244 ) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227 ) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1. runReflectiveCall(SpringJUnit4ClassRunner.java:289 ) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12 ) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291 ) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246 ) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97 ) at org.junit.runners.ParentRunner$3. run(ParentRunner.java:290 ) at org.junit.runners.ParentRunner$1. schedule(ParentRunner.java:71 ) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288 ) at org.junit.runners.ParentRunner.access$000 (ParentRunner.java:58 ) at org.junit.runners.ParentRunner$2. evaluate(ParentRunner.java:268 ) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61 ) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70 ) at org.junit.runners.ParentRunner.run(ParentRunner.java:363 ) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190 ) at org.junit.runner.JUnitCore.run(JUnitCore.java:137 ) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69 ) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1. execute(IdeaTestRunner.java:38 ) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11 ) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35 ) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232 ) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55 ) Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [Test.Config.SpringConfig]; nested exception is java.io.FileNotFoundException: class path resource [ jdbc.properties] cannot be opened because it does not exist at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:189 ) at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:319 ) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236 ) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280 ) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96 ) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707 ) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533 ) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:127 ) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60 ) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:275 ) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:243 ) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99 ) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124 ) ... 26 more Caused by: java.io.FileNotFoundException: class path resource [ jdbc.properties] cannot be opened because it does not exist <--------- 重点找到这个 at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:180 ) at org.springframework.core.io.support.EncodedResource.getInputStream(EncodedResource.java:159 ) at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:99 ) at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:73 ) at org.springframework.core.io.support.PropertiesLoaderUtils.loadProperties(PropertiesLoaderUtils.java:59 ) at org.springframework.core.io.support.ResourcePropertySource.<init>(ResourcePropertySource.java:67 ) at org.springframework.core.io.support.DefaultPropertySourceFactory.createPropertySource(DefaultPropertySourceFactory.java:37 ) at org.springframework.context.annotation.ConfigurationClassParser.processPropertySource(ConfigurationClassParser.java:463 ) at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:280 ) at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250 ) at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:207 ) at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:175 ) ... 38 more
简单解释以下就是 cannot be opened because it does not exist 不能打开jdbc.properties 因为它不存在 问题就在下面,因为没有空格,所以找不到,所以加上空格,就找到文件了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration @ComponentScan("Test") @PropertySource("classpath: jdbc.properties") @Import({jdbcConfig.class,MybatisConfig.class}) public class SpringConfig {} @Configuration @ComponentScan("Test") @PropertySource("classpath:jdbc.properties") @Import({jdbcConfig.class,MybatisConfig.class}) public class SpringConfig {}
POST请求中文乱码 解决方案:配置过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class [0 ]; } protected Class<?>[] getServletConfigClasses() { return new Class []{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String []{"/" }; } @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter (); filter.setEncoding("utf-8" ); return new Filter []{filter}; } }
SpringMVC学习所需依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <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 http://maven.apache.org/maven-v4_0_0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > org.example</groupId > <artifactId > untitled12</artifactId > <packaging > war</packaging > <version > 1.0-SNAPSHOT</version > <name > untitled12 Maven Webapp</name > <url > http://maven.apache.org</url > <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 3.8.1</version > <scope > test</scope > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 3.8.1</version > <scope > test</scope > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 3.1.0</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.9.0</version > </dependency > </dependencies > <build > <finalName > untitled12</finalName > <plugins > <plugin > <groupId > org.apache.tomcat.maven</groupId > <artifactId > tomcat7-maven-plugin</artifactId > <version > 2.1</version > <configuration > <port > 8081</port > <path > /</path > </configuration > </plugin > </plugins > </build > </project >
springMVc访问页面404问题 我们通过注解形式配置的springMVC配置,初学者会出现静态资源加载出现404
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.config;import org.springframework.web.filter.CharacterEncodingFilter;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;import javax.servlet.Filter;public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class [0 ]; } @Override protected Class<?>[] getServletConfigClasses() { return new Class []{springConfig.class}; } @Override protected String[] getServletMappings() { return new String []{"/" }; } @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter (); filter.setEncoding("utf-8" ); return new Filter []{filter}; } }
上述代码中问题出现在getServletMappings
这段代码,我们设置的是拦截所有资源交由springmvc来处理,其实在容器中又没有静态资源 所以控制台下面会报错[WARNING] No mapping for GET /Book.html
我们需要配置将静态页面给放行,不由它来处理就行 config包中SpringMvcSupport
1 2 3 4 5 6 7 8 9 10 @Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers (ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**" ).addResourceLocations("/pages/" ); registry.addResourceHandler("/js/**" ).addResourceLocations("/js/" ); registry.addResourceHandler("/css/**" ).addResourceLocations("/css/" ); registry.addResourceHandler("/plugins/**" ).addResourceLocations("/plugins/" ); } }
记住加载进springmvcconfig来管理 然后重启就行了
端口被占用导致前端资源找不到或者后台报错 通过下面的方法查找正在使用的端口,一般找没有前缀的TCP
使用命令行工具 netstat: 打开命令提示符(CMD),然后输入以下命令: netstat -ano 这会列出当前系统中所有的网络连接和监听端口,以及相应的进程 ID(PID)。你可以在输出中查找你感兴趣的端口号,并查看对应的 PID。然后你可以使用任务管理器或其他工具,通过 PID 来找到占用该端口的进程。
使用资源监视器: 在 Windows 中,你也可以使用资源监视器来查看网络活动情况,包括端口占用情况。你可以按下 Win + R 打开运行窗口,输入 resmon,然后按 Enter 键来打开资源监视器。在资源监视器中,切换到网络选项卡,你将看到正在使用的端口及其相关信息。
IDEA中建包的时候如何才能把包分开 原先我们创建包时输入com.atguigu.web时,idea只会有一个包。那么如何变成层级结构的两个包呢?
🧑💻点击链接
tomcat插件以及端口等书写方式 1 2 3 4 5 6 7 8 9 10 11 <plugins > <plugin > <groupId > org.apache.tomcat.maven</groupId > <artifactId > tomcat7-maven-plugin</artifactId > <version > 2.1</version > <configuration > <port > 8081</port > <path > /</path > </configuration > </plugin > </plugins >
在SpringBoot中访问路径显示404 第一种情况,Controller包应该和application.java同级,否则访问路径会显示404。
SpringBoot快速整合Mybatis的yml配置 添加依赖,版本可以自己改
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.12</version > </dependency >
yml配置: 注意: SpringBoot 版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在url连接串中配置时区 jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC,或在MySQL数据库端配置时区解决此问题
1 2 3 4 5 6 7 8 9 10 11 server: port: 8081 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource
Lombok常见的注解和依赖 1 2 3 4 5 <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency >
@Setter:为模型类的属性提供setter方法 @Getter:为模型类的属性提供getter方法 @ToString:为模型类的属性提供toString方法 @EqualsAndHashCode:为模型类的属性提供equals和hashcode方法 @Data:是个组合注解,包含上面的注解的功能 @NoArgsConstructor:提供一个无参构造函数 @AllArgsConstructor:提供一个包含所有参数的构造函数
1 2 3 4 5 6 7 8 9 10 @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private String password; private Integer age; private String tel; }
分页拦截器的固定写法 在分页配置完成后,只有加上这段代码,才能实现分页功能。
1 2 3 4 5 6 7 8 9 10 11 @Configuration public class ConfigPlus { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor (); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor ()); return mybatisPlusInterceptor; } }
项目部署yml文件配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 server: port: 8081 spring: application: name: reggie_take_out datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true username: root password: 123456 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: ASSIGN_ID
SpringBoot公共字段的自动填充 1 2 3 4 5 6 7 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;
updatetime这边需要将INSERT_UPDATE,如果只添加update,则在新增操作的时候系统会报updatetime字段未初始化
文件上传与下载(阿里云oss与本地版) 文件上传介绍
method=”post”,采用post方式提交数据
enctype=”multipart/form-data”,采用multipart格式上传文件
type=”file”,使用input的file控件上传
下面是前端的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > 文件上传</title > <link rel ="stylesheet" href ="../../plugins/element-ui/index.css" /> <link rel ="stylesheet" href ="../../styles/common.css" /> <link rel ="stylesheet" href ="../../styles/page.css" /> </head > <body > <div class ="addBrand-container" id ="food-add-app" > <div class ="container" > <el-upload class ="avatar-uploader" action ="/common/upload" :show-file-list ="false" :on-success ="handleAvatarSuccess" :before-upload ="beforeUpload" ref ="upload" > <img v-if ="imageUrl" :src ="imageUrl" class ="avatar" > </img > <i v-else class ="el-icon-plus avatar-uploader-icon" > </i > </el-upload > </div > </div > <script src ="../../plugins/vue/vue.js" > </script > <script src ="../../plugins/element-ui/index.js" > </script > <script src ="../../plugins/axios/axios.min.js" > </script > <script src ="../../js/index.js" > </script > <script > new Vue ({ el : '#food-add-app' , data ( ) { return { imageUrl : '' } }, methods : { handleAvatarSuccess (response, file, fileList) { this .imageUrl = `/common/download?name=${response.data} ` }, beforeUpload (file) { if (file){ const suffix = file.name .split ('.' )[1 ] const size = file.size / 1024 / 1024 < 2 if (['png' ,'jpeg' ,'jpg' ].indexOf (suffix) < 0 ){ this .$message .error ('上传图片只支持 png、jpeg、jpg 格式!' ) this .$refs .upload .clearFiles () return false } if (!size){ this .$message .error ('上传文件大小不能超过 2MB!' ) return false } return file } } } }) </script > </body > </html >
通过这个示例代码我们来写后端的代码
文件上传本地版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @RestController @RequestMapping("/common") @Slf4j public class CommonController { @Value("${reggie.path}") private String basepath; @PostMapping("/upload") public Result<String> upload (MultipartFile file) { log.info("获取文件:{}" , file.toString()); File dir = new File (basepath); if (!dir.exists()) { dir.mkdirs(); } String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf("." )); String fileName = UUID.randomUUID() + suffix; try { file.transferTo(new File (basepath + fileName)); } catch (IOException e) { throw new RuntimeException (e); } return Result.success(fileName); } }
文件转存的位置改为动态可配置
1 2 reggie: path: E:\\reggie\\img\\
文件下载本地版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @GetMapping("/download") public void download (String name, HttpServletResponse response) { FileInputStream fis = null ; ServletOutputStream os = null ; try { fis = new FileInputStream (basePath + name); os = response.getOutputStream(); response.setContentType("image/jpeg" ); int len; byte [] buffer = new byte [1024 ]; while ((len = fis.read(buffer)) != -1 ) os.write(buffer, 0 , len); } catch (IOException e) { throw new RuntimeException (e); } finally { if (fis != null ) { try { fis.close(); } catch (IOException e) { throw new RuntimeException (e); } } if (os != null ) { try { os.close(); } catch (IOException e) { throw new RuntimeException (e); } } } }
文件上传OOS版: 由于要上传服务器所以这边的 ACCESS_KEY_ID ACCESS_KEY_SECRET 直接在代码里面配置也可以在yml文件里面配置 对于官方的代码需要修改一下,官方的是在本地的环境中自动检测环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class AliOSSUtil { private static final String ENDPOINT = "" ; private static final String ACCESS_KEY_ID="" ; private static final String ACCESS_KEY_SECRET="" ; private static final String BUCKETNAME = "" ; public static String uploadFile (String objectName, InputStream inputStream) throws Exception { OSS ossClient = new OSSClientBuilder ().build(ENDPOINT,ACCESS_KEY_ID,ACCESS_KEY_SECRET ); String url = "" ; try { String content = "Hello OSS,你好世界" ; PutObjectRequest putObjectRequest = new PutObjectRequest (BUCKETNAME, objectName, inputStream); PutObjectResult result = ossClient.putObject(putObjectRequest); url="https://" +BUCKETNAME+"." +ENDPOINT.substring(ENDPOINT.lastIndexOf("/" )+1 )+"/" +objectName; } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason." ); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network." ); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null ) { ossClient.shutdown(); } } return url; } }
调用就行了
1 2 3 4 5 6 7 8 9 @PostMapping("/upload") public Result<String> upload (MultipartFile file) throws Exception { log.info(file.toString()); String originalFilename = file.getOriginalFilename(); String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("." )); String url = AliOSSUtil.uploadFile(filename, file.getInputStream()); return Result.success(url); }
文件下载OOS版 这里我们使用阿里云官方给我们提供的流式下载,这里我们依然要修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.reggie_take_out.OSS;import com.aliyun.oss.OSS;import com.aliyun.oss.OSSClientBuilder;import com.aliyun.oss.OSSException;import com.aliyun.oss.model.OSSObject;import jakarta.servlet.ServletOutputStream;import java.io.BufferedReader;import java.io.InputStreamReader;public class AliOSSDownLoad { private static final String ENDPOINT = "" ; private static final String ACCESS_KEY_ID = "" ; private static final String ACCESS_KEY_SECRET = "" ; private static final String BUCKETNAME = "" ; public static void downloadFile (String objectName, ServletOutputStream inputStream) { OSS ossClient = new OSSClientBuilder ().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET); try { OSSObject ossObject = ossClient.getObject(BUCKETNAME, objectName); System.out.println("Object content:" ); BufferedReader reader = new BufferedReader (new InputStreamReader (ossObject.getObjectContent())); while (true ) { String line = reader.readLine(); if (line == null ) break ; System.out.println("\n" + line); } reader.close(); ossObject.close(); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason." ); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (Throwable ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network." ); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null ) { ossClient.shutdown(); } } } }
调用就行了
1 2 3 4 @GetMapping("/download") public void download (String name, HttpServletResponse response) throws IOException { AliOSSDownLoad.downloadFile(name,response.getOutputStream()); }
然后前端需要改一点点就是将
1 2 3 handleAvatarSuccess (response, file, fileList) { this.imageUrl = `/common/download?name=${response.data}` },
/common/download?name=
删除掉,让完整的路径进来就行了
spring 整合mybatis所需要的依赖 这个是最新的依赖不会出现依赖易受攻击的警告
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.33</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.16</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 6.1.5</version > </dependency >
邮箱验证功能问题 https://zhuanlan.zhihu.com/p/340704323 近两年国内各大云服务器厂商如阿里云、腾讯云、各种云纷纷加入了对25端口的封杀行列,最主要也是最重要的原因就是为了防堵垃圾邮件的发送。
电子邮件SMTP的协议标准本身就是非常脆弱的,协议的制定者在当时相对单纯的网络环境下制定了单纯的SMTP协议,其中最大的缺陷就是SMTP协议是开放性的,发信域和收信域之间缺乏认证机制
认证机制的缺乏导致了SMTP协议是包容的,当然也就包括了垃圾邮件。
就SMTP协议层面来说,垃圾邮件发送者只需要租用一台服务器,就可以使用任意域名任意发送垃圾邮件,这也就导致了电子邮件的世界垃圾邮件遍地。
为了解决这个问题不至于成为垃圾邮件发送者的帮凶,国内各大服务器厂商都开始封锁25端口,限制邮件的外发能力,并建议使用者使用465端口来代替25端口发送邮件
那么465端口真的能发送邮件吗,答案是能也不能,看使用者如何去理解。
这里有一个误区,许多不了解SMTP和电子邮件实现原理的人会误以为直接用465代替25端口就能实现邮件的最终发送,这是错误的。
为了说明问题,需要普及一点,一封email邮件的传递,从发件人发出到最终进入收件人的邮箱,是多方接力完成的,以下是邮件以此流转的简化过程:
1、发件人Foxmail/Outlook客户端 ====> 发信服务器(25端口或其他任意端口) 2、发件人所在发信服务器 ====> 收信服务器(25端口) 可以看到,一封邮件首先由Foxmail或Outlook客户端生成,然后进入第二步发件人邮箱服务器,使用25端口或其他任意端口都可以,邮件到这里并没有真正投递成功,你可以想象成寄快递,到目前为止你只是将包裹交给了快递公司,由快递公司负责将包裹投递给收件人。
发件人所在发信服务器收到邮件后,会查询收件人域名的MX记录解析到对应的收件IP,并将邮件投递到该IP地址的25端口,这一步的25端口是固定的雷打不动,SMTP协议就是这么规定的,除非你和收件方有特别的约定使用私有端口,否则你一定要连接大家约定俗成的25端口才可能将邮件真正投递出去。
以往25端口没有被封锁的年代,随便租用一台vps主机即可往外疯狂发送垃圾邮件,导致服务器主机商被人投诉不堪其扰,后来索性就一概将25端口封锁,宁可错杀不可错放
阿里云、腾讯云封锁25端口就是为了限制用户在邮件发送这一过程中扮演邮件最终投递者的角色。但如果你确实有正常邮件需要发送怎么办?厂商告诉你:OK,你不能直接使用25端口发送,请使用465或其他端口,将邮件交由第三方系统发送。即你可以寄快递,但请交给快递公司,让他们来寄送。如果你想做快递公司自己寄送,那么不欢迎你。
源码留存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @PostMapping("/sendMsg") public Result<String> sendMsg (@RequestBody User user, HttpSession session) throws MessagingException { String phone = user.getPhone(); if (!phone.isEmpty()) { String code = MailUtils.achieveCode(); log.info(code); MailUtils.sendTestMail(phone, code); session.setAttribute(phone, code); return Result.success("验证码发送成功" ); } return Result.error("验证码发送失败" ); } @PostMapping("/login") public Result<User> login (@RequestBody Map map, HttpSession session) { log.info(map.toString()); String phone = map.get("phone" ).toString(); String code = map.get("code" ).toString(); String codeInSession = session.getAttribute(phone).toString(); if (code != null && code.equals(codeInSession)) { LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(User::getPhone, phone); User user = userService.getOne(queryWrapper); if (user == null ) { user = new User (); user.setPhone(phone); userService.save(user); user.setName("用户" + codeInSession); } session.setAttribute("user" ,user.getId()); return Result.success(user); } return Result.error("登录失败" ); }
Java 8的Stream API对集合进行处理 这段代码我在项目中经常使用,我觉得很有必要记录下来,后面的时候需要用到直接拿过来就行
1 2 3 4 5 6 7 8 List<ShoppingCart> shoppingCartList = orderDetailList.stream().map(x -> { ShoppingCart shoppingCart = new ShoppingCart (); BeanUtils.copyProperties(x, shoppingCart, "id" ); shoppingCart.setUserId(userId); shoppingCart.setCreateTime(LocalDateTime.now()); return shoppingCart; }).collect(Collectors.toList());
一段一段的解释: 创建Stream:
1 orderDetailList.stream()
使用stream()
方法从orderDetailList
(假设是一个List<OrderDetail>
或类似的结构)中创建一个Stream
转换每个元素:
使用map函数对Stream中的每个元素(在这里用x表示)执行一个转换操作。转换操作是创建一个新的ShoppingCart对象,并将OrderDetail对象(x)中的属性复制到新的ShoppingCart对象中 收集结果:
1 .collect(Collectors.toList());
使用collect方法和Collectors.toList()收集器将Stream中的元素(在这里是转换后的ShoppingCart对象)收集到一个新的列表中。这个列表就是最终的结果,并赋值给shoppingCartList。
总结:常见的使用场景 Java 8 Stream API 的使用场景非常广泛,特别适用于对集合(如 List、Set、Map 等)进行复杂的数据处理。以下是一些常见的使用场景:
数据转换(Mapping)和过滤(Filtering) :当你需要将一个集合中的元素转换成另一种形式,并可能同时过滤掉一些元素时,Stream API 非常有用。例如,从一个用户列表中提取出所有活跃用户的姓名和电子邮件地址。
数据聚合(Aggregation) :需要对集合中的元素进行聚合操作时,如计算总和、平均值、最大值、最小值等,可以使用 Stream API 提供的 reduce、sum、average 等方法。
排序(Sorting) :如果你想对集合中的元素进行排序,可以使用 sorted 方法,它接受一个 Comparator。例如,根据价格对商品列表进行排序。
分组(Grouping) :使用 collect 方法配合 Collectors.groupingBy 可以轻松地将集合中的元素按照某个属性进行分组。例如,按照订单状态对订单列表进行分组。
查找和匹配(Finding and Matching) :Stream API 提供了 anyMatch、allMatch、noneMatch 方法用于检查集合中的元素是否满足某个条件,以及 findFirst、findAny 方法用于查找满足条件的第一个或任意一个元素。
并行处理(Parallel Processing) :通过调用 parallelStream() 而不是 stream(),你可以利用多核处理器并行处理集合中的元素。这在处理大数据集时可以提高性能。
复杂数据处理 :当数据处理逻辑非常复杂,涉及到多个步骤时,Stream API 的链式调用可以使代码更加清晰和易于维护。你可以将多个中间操作和终端操作组合在一起,形成一个流畅的处理管道。
与数据库查询结合 :在某些情况下,你可以使用 Stream API 来模拟数据库查询的某些功能,尤其是在内存中进行复杂的数据处理时。然而,请注意,对于大型数据集,直接使用数据库查询通常更加高效。
函数式编程风格 :Stream API 鼓励使用函数式编程风格,这有助于提高代码的可读性和可维护性。通过将数据和处理逻辑分离,你可以更容易地理解和测试代码。
与其他Java 8特性结合 :Stream API 可以与 Java 8 中的其他新特性(如 Lambda 表达式、方法引用、默认方法和接口中的静态方法等)结合使用,以进一步简化代码并提高性能。
SpringTask 使用方式
导入maven坐标spring-context(已存在)
启动类添加注解@EnableScheduling开启任务调度
自定义定时任务类
举个例子: 每隔五秒触发一次
1 2 3 4 5 6 7 8 9 10 11 @Component @Slf4j public class MyTask { @Scheduled(cron = "0/5 * * * * ?") public void executeTask () { log.info("定时任务开始执行:{}" ,new Date ()); } }
通过POI创建excel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class POITest { public static void write () throws Exception { XSSFWorkbook excel = new XSSFWorkbook (); XSSFSheet sheet = excel.createSheet("info" ); XSSFRow row = sheet.createRow(1 ); row.createCell(1 ).setCellValue("姓名" ); row.createCell(2 ).setCellValue("城市" ); row = sheet.createRow(2 ); row.createCell(1 ).setCellValue("张三" ); row.createCell(2 ).setCellValue("北京" ); row = sheet.createRow(3 ); row.createCell(1 ).setCellValue("李四" ); row.createCell(2 ).setCellValue("安徽" ); FileOutputStream out = new FileOutputStream (new File ("D:\\info.xlsx" )); excel.write(out); out.close(); excel.close(); } public static void main (String[] args) throws Exception { write(); } }
读取文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void read () throws Exception { FileInputStream fileInputStream = new FileInputStream (new File ("D:\\info.xlsx" )); XSSFWorkbook excel = new XSSFWorkbook (fileInputStream); XSSFSheet sheet = excel.getSheetAt(0 ); int lastRowNum = sheet.getLastRowNum(); for (int i = 1 ; i < lastRowNum; i++) { XSSFRow row = sheet.getRow(i); String cellValue = row.getCell(1 ).getStringCellValue(); String cellValue1 = row.getCell(2 ).getStringCellValue(); System.out.println(cellValue + " " + cellValue1); } excel.close(); fileInputStream.close(); }