. Spring 是一个“引擎”;
. Spring MVC 是基于Spring的一个 MVC 框架;
. Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。
整理自:
https://mp.weixin.qq.com/s/nOx6iRfhPWWl2vKTSQ52Hg
https://mp.weixin.qq.com/s/ztM0VRgSLUZMK4sZ4rVFwA
https://mp.weixin.qq.com/s/OaMaT-QF20P65tKOjC5_oA
Spring Boot 自动配置
- IOC
- MVC
- mybatis
- mybatis plus
注解
- 注解(annotations)列表
- 注解(annotations)详解
- JPA注解
- springMVC相关注解
- 全局异常处理
- mybatis-plus常用注解
- @Transactional
Spring Boot 怎么做自动配置的?
从代码里看项目SpringBoot的项目启动类只有一个注解@SpringBootApplication和一个run方法。
@SpringBootApplicationpublic
class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}}
@SpringBootApplication:包含了@SpringBootConfiguration(打开是@Configuration),@EnableAutoConfiguration,@ComponentScan注解。
- @Configuration 等同于spring的XML配置文件;使用Java代码可以检查类型安全。
- @EnableAutoConfiguration 自动配置。
- @ComponentScan 让spring Boot扫描到Configuration类并把它加入到程序上下文,可自动发现和装配一些Bean。
1. @Configuration
用一个过滤器举例,JavaConfig的配置方式是这样
@Configurationpublic
class DruidConfiguration {
@Bean
public FilterRegistrationBean statFilter(){
//创建过滤器
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//设置过滤器过滤路径
filterRegistrationBean.addUrlPatterns("/*");
//忽略过滤的形式
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}}
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
2. @ComponentScan
@ComponentScan对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在rootpackage下,因为默认不指定basePackages。
3. @EnableAutoConfiguration
借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。该配置模块的主要使用到了SpringFactoriesLoader
。
SpringFactoriesLoader
: 类加载器,spring.factories文件,文件中对应的类则是接口的实现类
AutoConfigurationImportSelector
: ImportSelector
在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表,类信息加载到 JVM。
4. IOC
理论:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护。
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
目的:创建对象并且组装对象之间的关系
Bean
和Application容器
:
●org.springframework.beans : BeanFactroy提供配置结构和基本功能,加载并初始化Bean
●org.springframework.context :ApplicationContext保存了Bean对象并在Spring中被广泛使用
●标签: @Component、@Repository、@Service、@Controller、@Autowired
●注入方式:属性注入、构造器注入
●初始化方式:文件、Classpath,Web应用
5. MVC
主要组件:前端控制器DispatcherServlet、处理器映射器HandlerMapping、处理器适配器HandlerAdapter、处理器Handler(需要工程师开发)、视图解析器View resolver(jstlView、freemarkerView、pdfView)
6. MyBatis
Maven:依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能
MyBatis 支持定制化 SQL、存储过程以及高级映射:
●MyBatis使用SqlSessionFactoryBuilder来完成连接
●MyBatis可以将SQL代码写入xml中,使用简单的 XML 或注解来配置和映射原生信息
●MyBatis的mapper会自动将执行后的结果映射到对应的Java对象中,映射成数据库中的记录
组件:SqlSessionFactoryBuilder
连接数据库、SqlSessionFactory
数据源、SqlSession
增删改查方法
缺点: SQL 工作量大
7. mybatis plus
特性:
●无侵入:Mybatis 的增强工具,只做增强不做改变;
●依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring,
●损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
●预防Sql注入:内置 Sql 注入剥离器,有效预防Sql注入攻击
●通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
●多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题
●支持热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
●支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作Active Record 是一种数据访问设计模式,它可以帮助你实现数据对象Object到关系数据库的映射。
●支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!)
●支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
●支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
●内置分页插件:基于 Mybatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List查询
●内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询
●内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作
注解
1、注解(annotations)列表
@SpringBootApplication:
包含了@ComponentScan、@Configuration和@EnableAutoConfiguration注解。
其中@ComponentScan让spring Boot扫描到Configuration类并把它加入到程序上下文。@Component 可配合CommandLineRunner使用,在程序启动后执行一些基础任务。
@RestController注解是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器。
@Autowired自动导入。
@PathVariable获取参数。
@JsonBackReference解决嵌套外链问题。
@RepositoryRestResourcepublic配合spring-boot-starter-data-rest使用。
2、注解(annotations)详解
- @SpringBootApplication:申明让spring boot自动给程序进行必要的配置,这个配置等同于:@Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三个配置。
package com.example.myproject;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
-
@ResponseBody:表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。
在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。
比如异步获取json数据,加上@responsebody后,会直接返回json数据。
该注解一般会配合@RequestMapping一起使用。示例代码:
@ResponseBody
public String test(){
return”ok”;
}
-
@Controller:用于定义控制器类,在spring 项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层)
一般这个注解在类中,通常方法需要配合注解@RequestMapping。
示例代码:
@Controller
@RequestMapping(“/demoInfo”)
publicclass DemoController {
@Autowired
private DemoInfoService demoInfoService;
@RequestMapping("/hello")
public String hello(Map map){
System.out.println("DemoController.hello()");
map.put("hello","from TemplateController.helloHtml");
//会使用hello.html或者hello.ftl模板进行渲染显示.
return"/hello";
}
}
-
@RestController:用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。
示例代码:
package com.kfit.demo.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/demoInfo2”)
publicclass DemoController2 {
@RequestMapping("/test")
public String test(){
return"ok";
}
}
- @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。
-
@EnableAutoConfiguration:Spring Boot自动配置(auto-configuration):尝试根据你添加的jar依赖自动配置你的Spring应用。
例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连接beans,那么我们将自动配置一个内存型(in-memory)数据库”。
你可以将@EnableAutoConfiguration或者@SpringBootApplication注解添加到一个@Configuration类上来选择自动配置。
如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration注解的排除属性来禁用它们。 -
@ComponentScan:表示将该类自动发现扫描组件。
个人理解相当于,如果扫描到有@Component、@Controller、@Service等这些注解的类,并注册为Bean,可以自动收集所有的Spring组件,包括@Configuration类。
我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。可以自动收集所有的Spring组件,包括@Configuration类。
如果没有配置的话,Spring Boot会扫描启动类所在包下以及子包下的使用了@Service,@Repository等注解的类。 - @Configuration:相当于传统的xml配置文件,如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
- @Import:用来导入其他配置类。
- @ImportResource:用来加载xml配置文件。
- @Autowired:自动导入依赖的bean
- @Service:一般用于修饰service层的组件
- @Repository:使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
- @Bean:用@Bean标注方法等价于XML中配置的bean。
- @Value:注入Spring boot application.properties配置的属性的值。示例代码:
@Value(value = “#{message}”)
private String message;
- @Inject:等价于默认的@Autowired,只是没有required属性;
- @Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
- @Bean:相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
- @AutoWired:自动导入依赖的bean。byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
- @Qualifier:当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用。@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:
@Autowired
@Qualifier(value = “demoInfoService”)
private DemoInfoService demoInfoService;
- @Resource(name=”name”,type=”type”):没有括号内内容的话,默认byName。与@Autowired干类似的事。
3、JPA注解
- @Entity:@Table(name=”“):表明这是一个实体类。一般用于jpa这两个注解一般一块使用,但是如果表名和实体类名相同的话,@Table可以省略
- @MappedSuperClass:用在确定是父类的entity上。父类的属性子类可以继承。
- @NoRepositoryBean:一般用作父类的repository,有这个注解,spring不会去实例化该repository。
- @Column:如果字段名与列名相同,则可以省略。
- @Id:表示该属性为主键。
- @GeneratedValue(strategy=GenerationType.SEQUENCE,generator= “repair_seq”):表示主键生成策略是sequence(可以为Auto、IDENTITY、native等,Auto表示可在多个数据库间切换),指定sequence的名字是repair_seq。
- @SequenceGeneretor(name = “repair_seq”, sequenceName = “seq_repair”, allocationSize = 1):name为sequence的名称,以便使用,sequenceName为数据库的sequence名称,两个名称可以一致。
-
@Transient:表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。
如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic。@Basic(fetch=FetchType.LAZY):标记可以指定实体属性的加载方式 - @JsonIgnore:作用是json序列化时将Java bean中的一些属性忽略掉,序列化和反序列化都受影响。
- @JoinColumn(name=”loginId”):一对一:本表中指向另一个表的外键。一对多:另一个表指向本表的外键。
- @OneToOne、@OneToMany、@ManyToOne:对应hibernate配置文件中的一对一,一对多,多对一。
4、springMVC相关注解
-
@RequestMapping:@RequestMapping(“/path”)表示该控制器处理所有“/path”的UR L请求。
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。
用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。该注解有六个属性:
params:指定request中必须包含某些参数值是,才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
value:指定请求的实际地址,指定的地址可以是URI Template 模式
method:指定请求的method类型, GET、POST、PUT、DELETE等
consumes:指定处理请求的提交内容类型(Content-Type),如application/json,text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回 - @RequestParam:用在方法的参数前面。
@RequestParam
String a =request.getParameter(“a”)。
@PathVariable:路径变量。如
RequestMapping(“user/get/mac/{macAddress}”)
public String getByMacAddress(@PathVariable String macAddress){
//do something;
}参数与大括号里的名字一样要相同。
5、mybatis-plus常用注解
- 表名注解@TableName
- 主键注解@Tabeld
- 乐观锁标记注解@Version
- 字段注解@TableField
- 序列主键策略注解@KeySequence
- 当数据库的表字段名是驼峰命名时无需注解处理
- 或者全局配置: 下划线命名 dbColumnUnderline 设置 true , 大写 isCapitalMode 设置 true
6、全局异常处理
- @ControllerAdvice:包含@Component。可以被扫描到。统一处理异常。
- @ExceptionHandler(Exception.class):用在方法上面表示遇到这个异常就执行以下方法。
7、@Transactional 注解六种失效场景
1. 事务
事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。
编程式事务:是指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强,如下示例:
try {
//TODO something
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new InvoiceApplyException("异常失败");
}
声明式事务:基于AOP面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。声明式事务也有两种实现方式,一是基于TX和AOP的xml配置文件方式,二种就是基于@Transactional注解了。
@Transactional
@GetMapping("/test")
public String test() {
int insert = cityInfoDictMapper.insert(cityInfoDict);
}
2. @Transactional介绍
1)、@Transactional注解可以作用于哪些地方?
@Transactional 可以作用在接口、类、类方法。
- 作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
- 作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
- 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效
@Transactional
@RestController
@RequestMapping
public class MybatisPlusController {
@Autowired
private CityInfoDictMapper cityInfoDictMapper;
@Transactional(rollbackFor = Exception.class)
@GetMapping("/test")
public String test() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setParentCityId(2);
cityInfoDict.setCityName("2");
cityInfoDict.setCityLevel("2");
cityInfoDict.setCityCode("2");
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert + "";
}
}
2)、@Transactional注有哪些属性?
propagation
代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:
- Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )
- Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
- Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
- Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )
- Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
- Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
- Propagation.NESTED :和 Propagation.REQUIRED 效果一样。
isolation
:事务的隔离级别,默认值为 Isolation.DEFAULT。
- Isolation.DEFAULT:使用底层数据库默认的隔离级别。
- Isolation.READ_UNCOMMITTED
- Isolation.READ_COMMITTED
- Isolation.REPEATABLE_READ
- Isolation.SERIALIZABLE
timeout
:事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly
:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor
:用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
noRollbackFor
:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
3. @Transactional失效场景
1). @Transactional 应用在非 public 修饰的方法上
因为在Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。
注意:protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。
2). @Transactional 注解属性 propagation 设置错误
这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
-
TransactionDefinition.PROPAGATION_SUPPORTS
:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 -
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
:以非事务方式运行,如果当前存在事务,则把当前事务挂起。 -
TransactionDefinition.PROPAGATION_NEVER
:以非事务方式运行,如果当前存在事务,则抛出异常。
3). @Transactional 注解属性 rollbackFor 设置错误
rollbackFor
可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException
的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。
4). 同一个类中方法调用,导致@Transactional失效
开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。
那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
5). 异常被你的 catch“吃了”导致@Transactional失效
如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务还能正常回滚吗?
答案:不能!
会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolle
因为当ServiceB中抛出了一个异常以后,ServiceB标识当前事务需要rollback。但是ServiceA中由于你手动的捕获这个异常并进行处理,ServiceA认为当前事务应该正常commit
。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException
异常。
spring的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行commit
or rollback
,事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception
并在你的业务方法中没有catch到的话,事务会回滚。
在业务方法中一般不需要catch异常,如果非要catch一定要抛出throw new RuntimeException(),或者注解中指定抛异常类型@Transactional(rollbackFor=Exception.class),否则会导致事务失效,数据commit造成数据不一致,所以有些时候try catch反倒会画蛇添足。
6). 数据库引擎不支持事务
这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。