一、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;}
}