一文学习SpringBoot

一、SpringBoot介绍

(一)SpringBoot简介

        Spring Boot 是由 Pivotal 团队提供的一个用于简化 Spring 应用初始搭建以及开发过程的框架。它基于 Spring 框架,旨在通过减少配置和简化开发流程来加速应用的开发和部署。Spring Boot 提供了嵌入式的 Tomcat、Jetty 或 Undertow 服务器,使得应用可以作为一个独立的 JAR 文件运行,无需外部部署。

(二)SpringBoot 的优点

  • 简化配置:
    • 自动配置:Spring Boot 会根据类路径中的依赖自动配置 Spring 应用,减少了手动配置的需要。
    • Starter 依赖:通过引入 spring-boot-starter-* 依赖,可以快速集成各种功能,如 Web、JPA、Security 等。
  • 快速启动:
    • 嵌入式服务器:支持嵌入式 Tomcat、Jetty 或 Undertow,使得应用可以作为一个独立的 JAR 文件运行,无需外部部署。
    • 减少启动时间:由于简化了配置和依赖管理,应用的启动时间大大缩短。
  • 生产就绪:
    • 监控和管理:提供了 Actuator 模块,可以方便地监控和管理应用。
    • 健康检查:内置健康检查功能,便于监控应用状态。
  • 社区支持:
    • 活跃社区:Spring Boot 拥有庞大的用户社区和丰富的文档资源,便于开发者解决问题和获取帮助。
    • 广泛使用:许多大型企业和开源项目都在使用 Spring Boot,证明了其稳定性和可靠性。
  • 微服务支持:
    • Spring Cloud:与 Spring Cloud 结合使用,可以轻松构建微服务架构。
    • 服务发现、负载均衡:提供了服务发现(Eureka)、配置中心(Config Server)等功能,支持微服务的开发和部署。

(三)SpringBoot 的缺点

  • 学习曲线:
    • 复杂性:虽然 Spring Boot 简化了许多配置,但对于初学者来说,理解其自动配置机制和依赖管理可能需要一定时间。
    • 依赖管理:Spring Boot 的 Starter 依赖可能会引入不必要的库,导致应用体积增大。
  • 过度封装:
    • 黑盒操作:自动配置机制可能会隐藏一些底层实现细节,使得开发者难以理解某些配置是如何生效的。
    • 调试困难:当遇到问题时,由于自动配置的复杂性,调试过程可能会变得复杂。
  • 性能开销:
    • 嵌入式服务器:虽然嵌入式服务器简化了部署,但在高并发场景下,性能可能不如独立部署的服务器。
    • 内存占用:Spring Boot 应用通常会占用较多的内存,尤其是在使用大量依赖的情况下。
  • 版本兼容性:
    • 依赖冲突:由于 Spring Boot 使用了固定的依赖版本,可能会导致与其他库的版本冲突。
    • 升级困难:升级 Spring Boot 版本时,可能会遇到兼容性问题,需要仔细测试和调整配置。

(四)SpringBoot与微服务的区别

SpringBoot和微服务架构是两个不同的概念,但它们可以很好地结合使用。

  • Spring Boot:
    • 定义:SpringBoot是一个用于简化Spring应用开发的框架。
    • 功能:提供自动配置、嵌入式服务器、生产就绪功能等。
    • 适用场景:适用于各种规模的应用开发,包括单体应用和微服务。
  • 微服务架构:
    • 定义:微服务架构是一种设计风格,将应用拆分为一组小型、独立的服务,每个服务运行在自己的进程中,并通过轻量级的通信机制(如 HTTP)进行交互。
    • 功能:强调服务的独立部署、独立扩展和独立维护。
    • 适用场景:适用于大型、复杂的系统,需要高可用性和可扩展性的场景。
  • 结合使用SpringBoot和微服务
    • SpringBoot可以作为构建微服务的基础框架,提供简化开发和部署的功能。
    • SpringCloud提供了微服务相关的组件,如服务发现(Eureka)、配置中心(Config Server)、负载均衡(Ribbon)等,与SpringBoot结合使用可以构建完整的微服务架构。  

二、创建一个SpringBoot项目并打包运行

pom.xml: 

打包插件: 

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>11</source><target>11</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><!--打包:打包jar包 运行jar包必须有一个main方法,作为程序入口--><!--仅仅影响打包,不影响日常开发--><mainClass>com.javasm.bootdemo.Bootdemo1Application</mainClass><!--<skip>true</skip>--></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>

打包: 

CMD窗口运行:

java -jar bootdemo-0.0.1-SNAPSHOT.jar

三、@ConfigurationProperties

        @ConfigurationProperties是SpringBoot提供的一个注解,用于将配置文件中的属性绑定到 Java对象中。这使得配置管理更加方便和类型安全。 

// DogProperties类如果已经添加到容器中,则不需要再添加@EnableConfigurationProperties注解
// @EnableConfigurationProperties(DogProperties.class)
@SpringBootApplication
public class BootdemoApplication {public static void main(String[] args) {SpringApplication.run(BootdemoApplication.class, args);}
}
@ConfigurationProperties(prefix = "dog")
@Component
@Data
public class DogProperties {private String name;private Integer age;private String color;
}

application.properties: 

# 应用服务 WEB 访问端口
server.port=8080dog.name=gigi
dog.age=3
dog.color=black

测试类: 

@SpringBootTest
class BootdemoApplicationTests {@AutowiredDogProperties dogProperties;@Testvoid contextLoads() {System.out.println(dogProperties);}
}

运行结果:

DogProperties(name=gigi, age=3, color=black)

        日常开发的时候,很少用到读取配置文件中的自定义属性。当你要配置一个新的框架(SpringBoot官方没有自动配置,框架本身也没有对SpringBoot自动配置)。配置好了框架之后,需要给框架源码的配置赋值的时候才需要读取配置文件中的值。 

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
</dependency>

四、配置文件:properties与yml

        在properties文件中写中文会乱码,推荐使用yml文件,properties的优先级比yml高,出现相同配置,优先使用properties文件中的配置。

@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person {private String name;private Integer age;private String birthDay;private Boolean like;private Child child;private List<Dog> dogs;private Map<String,Cat> cats;
}@Data
class Child{private String name;private Integer age;private String birthDay;private List<String> text;
}@Data
class Cat{private String name;private Integer age;
}@Data
class Dog{private String name;private Integer age;
}

application.yml文件: 

spring:application:name: bootdemodog:name: hahaage: 10color: whiteperson:name: 张三age: 10birthDay: 2019-01-01like: truechild:name: 李四age: 12birthDay: 2020-01-01text: # 或者写在[]里面  ["aa","bb","cc"]- hello- worlddogs:- name: dog1age: 1- name: dog2age: 3- { name: dog3, age: 4 }cats:- bluecat:name: 蓝猫age: 1- whitecat:name: 白猫age: 2- blackcat: { name: 黑猫, age: 3 }

五、banner的配置

在resource文件夹下,新建banner.txt 或者 banner.jpg

https://www.bootschool.net/ascii

六、启动Spring应用的其他方式

@SpringBootApplication
public class BootdemoApplication {public static void main(String[] args) {// 应用启动SpringApplication.run(BootdemoApplication.class, args);/*** 下面两种启动方式可以在启动前配置一些参数* */// 链式调用/*SpringApplicationBuilder builder = new SpringApplicationBuilder();builder.sources(BootdemoApplication.class).bannerMode(Banner.Mode.OFF).environment(null).run(args);*//* SpringApplication application = new SpringApplication(BootdemoApplication.class);application.setBannerMode(Banner.Mode.OFF);
//        application.setListeners();application.setEnvironment(null);application.run(args);*/}
}

七、日志系统

(一)日志格式

规定:项目开发不要写System.out.println();用日志记录信息

@Slf4j
@SpringBootTest
public class LogTest {@Testpublic void test() {System.out.println("====================");log.error("错误日志");log.warn("警告日志");log.trace("追踪日志");log.debug("调试日志");log.info("信息日志");}
}2024-12-27 20:57:20.930 ERROR 15588 --- [           main] com.javatest.springboot01Demo.LogTest    : 错误日志
2024-12-27 20:57:20.931  WARN 15588 --- [           main] com.javatest.springboot01Demo.LogTest    : 警告日志
2024-12-27 20:57:20.931  INFO 15588 --- [           main] com.javatest.springboot01Demo.LogTest    : 信息日志

(二)日志分组 

# 调整日志级别 root表示根级别,默认为info,可以不写
# 如果哪个包、哪个类不说明日志级别,则默认为root的级别
# root后可选参数:trace, debug, info, warn, error, fatal, off
logging.level.root=debug
# 指定包打印日志级别
logging.level.com.javatest.boot=info
# 设置日志组
logging.group.biz=com.javatest.boot.service,com.javatest.boot.dao
# 整组批量设置日志级别
logging.level.biz=debug

(三)日志输出文件输出

# 指定日志输出文件
logging.file.name=springboot-01-demo/boot.log
# 指定日志输出路径
#logging.file.path=D://logs

(四)文件归档与滚动切割

(五)自定义配置 

八、全局异常处理

JavaTestExceptionEnum: 

@AllArgsConstructor
@Getter
// 默认情况下,json把enum转成json字符串的时候,是"ParameterNull",需要加上注解
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum JavaTestExceptionEnum {ParameterNull(1001,"参数为空");private Integer code;private String msg;
}

JavaTestException: 

@Getter
public class JavaTestException extends RuntimeException {JavaTestExceptionEnum javaTestExceptionEnum;public JavaTestException(JavaTestExceptionEnum javaTestExceptionEnum) {super(javaTestExceptionEnum.getMsg());this.javaTestExceptionEnum = javaTestExceptionEnum;}
}

JavaTestExceptionAdvice:

@RestControllerAdvice
public class JavaTestExceptionAdvice {// 处理自定义异常@ExceptionHandler(JavaTestException.class)public ResponseEntity<JavaTestExceptionEnum> f1(JavaTestException e) {JavaTestExceptionEnum exceptionEnum = e.getJavaTestExceptionEnum();return ResponseEntity.status(HttpStatus.OK).body(exceptionEnum);}//这样写也可以public JavaTestExceptionEnum f2(JavaTestException e){return e.getJavaTestExceptionEnum();}
}

测试:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class R {private Integer code;private String msg;private Object data;public R(Integer code, String msg) {this.code = code;this.msg = msg;}public static R ok() {return new R(200, "success");}public static R ok(Object data) {return new R(200, "success", data);}
}
@Data
public class Company {private String name;private String address;
}@Component
@Data
@ConfigurationProperties(prefix = "javatest")
public class TestModel {private String name;private String password;private Integer age;private List<String> nameList;private Map<Integer, String> tmap;private String[] arr1;private Date ctime;private Company company;
}
@RestController
@RequestMapping("/book")
public class BookController {@GetMapping("/f1")public R f1(Integer id) {if (id == null) {//自定义异常//只有在 抛出异常的代码,才知道这个是什么异常//throw new JavaTestException("1000","参数不合法");throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}return R.ok(id);}@ResourceTestModel testModel;@GetMapping("/f2")public R f2(Integer id) {if (id == null) {throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}return R.ok(testModel);}
}

正常数据处理: 

异常数据处理:

九、SpringBoot与Mybatis整合

(一)基础配置

1.导入依赖 

<!--Mysql 驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
<!--Druid数据库连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>
<!--mybatis,去掉默认的连接池HikariCP-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version><exclusions><exclusion><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></exclusion></exclusions>
</dependency>

2.修改配置文件application.yml

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: rootdruid:initial-size: 5max-active: 100min-idle: 10
mybatis:mapper-locations: classpath:mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.修改启动类

@SpringBootApplication
// 扫描的是dao的路径,mybatis中,把dao称为mapper
@MapperScan("com.javatest.bootdemo.*.dao")
public class Bootdemo1Application {public static void main(String[] args) {SpringApplication.run(Bootdemo1Application.class, args);}
}

4.生成mvc各层代码

5.CRUD实现

Controller:

@RestController
@RequestMapping("/user/info")
public class WebUserInfoController {/*** 服务对象*/@Resourceprivate WebUserInfoService userInfoService;@GetMapping("/query/{id}")public R queryById(@PathVariable Integer id) {if (id == null) {throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}return R.ok(userInfoService.queryById(id));}@PostMapping("/save")public R save(@RequestBody WebUserInfo webUserInfo) {if (webUserInfo == null) {throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}userInfoService.insert(webUserInfo);return R.ok();}@PutMapping("/update")public R update(@RequestBody WebUserInfo webUserInfo) {if (webUserInfo == null && webUserInfo.getUid() == null) {throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}return R.ok(userInfoService.update(webUserInfo));}@DeleteMapping("/{uid}")public R deleteById(@PathVariable Integer uid) {userInfoService.deleteById(uid);return R.ok();}
}

WebUserInfoService: 

public interface WebUserInfoService {/*** 通过ID查询单条数据** @param uid 主键* @return 实例对象*/WebUserInfo queryById(Integer uid);/*** 新增数据** @param webUserInfo 实例对象* @return 实例对象*/WebUserInfo insert(WebUserInfo webUserInfo);/*** 修改数据** @param webUserInfo 实例对象* @return 实例对象*/WebUserInfo update(WebUserInfo webUserInfo);/*** 通过主键删除数据** @param uid 主键* @return 是否成功*/void deleteById(Integer uid);
}

WebUserInfoServiceImpl: 

@Service("webUserInfoService")
public class WebUserInfoServiceImpl implements WebUserInfoService {@Resourceprivate WebUserInfoDao webUserInfoDao;/*** 通过ID查询单条数据** @param uid 主键* @return 实例对象*/@Overridepublic WebUserInfo queryById(Integer uid) {return this.webUserInfoDao.queryById(uid);}/*** 新增数据** @param webUserInfo 实例对象* @return 实例对象*/@Overridepublic WebUserInfo insert(WebUserInfo webUserInfo) {this.webUserInfoDao.insert(webUserInfo);return webUserInfo;}/*** 修改数据** @param webUserInfo 实例对象* @return 实例对象*/@Overridepublic WebUserInfo update(WebUserInfo webUserInfo) {this.webUserInfoDao.update(webUserInfo);return this.queryById(webUserInfo.getUid());}/*** 通过主键删除数据** @param uid 主键* @return 是否成功*/@Overridepublic void deleteById(Integer uid) {if (webUserInfoDao.deleteById(uid) <= 0) {throw new JavaTestException(JavaTestExceptionEnum.DeleteError);}webUserInfoDao.deleteById(uid);}
}

(二)分页

1.导入依赖

<!--分页插件-->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.3.0</version>
</dependency>

2.代码实现 

        在不改变原有sql语句编码的情况下,在执行之前,先查询了总条数,然后在sql语句之后,追加了limit,拦截了数据库的请求,做了修改。

@GetMapping("/list")
public R list() {List<WebUserInfo> list = userInfoService.list();return R.ok(list);
}
@GetMapping("/listByPage")
public R listByPage(Integer pageNum, Integer pageSize) {PageInfo<WebUserInfo> pageInfo = userInfoService.listByPage(pageNum, pageSize);return R.ok(pageInfo);
}

3.PageInfo属性

private int pageNum;//当前页码
private int pageSize;//设置每页多少条数据
private int size;//当前页有多少条数据
private int startRow;//当前页码第一条数据的
private int endRow;//当前页码的开始条
private int pages;//当前页码结束条
private int prePage;//上一页(页面链接使用)
private int nextPage;//下一页(页面链接使用)
private boolean isFirstPage;//是否为第一页
private boolean isLastPage;//是否为最后一页
private boolean hasPreviousPage;//是否有前一页
private boolean hasNextPage;//是否有下一页
private int navigatePages;//导航页码数(就是总共有多少页)
private int[] navigatepageNums;//导航页码数(就是总共有多少页),可以用来遍历
private int navigateFirstPage;//首页号
private int navigateLastPage;//尾页号

十、SpringBoot中定义拦截器

1.拦截器

@Component
public class LoginInterceptors implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 被访问的方法,执行之前,执行当前的方法,返回true表示放行,返回false表示拦截// 登录拦截器:已登录的用户可以访问,未登录的不能访问// 通过session判断是否登录HttpSession session = request.getSession();// 获取session中保存的用户信息Object user = session.getAttribute("user");if (user == null) {throw new JavaTestException(JavaTestExceptionEnum.PermissionDenied);}// 代码执行到这里,说明没有抛出异常,放行return true;}
}
@Component
public class JavaTestWebConfiguration implements WebMvcConfigurer {@Resourceprivate LoginInterceptors loginInterceptors;@Override // 添加拦截器public void addInterceptors(InterceptorRegistry registry) {// 注册一个拦截器registry.addInterceptor(loginInterceptors)// 拦截所有的页面.addPathPatterns("/**")// 排除不拦截的路径.excludePathPatterns("/login/**");}
}
@RestController
@RequestMapping("/login")
public class LoginController {@Resourceprivate LoginService loginService;@PostMapping("/doLogin")public R doLogin(String uname, String pwd) {if (StringUtils.isEmpty(uname) || StringUtils.isEmpty(pwd)) {throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}// 业务逻辑:已知用户名和密码,要根据用户名密码查询用户信息// 如果查询不到,说明用户名和密码错误WebUser webUser = loginService.loadByUname(uname, pwd);return R.ok(webUser);}
}public interface LoginService {WebUser loadByUname(String uname, String pwd);
}@Service
public class LoginServiceImpl implements LoginService {@Resourceprivate WebUserService webUserService;@Resourceprivate HttpSession session;@Overridepublic WebUser loadByUname(String uname, String pwd) {// 根据用户名查询用户信息WebUser webUser = webUserService.queryByUname(uname);if (webUser == null) {throw new JavaTestException(JavaTestExceptionEnum.UserNotExist);}if (!webUser.getPassword().equals(pwd)) {throw new JavaTestException(JavaTestExceptionEnum.PassWordError);}// 登录成功,登录信息要存入session中session.setAttribute("user", webUser);return webUser;}
}WebUserService:
WebUser queryByUname(String uname);WebUserServiceImpl:
@Override
public WebUser queryByUname(String uname) {WebUser webUser = webUserDao.selectByUname(uname);if (webUser != null) {WebUserInfo userInfo = webUserInfoService.queryById(webUser.getUid());webUser.setWebUserInfo(userInfo);}return webUser;
}WebUserDao:
WebUser selectByUname(String uname);WebUserDao.xml:
<select id="selectByUname" resultMap="WebUserMap">selectuid, username, password, status, email, phone, reg_timefrom web_userwhere username = #{username}
</select>

2.自定义注解+拦截器

案例:

  • 只拦截需要保密的接口

  • 正常的接口都可以未登录访问

  • 哪个接口需要登录才能访问,就在这个接口上加自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoginAuth {
}@Component
public class NewLoginInterceptors implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();LoginAuth loginAuth = method.getAnnotation(LoginAuth.class);if (loginAuth == null) {return true;}// 有@LoginAuth注解,必须登录之后才能访问HttpSession session = request.getSession();Object user = session.getAttribute("user");if (user == null) {throw new JavaTestException(JavaTestExceptionEnum.PermissionDenied);}return true;}
}@Component
public class JavaTestWebConfiguration implements WebMvcConfigurer {@Resourceprivate LoginInterceptors loginInterceptors;@Resourceprivate NewLoginInterceptors newLoginInterceptors;@Override // 添加拦截器public void addInterceptors(InterceptorRegistry registry) {// 注册一个拦截器/*registry.addInterceptor(loginInterceptors)// 拦截所有的页面.addPathPatterns("/**")// 排除不拦截的路径.excludePathPatterns("/login/**");*/registry.addInterceptor(newLoginInterceptors).addPathPatterns("/**").excludePathPatterns("/login/**");}
}

此时,只有添加了@LoginAuth的注解才会校验是否登录过: 

@GetMapping("/query/{id}")
@LoginAuth
public R queryById(@PathVariable Integer id) {if (id == null) {throw new JavaTestException(JavaTestExceptionEnum.ParameterNull);}return R.ok(userInfoService.queryById(id));
}

如果项目中没有使用权限框架,可以使用这种方式,简单地控制权限;

否则就要在拦截器中一个个指定排除和要拦截的接口路径

十一、AOP记录日志

1.引入依赖

<!--aop:-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.使用AOP记录用户操作行为

2.1开启AOP
@SpringBootApplication
//扫描的是dao的路径,mybatis中,把dao称为mapper
@MapperScan("com.javasm.bootdemo.*.dao")
@EnableAspectJAutoProxy//开启AOP
public class Bootdemo1Application {public static void main(String[] args) {SpringApplication.run(Bootdemo1Application.class, args);}
}
2.2自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SaveLog {
}
2.3创建记录日志的表和MVC 
create table if not exists testdb.system_log
(id          int auto_increment primary key,uid         int          null,class_name  varchar(255) null,method_name varchar(255) null,args        varchar(255) null,ctime       datetime     null
);

2.4创建日志切片
@Aspect
@Component
public class LogAspect {@Resourceprivate HttpSession session;@Resourceprivate SystemLogService systemLogService;@Around("@annotation(com.javatest.bootdemo.common.interfaces.SaveLog)")public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {Object proceed = joinPoint.proceed();// 获取用户信息Object user = session.getAttribute("user");Integer uid = -1;if (user != null) {WebUser webUser = (WebUser) user;uid = webUser.getUid();}// 获取类名String className = joinPoint.getTarget().getClass().getName();// 方法名String methodName = joinPoint.getSignature().getName();// 参数Object[] args = joinPoint.getArgs();String argsString = Arrays.toString(args);//组装参数SystemLog systemLog = new SystemLog(uid, className, methodName, argsString);systemLogService.insert(systemLog);return proceed;}
}
2.5在需要的方法上添加注解 

2.6测试 

十二、事务

启动类上开启事务: 

@EnableTransactionManagement//开启事务

在需要开启事务的方法上添加@Transactional注解

@Override
@Transactional
public WebUser doRegister(RegisterVO registerVO) {// 添加用户信息WebUser webUser = registerVO.getWebUser();webUserService.insert(webUser);// 获取添加成功的uidInteger uid = webUser.getUid();WebUserInfo webUserInfo = new WebUserInfo();webUserInfo.setUid(uid);webUserInfo.setNickname(registerVO.getNickname());// 保存用户详情信息webUserInfoService.insert(webUserInfo);webUser.setWebUserInfo(webUserInfo);return webUser;
}

事务是否生效,完全看方法是否向spring抛出异常

  • 在调用的一方,添加@Transactional,事务生效

  • 在被调用的一方,事务不会生效

  • 使用try...catch捕获异常,并打印,会导致事务失效

Spring的事务机制,是看调用的方法,是否抛出异常;

不要在每一个方法上都加@Transactional,只有多表调用的时候,才建议使用事务。

十三、跨域

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;@Configuration
public class CorsConfig {@Beanpublic CorsFilter corsFilter(){//预先的配置UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();//所有的请求,都要过滤,都添加跨域source.registerCorsConfiguration("/**",buildConfig());return new CorsFilter(source);}private CorsConfiguration buildConfig(){CorsConfiguration config = new CorsConfiguration();//配置 允许所有的作用域config.addAllowedOrigin("*");//头信息config.addAllowedHeader("*");//方法config.addAllowedMethod("*");//cookie,session会失效config.setAllowCredentials(true);//有效期config.setMaxAge(3600L);return config;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

本地小主机安装HomeAssistant开源智能家居平台打造个人AI管家

文章目录 前言1. 添加镜像源2. 部署HomeAssistant3. HA系统初始化配置4. HA系统添加智能设备4.1 添加已发现的设备4.2 添加HACS插件安装设备 5. 安装cpolar内网穿透5.1 配置HA公网地址 6. 配置固定公网地址 前言 大家好&#xff01;今天我要向大家展示如何将一台迷你的香橙派Z…

streamlit、shiny、gradio、fastapi四个web APP平台体验

streamlit、shiny、gradio、fastapi四个web APP平台体验 经常被问的问题就是&#xff1a;web APP平台哪个好&#xff1f;该用哪个&#xff1f;刚开始只有用streamlit和shiny&#xff0c;最近体验了一下gradio和fastapi&#xff0c;今天根据自己的体会尝试着回答一下。 使用R语…

Presto-简单了解-230403

presto是什么了解一下&#xff1a; 秒级查询引擎&#xff08;不做存储&#xff09;&#xff0c;GB-PB级不依赖于yarn&#xff0c;有自己的资源管理和执行计划支持多种数据源&#xff1a;hive、redis、kafka presto架构 presto优缺点 presto优点 内存到内存的传输&#xff0…

VScode 格式化代码空格记录

点击 -> “文件” -> “首选项" -> “设置” -> 按下图操作&#xff1a; 怎么格式化代码空格&#xff0c;先看下&#xff1a; 保存代码后&#xff0c;这代码自动格式化发&#xff0c;如下图&#xff1a; 你可以试试看就即可

HTML5 开关(Toggle Switch)详细讲解

HTML5 开关&#xff08;Toggle Switch&#xff09;详细讲解 1. 任务概述 开关&#xff08;Toggle Switch&#xff09;是一种用于表示二元状态&#xff08;如开/关&#xff09;的用户界面控件。用户可以通过点击开关来切换状态&#xff0c;常见于设置选项、开关功能等场景。 2…

Python中PDF转Word的技术

Python PDF转Word技术概述 在日常办公和数据处理中&#xff0c;经常需要将PDF文档转换为Word文档&#xff0c;以便进行编辑、修改或格式调整。Python作为一种强大的编程语言&#xff0c;提供了多种库和工具来实现这一功能。以下是对Python中PDF转Word技术的详细介绍。 一、技…

RabbitMQ中的异步Confirm模式:提升消息可靠性的利器

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;扮演着至关重要的角色&#xff0c;它能够解耦系统组件、提高系统的可扩展性和可靠性。RabbitMQ作为一款广泛使用的消息队列中间件&#xff0c;提供了多种机制来确保消息的可靠传递。其中&#xff…

【深度学习】多目标融合算法—样本Loss提权

目录 一、引言 二、样本Loss提权 2.1 技术原理 2.2 技术优缺点 三、总结 一、引言 在朴素的深度学习ctr预估模型中&#xff08;如DNN&#xff09;&#xff0c;通常以一个行为为预估目标&#xff0c;比如通过ctr预估点击率。但实际推荐系统业务场景中&#xff0c;更多是多…

如何在谷歌浏览器中创建安全的密码

在数字化时代&#xff0c;网络安全变得日益重要。谷歌浏览器提供了多种工具和功能帮助用户创建和管理强密码&#xff0c;确保在线账户的安全。本文将简要介绍几种方法&#xff0c;帮助您在谷歌浏览器中创建和管理安全密码。 一、启用自动填充功能 确认密码保存功能已开启&…

一份完整的营销策划包含哪些内容?营销策划主要内容和流程--中小企实战运营和营销工作室博客

一份完整的营销策划包含哪些内容&#xff1f;营销策划主要内容和流程–中小企实战运营和营销工作室博客 在当今竞争激烈的市场环境中&#xff0c;营销策划成为企业取得成功的关键。一份完整的营销策划是企业实现市场目标的重要工具&#xff0c;它涵盖了多个方面的内容&#xff…

vscode-QT环境配置

vscode-QT环境配置 参考链接&#xff1a;https://www.cnblogs.com/RioTian/p/18281114 一、 背景 已经安装了QT软件&#xff0c;电脑里有了QT Creater 12.0。使用QT生成并运行了一个project在这个project的基础上&#xff0c;直接配置vscode的环境 二、环境配置 确认QT工程成…

[2025] 如何在 Windows 计算机上轻松越狱 IOS 设备

笔记 1. 首次启动越狱工具时&#xff0c;会提示您安装驱动程序。单击“是”确认安装&#xff0c;然后再次运行越狱工具。 2. 对于Apple 6s-7P和iPad系列&#xff08;iOS14.4及以上&#xff09;&#xff0c;您应该点击“Optinos”并勾选“允许未经测试的iOS/iPadOS/tvOS版本”&…

ARM64 Windows 10 IoT工控主板运行x86程序效率测试

ARM上的 Windows 10 IoT 企业版支持仿真 x86 应用程序&#xff0c;而 ARM上的 Windows 11 IoT 企业版则支持仿真 x86 和 x64 应用程序。英创推出的名片尺寸ARM64工控主板ESM8400&#xff0c;可预装正版Windows 10 IoT企业版操作系统&#xff0c;x86程序可无需修改而直接在ESM84…

万里数据库GreatSQL监控解析

GreatSQL是MySQL的一个分支&#xff0c;专注于提升MGR&#xff08;MySQL Group Replication&#xff09;的可靠性及性能。乐维监控平台可以有效地监控GreatSQL&#xff0c;帮助用户及时发现并解决潜在的性能问题。 通过在GreatSQL服务器上安装监控代理&#xff0c;收集数据库性…

【贪心算法】贪心算法七

贪心算法七 1.整数替换2.俄罗斯套娃信封问题3.可被三整除的最大和4.距离相等的条形码5.重构字符串 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f…

四年匠心磨砺,快手系统软件技术创新与领域演进之路

一、系统软件技术的核心价值与面临挑战 系统软件作为软件架构的基石&#xff0c;扮演着连接软件与硬件的桥梁角色&#xff0c;位于整个软件生态的最底层&#xff0c;处于关键核心的位置。系统软件最为显著的特征在于其规模效应&#xff0c;随着服务器体量的增加&#xff0c;系…

使用JMeter对Linux生产服务器进行压力测试

安装 JMeter wget https://downloads.apache.org/jmeter/binaries/apache-jmeter-5.4.1.tgz tar -xzf apache-jmeter-5.4.1.tgz cd apache-jmeter-5.4.1创建 JMeter 脚本 设置中文 选择Options—>Choose Language—>选择其他语言&#xff08;例如&#xff1a;Chinese&am…

Nginx1.20.2-Linux-安装

文章目录 1.下载压缩包1.官网下载2.找到1.20.23.百度网盘 2.Linux安装1.搭建gcc环境2.上传到 /usr/local/nginx1.20.23.解压1.解压到当前目录2.删除压缩包 4.配置Nginx的编译路径1.进入nginx-1.20.22.执行内部的脚本&#xff0c;指定编译路径为/usr/local/nginx 5.编译并安装6.…

常用的linux命令介绍

Linux是一个强大的操作系统&#xff0c;它提供了许多命令行工具来帮助用户管理文件和目录、监控系统性能、以及执行各种系统管理任务。下面是一些常用的Linux命令&#xff0c;我会用简单的语言来解释它们的作用&#xff1a; 1. ls • 作用&#xff1a;列出目录内容。 • 比喻&a…

linux--编译驱动模块【虚拟网卡 tun】

linux--编译驱动模块【虚拟网卡 tun】 1 介绍2 操作2.1 源码 linux-5.10.1602.2 安装控制台应用程序依赖库&#xff0c;其他库2.3 普通用户模式操作2.4 然后配置需要编译的模块2.5 关闭 preempt2.6 开启 bpf【未成功&#xff0c;放弃】2.7 编译模块报错处理一&#xff1a;缺少证…