目录
mybatis
Mybatis中${} 和#{}有什么区别?
Mybatis如何执行批量插入?
Mybatis如何在插⼊后获得主键 ?
Mybatis中resultmap是做什么的?
Mybatis中resulttype和resultmap区别?
mybatis都有哪些 Executor 执行器?它们之间的区别是什么?
mybatis⼀级缓存和二级缓存?
mybatis怎么调用【存储过程】?
动态sql的常⽤标签,动态sql的执⾏原理 ?
Mybatis是否支持延迟加载,延迟加载的原理是什么?
分页插件的原理是什么 & Mybatis如何编写一个自定义插件?
Spring
对IOC的理解
对Aop的理解
切⼊点、通知、连接点、切⾯是什么
springAOP代理方式有几种,怎么配置,实现原理是什么
spring常用的注解
@Autowied和@Qualifier,@Resource区别
BeanFactory和ApplicationContext有什么区别
spring BeanFactory和FactoryBean区别
简述spring bean的生命周期(简版)
spring bean生命周期,实现哪些接口可以自定义生命周期行为
spring支持的bean作用域
Spring框架中的单例Bean是线程安全的么
spring如何保证⾼并发下Controller线程安全
spring框架中使用了哪些设计模式及应用场景
spring事务的实现方式原理以及隔离级别
spring的事务传播机制
spring事务什么时候会失效。
spring事物配置方式有几种
ApplicationContext Refresh的过程
spring中的循环依赖是什么意思,怎么解决
SpringMVC
springmvc工作流程
springmvc的四大组件
springmvc常用的注解,每个注解的作⽤
springmvc参数绑定方式
springmvc如何即可以响应xml又可以响应json(早期)
使用springmvc如何完成文件上传
springmvc和spring父子容器关系
springmvc的controller线程安全吗?怎么解决 。
什么是restful风格
springmvc支持restful风格吗?有哪些注解
springmvc获得request,response,session的⼏种⽅式
注解方式springmvc使用的是那个HandlerMapping,HandlerAdapter
springmvc怎样设定重定向和转发 (了解)
springmvc怎么处理异常
springmvc拦截器怎么使用
拦截器和过滤器的区别
spring boot
springboot提供了哪些核心功能
springboot的常用注解
如何理解springboot的starter(自动装配原理)
springboot常用的starter有哪些
springboot的配置⽂件有哪几种格式
springboot有哪几种读取配置的⽅式?
springboot怎么管理不同环境配置文件
spring boot启动过程(有可能)
MyBatis 中的并发控制主要由数据库自身的两种锁来完成,分别是行级锁和表级锁。MyBatis 提供了以下两种行级锁的实现方式:
1. 乐观锁:在 MyBatis 的映射文件中,可以通过在 SQL 语句中使用 version 字段或其他版本控制字段来实现乐观锁。在更新或者删除操作时,如果当前版本号与数据库中的版本号不一致,则更新或者删除将失败,并返回 0 行受影响的记录。此时,我们可以通过重试机制来确保数据一致性。
2. 悲观锁:在 MyBatis 的映射文件中,可以通过 SQL 语句中是否使用 FOR UPDATE 语句来实现悲观锁。在查询操作时,如果被查询的记录被其他事务加锁,则当前事务将等待锁释放后才能进行查询操作。
表锁是在整个表上设置锁,与行锁不同,表锁是一种更加粗粒度的锁定方式。在 MyBatis 中,我们可以通过在 SELECT 语句中添加 FOR UPDATE 代码来实现表锁。在这种情况下,SELECT 语句会锁定整个表格并且一直等待,直到当前事务完成,其他事务才能开始执行。
总之,在完成 MyBatis 的 CRUD 操作时,针对数据并发控制,我们可以使用乐观锁和悲观锁,这些锁可以保证数据的一致性和正确性。通过表锁(表级锁)可以跨行,跨表地控制数据并发访问,优化操作性能。
#{}是预编译处理,Mybatis在处理#{}时,会将sql中的#{}替换成?,调用 PreparedStatement 的 set 方法来赋值;${}是字符串替换,Mybatis 在处理${}时,就是把${}替换成变量的值,使用#{}可以方式sql注入的问题,提高安全性;
1.批量插入的sql语句:insert into table (列名1,列名2...)values(值1,值2),(值1,值2),(值1,值2)...;2.MyBatis 层面批量插入数据到数据库(使用
在insert标签中插入三个属性:useGeneratedKeys="true" keyProperty="id" keyColumn="id"
插入完毕后在插入的对象中就可以获取。
1.在做查询的时候,表字段名和实体类属性名不一致时,可以进行字段名和属性名的映射;
2.如果要把多表查询的结果进行封装,也要用resultMap;
对查询结果进行自定义映射,主要可以解决2方面的问题:1:1和1:m查询、查询的结果集字段名称和类中属性名称不一致的问题。
如果表字段名和实体类属性名一致就用resultType,不一致就用resultMap;
resultMap不仅能解决实体类属性名和表名字段不一致的问题,还可以解决多表联查的结果封装。
如果查询结果只是返回一个简单类型,并且表字段名和实体类属性名一致,那么久使用resultType。
resultType主要针对的场景:如果查询的结果只是返回一个简单类型,比如String或int,那么可以使用resultType;如果数据库表中的字段名和实体对象的属性名一致时,那么也可以直接使用resultType直接返回结果。
resultmap主要针对的场景:1 : 1 / 1:m多表关联查询结果封装;如果数据库表中字段和实体类对象的属性名不一致时,可以使用resultMap,如果要使用resultType需要给表字段起别名。
SimpleExecutor:每执行一次update或者select,就开启一个statement对象,用完立刻关闭statement对象。
ReuseExecutor:执行update或者select,以sql作为key查找statement对象,存在就使用,不存在就创建,用完后不关闭statement对象,而是放在map内,供下一次使用,简言之就是重复使用statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个statement对象,每个statement对象都是addBatch()完毕后,等待注意执行executeBatch()批处理。与JDBC批处理相同。
为了提高查询效率,数据库中的数据存储在磁盘里,如果每一次查询数据都是在数据库里面查,这个效率就不是很高,所以为了提高查询效率,mybatis提供了数据缓存的支持,分为一级缓存和二级缓存。
一级缓存默认开启,不能关闭,一级缓存的缓存空间就在sqlsession的内部,可以理解为sqlsession中有一个map,把查询的sql语句和参数作为键,查询出来的数据作为值存放在当前sqlsession中,所以在不同的sqlsession中,缓存是无法共享的,除此之外还有以下三种失效常见:①相同的sqlsession中查询数据但查询的条件不同;②相同的sqlsession中查询数据但两次查询中执行了增删改的操作;③相同的sqlsession中查询数据,但在第二次查询前,程序调用了sqlsession的clearcache()方法手动清理了一级缓存。
在高版本中的二级缓存是默认开启的,也可以在全局配置文件中配置setting来打开或关闭二级缓存,要用该级缓存的话需要在每一个xml映射文件中添加
通过sqlsession查询的数据会先把数据放在一级缓存中,如果把当前sqlsession资源释放(sqlsession.close())了,它就会把一级缓存中的数据放进二级缓存中,以后新的sqlsession也可以从二级缓存中查找数据。(查询流程:先看一级缓存再看二级缓存)当使用任何sqlsession执行了增删改操作以后,二级缓存都会被清理。
(没啥用,缓存命中率并不是很高,一般我们会用专业的缓存技术,比如redis)
为了提高查询效率和用户体验,mybatis提供了数据缓存支持,依据数据缓存的有效范围默认定义了一级缓存和二级缓存;
一级缓存为sqlsession级别的缓存,默认开启不能关闭;在以下情况下会导致缓存失效:①在不同的sqlsession中查询数据;②相同的sqlsession查询数据但查询条件不同;③相同的sqlsession中查询数据但两次查询中执行了增删改的操作;④相同的sqlsession中查询数据,但在第二次查询前,程序调用了sqlsession的clearcache()方法手动清除了一级缓存。
二级缓存默认不开启,需要开启二级缓存的话需要在每一个XML映射文件中添加
二级缓存可以在全局配置文件中配置setting标签来关闭二级缓存;通过sqlsession查询的数据,这些数据将会被放到当前对话的一级缓存中,如果当前会话关闭,则会将一级缓存中的数据保存到二级缓存中,此后新的sqlsession将会从二级缓存中查找数据;
select标签的useCache属性用于是否使用二级缓存,默认是true;insert、update、delete和select标签均有flushCache属性,其中增删改默认为true;即执行sql后,会同时清空一级和二级缓存,查询默认为false。
1.在mysql中创建存储过程
2.在mybatis中调用
作用:在XML映射文件中,以标签的形式编写动态SQL
执行原理:根据表达式的值,完成逻辑判断,动态拼接SQL的功能
动态SQL标签:
-
-
-
-
-
支持,mybatis的延迟加载也被称为懒加载,是指在进行关联查询的时候按照设置的延迟规则推迟关联对象的select查询;mybatis的延迟加载需要借助resultMap标签中的association和collection子标签来实现。
延迟加载的规则分为【侵入式懒加载】和【非侵入式懒加载】;侵入式懒加载指的是在执行对主数据查询时,不会查询关联的对象,当访问主数据的某个属性时,就会马上执行关联对象的select查询。非侵入性懒加载指的是在执行对主数据查询时,不会查询关联的对象,当访问主数据的某个属性时,也不会查询关联的对象;只有真正访问关联对象的属性时,才会执行关联对象的select查询。
延迟加载的原理是:动态代理;通过延迟加载查询的对象是代理对象,并且对对象中的方法进行增强,当我们调用代理对象的方法的时候,发送sql查询关联对象。
mybatis使用rowBounds对象进行分页,也可以编写sql实现分页查询,或者使用mybatis的分页插件;
分页插件的原理:实现mybatis提供的接口,实现自定义插件,在插件的拦截方法内拦截执行的sql然后重写sql。
自定义插件分两步:1.新建类实现 Interceptor 接口,并指定想要拦截的方法签名。2.MyBatis 配置文件中添加该插件
/** * MyBatis 插件 */ @Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class ExamplePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { for (Object arg : invocation.getArgs()) { System.out.println("参数:" + arg); } System.out.println("方法:" + invocation.getMethod()); System.out.println("目标对象:" + invocation.getTarget()); Object result = invocation.proceed(); //只获取第一个数据 if (result instanceof List){ System.out.println("原集合数据:" + result); System.out.println("只获取第一个对象"); List list = (List)result; return Arrays.asList(list.get(0)); } return result; } }
控制反转,将对象的创建权交由spring的IOC容器,程序中如果需要用到对象,从spring的IOC容器中获取;可以实现类与类之间的解耦。
面向切面编程,在不修改源码的基础上,对类中的方法进行增强,可以实现业务之间的解耦,底层使用的是动态代理;
面向切面编程,可以在不修改源代码的基础上对目标对象中的方法进行增强,实现业务之间的解耦;底层使用的是动态代理;
连接点:目标对象中可以被增强的方法;
切入点:目标对象中真正要被增强的方法;
通知:要增强的功能
切面:将通知用到切入点的过程叫切面;描述通知和切入点对应的关系叫切面;
springAOP主要有两种代理方式:①基于JDK的动态代理(底层是反射);②基于CGlib的动态代理(底层是继承);
实现原理:JDK动态代理要求目标类至少实现一个接口,目标类如果没有实现任何接口,没法生成代理类,因为它生成的代理类和目标类实现的是同一个接口。CGlib要求目标类不是最终类,因为它生成的代理类是目标类的子类。
如果被代理的目标对象实现了至少一个接口,则会使用JDK的动态代理【基于接口的动态代理】;如果目标对象没有实现任何接口,则会创建CGlib动态代理【基于类的动态代理
Spring默认采取的动态代理机制实现AOP,当动态代理不可用时 (代理类无接口)会使用CGlib机制。
Spring AOP(面向切面编程)使用代理来创建切面。代理是控制对象访问的对象,它允许你在对象的方法执行前、执行后,与抛出异常后插入额外的逻辑。在Spring中,代理可以使用两种不同的方式来实现:
1. 基于JDK的动态代理:只能够对实现了至少一个接口的类进行代理。
2. 基于CGLIB的动态代理:可以代理未实现任何接口的类。
Spring会根据目标对象的实际情况来决定使用哪种代理方式。其中,默认情况下,如果目标对象实现了至少一个接口,Spring将会使用基于JDK的动态代理,否则,Spring将会使用基于CGLIB的动态代理。但是,你也可以通过配置来强制使用JDK动态代理或者CGLIB动态代理。
实现原理:
1. 基于JDK的动态代理:在运行时,通过Java Reflection实现,动态创建代理类及其实例,代理类是目标类的接口实现,通过实现InvocationHandler接口,控制对原始对象方法的访问。该方式仅支持对实现了至少一个接口的类进行代理。
2. 基于CGLIB的动态代理:使用CGLIB库在运行时动态地生成目标类的子类作为代理对象,代理对象继承了目标对象的所有非final的方法,并覆盖这些方法以提供横切逻辑的功能。该方式可以代理未实现任何接口的类。
Spring AOP的配置涉及多个组件,包括切面、切点、通知以及切面执行顺序等。下面是Spring AOP的配置流程:
1. 定义切面:声明一个Java类作为切面,其中包含了多个通知和切点等信息。
2. 定义切点:通过@Pointcut注解定义需要拦截的方法,即切点。
3. 定义通知:通过@Before、@After等注解定义切面所需要执行的增强操作。
4. 定义切面优先级:通过@Order注解定义多个切面的执行顺序。
5. 注册切面:将定义好的切面注册到Spring IoC Container中。
6. 开启AOP:通过@EnableAspectJAutoProxy注解开启AOP支持。
总之,Spring AOP主要使用基于JDK的动态代理和基于CGLIB的动态代理来实现对目标对象的代理,通过代理对目标对象的方法进行增强操作,实现了在业务方法执行前、执行后、抛出异常后等情况下插入额外的逻辑。针对不同的需求,开发者可以通过切面、切点、通知和切面顺序等配置来进行有效的AOP处理。
IOC相关的注解:
①创建Bean注解:@Component @Service @Repository @Controller
②依赖注入注解:@Autowired @Qualifier @Value
③作用范围注解:@Scope
④条件装配:@Conditional(符合一定的条件才会把该类的对象交给spring管理)
⑤配置类和组件扫描:@Configuration @Bean @ComponentScan @Import
⑥延迟加载:
@Lazy,标注在类上,表示延迟创建bean。第一次使用创建
@Lazy,标注在属性或者方法参数上,先给代理对象
⑦配置文件加载:@PropertySource
核心:@Order控制Bean的创建顺序
事务:
@EnableTransactionManager开启平台事务管理器
@Transactional
任务调度,异步:
@EnableShaduling @Schaduled
@EnbaleAync @Aync
切面注解:
@EnableAspectJAutoProxy
spring cache相关注解
@EnableCaching(配置类上,开启缓存)
(加在方法上)
@Cachable(先判断数据在缓存中有没有,如果有直接从缓存中获取,如果没有执行完方法并把方法的返回值存入缓存中)
@CacheEvict (当方法执行完毕后,根据某一个key把数据从缓存中删掉)
@CachePut(当执行完方法后将方法的返回值存入缓存)
Spring是一个基于IoC(控制反转)和AOP(面向切面编程)的框架,使用注解可以使开发者更加便捷地在Spring框架中操作和管理Java对象。以下是Spring常用的注解及其含义:
1. @Autowired:自动装配注解,Spring自动将对应的实现类注入到需要该实现的构造器,字段或者Setter方法中。
2. @Component:通用的Spring Bean注解,用于标记一个组件类Bean。
3. @Configurable:使使用AspectJ自动注入Spring Bean的类成为Spring管理的Bean,目的是使不受Spring管理的对象可以在其它Spring Bean中使用,并接收Spring的事务管理。
4. @Controller:MVC中的控制器组件,用于处理Web请求。
5. @Repository:DAO(数据访问对象)组件,用于数据库交互相关代码的实现。
6. @Service:服务层组件,用于实现业务逻辑。
7. @Transactional:声明式事务管理注解,用于标记在方法或类级别上,表示该方法或类中的所有操作都应该在一个事务内完成,同时也定义了事务的传播行为和隔离级别等信息。
8. @RequestMapping:用于映射HTTP请求和URL路径的注解,通常用于控制器类和方法上。
9. @PathVariable:用于从URL路径中获取参数的注解,常用于RequestMapping中。
10. @RequestBody:用于从HTTP请求体中获取参数的注解,常用于RESTful API。
11. @ResponseBody:用于将方法返回值序列化成HTTP响应体的注解,常用于RESTful API。
12. @Value:用于读取Spring配置文件中的配置信息,将其注入到对应的类属性中。
以上这些注解是Spring框架中比较常用的,当然还有其他的注解,如@Qualifier、@PropertySource等等。熟练掌握这些注解,可以有效地提升Spring应用的开发效率。
@Autowird是按照类型注入,@Qualifier是按照名称注入,需要配合@Autowird一起使用,他们两个是pring提供的注解;@Resource默认是按照类型注入,如果我们声明了name属性时,就会按照名称注入,效果就等同于@Autowired加@Qualifier。@Resource不是spring的注解,,全包名为javax.annotation.Resource,但是Spring支持该注解;
@Autowired按照类型注入,如果IOC容器中有多个bean的类型和注入的类型一致,会报错;使用@Autowired自动注入是会先判断你的bean是否有加属性autowireCandidate为false,优先排除这些bean;然后在排出后的bean中看哪个bean加了@Primary,优先使用该bean; 最后将属性的名称作为bean的id进行注入;
@Autowired + @Qualifier可以根据id注入
@Resource 该注解等价于@Autowired + @Qualifier
ApplicationContext是BeanFactory的子接口,提供了更完整的功能;(如:①继承MessageSource,因此支持国际化。②统一的资源文件访问方式。③提供在监听器中注册bean的事件。④同时加载多个配置文件。)BeanFactory采用的是延迟加载的形式来注入bean,即只有在用到某个bean时才会调用getBean()方法对该bean进行加载实例化;而ApplicationContext则是在容器启动时,就一次写创建了所有的单例bean;
ApplicationContext和BeanFactory都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,而两者之间的区别数BeanFactory需要手动注册,ApplicationContext则是自动注册。
BeanFactory是IOC容器的顶级接口,是spring的IOC工厂;
FactoryBean是将对象放入IOC容器的一种方式;
BeanFactory是一个factory,是spring的IOC工厂;
FactoryBean是spring创建bean的一种方式。
首先,Spring 里面的核心功能是IOC容器,所谓IOC容器呢,本质上就是一个Bean的容器或者是一个Bean的工厂。
它能够根据xml里面声明的Bean配置进行bean的加载和初始化,然后BeanFactory来生产我们需要的各种各样的Bean。
所以我对BeanFactory的理解了有两个。
- BeanFactory是所有Spring Bean容器的顶级接口,它为Spring的容器定义了一套规范,并提供像getBean这样的方法从容器中获取指定的Bean实例。
- BeanFactory在产生Bean的同时,还提供了解决Bean之间的依赖注入的能力,也就是所谓的DI。
FactoryBean是一个工厂Bean,它是一个接口,主要的功能是动态生成某一个类型的Bean的实例,也就是说,我们可以自定义一个Bean并且加载到IOC容器里面。
它里面有一个重要的方法叫
getObject()
,这个方法里面就是用来实现动态构建Bean的过程。Spring Cloud里面的OpenFeign组件,客户端的代理类,就是使用了FactoryBean来实现的。
spring bean的生命周期就是一个对象从创建到销毁的整个过程,大致可分为四个阶段:①实例化;②属性赋值;③初始化;④销毁;
1.解析xml配置或注解配置的类,得到BeanDefinition;(也就是bean的描述描述的是 这个bean的类型,即这个bean的名字、有哪些属性、有哪些构造函数、有哪些方法。)
2.通过BeanDefinition反射创建Bean对象;
3.对Bean对象的成员变量进行属性填充;
4.回调实现了Aware接口的方法,如BeanNameAware;
5.调用BeanPostProcessor的初始化前处理;
6.调用init初始化方法;通常在这个阶段可以进行相关资源的初始化、配置检查等操作。
7.在初始化方法执行完成之后调用BeanPostProcessor的初始化后处理,此处会进行AOP;
8.将创建的Bean对象放入一个Map中;
9.业务使用Bean对象;
10.Spring容器关闭时调用DisposableBean的destory()方法销毁;
Spring bean的生命周期包括以下几个阶段:
1. 实例化:当Spring容器启动时,会根据配置文件中定义的bean定义信息,将需要被实例化的类进行实例化操作。
2. 属性赋值:在实例化之后,Spring容器会通过调用setter方法或者使用构造函数进行bean属性的赋值操作,完成bean属性的初始化。
3. Aware接口回调:在完成bean属性的注入之后,Spring容器会自动回调实现了Aware接口的相关方法,比如BeanFactoryAware、ApplicationContextAware等。
4. BeanPostProcessor前置处理器执行:在以上三个步骤完成后,Spring容器会在特定的时机,比如bean实例化后、初始化前等时机,根据配置执行实现了BeanPostProcessor接口的前置处理器方法,可以在这个阶段进行一些自定义的处理。
5. 初始化方法执行:经历了前几个步骤之后,Spring容器会在特定的时机调用bean中配置的初始化方法,可以是通过@Bean注解配置的init-method,也可以是实现了InitializingBean接口的方法,通常在这个阶段可以进行相关资源的初始化、配置检查等操作。
6. BeanPostProcessor后置处理器执行:在初始化方法执行完成之后,Spring容器会在特定的时机再次执行实现了BeanPostProcessor接口的后置处理器方法,可以在这个阶段继续进行bean的自定义处理。
7. 销毁方法执行:当Spring容器关闭时,会回调实现了DisposableBean接口的destroy方法,也可以配置通过注解@Bean指定的destroy-method。在销毁方法中可以完成一些资源的释放、清理等操作。
值得注意的是,以上生命周期中的各个阶段都是可选的,具体的执行顺序与执行方式会根据不同的配置和实现而异。同时,在bean实例化和初始化的过程中,也可以实现BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor等接口,对bean进行进一步的自定义处理。
InitializingBean接口、DisposableBean接口、@PostConstruct 注解 和 @PreDestroy注解;
xml方式
建议在项目中使用@PostConstruct和@PreDestroy,因为InitializingBean和DisposableBean与代码强耦合。基于xml方式我们也可以使用
singleton:单例模式,在整个SpringIoC容器中,使用singleton定义的bean只有一个实例;
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的bean时,都会产生一个新的bean实例;
只有在web应用中使用Spring时,request、session、global-session作用域才会生效。
request:对于每次HTTP请求在IOC容器中都会创建该bean,即每次HTTP请求都会产生不同的bean实例;
同一个session共享一个bean实例;
global-session和Portlet应用相关,当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共享全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet容器中的session作用域效果相同。
Spring 框架支持以下作用域的 Bean:
1. singleton:单例作用域,容器中只存在一个实例,每次请求该 Bean 都会返回同一个实例。默认情况下,所有 Bean 都是 singleton,如果没有主动指定作用域,那么 Bean 的作用域就是 singleton。
2. prototype:原型作用域,每次请求该 Bean 都会返回一个新实例。每次注入或者调用 getBean() 方法时都会创建一个新的 Bean 实例。
3. request:请求作用域,每次 HTTP 请求都会创建一个新的 Bean 实例,适用于 Web 应用程序,请求结束时会自动销毁该 Bean。
4. session:会话作用域,同一会话中各个请求共享同一个 Bean 实例,不同会话中会创建不同的 Bean 实例。
5. globalSession:全局会话作用域,多用于集群环境中,同一个集群中所有节点共享一个 Bean 实例,当某个节点失效时,集群中其他节点可以获取到同一个全局会话中的 Bean 实例。
6. application:应用作用域,整个应用程序中只会创建一个 Bean 实例,适用于全局共享的数据。相当于 ServletContext 作用域。
除了以上作用域,还可以通过实现 org.springframework.beans.factory.config.Scope 接口来自定义 Bean 的作用域。 需要注意的是,使用不同作用域的 Bean 需要特别注意其生命周期,比如 RequestScope 的 Bean 需要在请求结束后手动销毁,否则容易造成内存泄漏等问题。
spring中的bean默认是单例模式的,如果bean是有状态的,那就需要开发人员自己进行线程安全的保证,最简单的方法就是改变bean的作用域,把singleton改为prototype,这样每次请求bean都相当于是调用了new Bean()方法,这样就可以保证线程的安全了;(有状态就是指有数据存储功能;无状态就是不会存储数据;controller、service和dao层本身并不是线程安全的,但如果只是调用里面的方法,多个线程调用一个实例的方法,如果没有对共享变量进行写操作,是不会有线程安全问题的。)
在Spring框架中,单例Bean是线程安全的,因为Spring容器仅创建一个单例Bean对象,这个对象的状态不会因线程数的增加而发生变化。 但是,当单例Bean引用其他非线程安全的Bean时,会导致不安全因素。在这种情况下,单例Bean的状态还是线程安全的,但是引用的非线程安全的对象可能导致数据不一致或其他的严重问题。
因此,要确保Spring框架中的单例Bean线程安全,必须确保单例Bean及其所有依赖项都是线程安全的。可以通过以下几种方式来确保单例Bean的线程安全:
1. 应尽量避免在单例Bean中引用非线程安全的对象。
2. 可以使用synchronized关键字或ReentrantLock等机制来保护单例Bean中的某些方法,以确保访问共享数据时的同步性。
3. 可以使用线程安全的Bean替换非线程安全的Bean,比如使用ConcurrentHashMap替代HashMap。
总之,虽然Spring框架中的单例Bean是线程安全的,但是要确保使用线程安全的方式来编写和使用单例Bean,以避免引起应用程序中的数据不一致性和其他问题。
不要在bean中声明任何有状态的实例变量或静态变量,如果必须如此,那么就通过ThreadLocal把变量变为线程私有的,如果bean的这些变量需要在多个线程中共享,就使用synchronized、lock或CAS等这些锁来保证线程安全了。
Spring框架是一个流行的Java开发框架,其中包含许多设计模式的实现以解决常见的软件开发问题。以下是一些设计模式及其应用场景在Spring框架中的使用:
1. 依赖注入(Dependency Injection,简称DI):DI是Spring框架的核心概念,它可以轻松地将对象之间的依赖关系从代码中抽象出来,使得开发人员更加专注于业务逻辑的实现。Spring框架使用DI来实现IoC(Inversion of Control,控制反转)的设计模式。在DI中,对象的依赖关系是通过构造器、Setter方法或者字段注入等方式的配置文件或通过注解自动装配实现的。
2. 面向切面编程(Aspect-Oriented Programming,简称AOP):AOP是一种编程范型,其目的是使系统中需要实现多个模块的横向关注点(如:安全、事务管理等)得到解脱。Spring框架支持AOP,通过在不改变原有代码的情况下,可以动态地向应用中添加横切关注点,这样就可以达到解耦、提高模块化、降低复杂度等好处。
3. 模板方法(Template Method):Spring框架使用了模板方法的设计模式来解决应用程序开发中的重复代码问题。例如Spring框架提供的JdbcTemplate和HibernateTemplate,它们都提供了大量的标准方法,开发人员可以通过继承这些模板类,来免费获得这些标准方法,从而大幅度减少了开发时间和代码量。
4. 单例(Singleton):Spring框架中的单例是指所有实例共享相同的内存区域,在Java中,它是一种常用的设计模式。Spring框架中的单例使用非常普遍,因为它可以保证一个对象只被创建一次,提高了应用程序的性能。
5. 工厂模式(Factory):Spring框架中的BeanFactory就是工厂模式的应用,通过BeanFactory可以动态创建对象,同时也可以更轻易地配置和处理对象之间的依赖关系。
6. 观察者模式(Observer):Spring框架中的事件模型则是利用观察者模式来实现的,所有的事件监听器都注册到执行器中,等待事件发生。一旦事件发生,执行器会通知所有的监听器进行处理。通过这种方式,观察者就可以获取到应用程序中需要的事件,以进一步进行处理。
总之,Spring框架中应用了多种设计模式,并将这些设计模式巧妙地应用于框架的不同部分,从而实现了软件开发中一系列常见问题的解决方案。
框架篇-41-spring_pattern_1_哔哩哔哩_bilibili
spring事务的实现方式:
spring中有两种使用事务的方式,一种是编程式的,另一种是声明式的;声明式又分为基于xml和基于注解;事务这个概念是数据库层面的,spring只是基于数据库中的事务进行了扩展以及提供了一些更加方便操作事务的方式,比如我们可以在某个方法上添加@Transactional注解,就可以开启事务。它的原理是:在一个方法上添加@Transactional注解后,spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象的方法时,如果方法上有@Transactional注解,那么代理逻辑会将吧事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑时出现了异常,则会将事物进行回滚;而针对哪些异常需要回滚是可以配置的,可以利用@Transactional注解中的rollbackFor属性进行配置,默认情况下会对runtimeException和Error进行回滚。
隔离级别:
spring事务的隔离级别就是数据库的隔离级别:read uncommitted(未提交读)、read committed(提交读、不可重复读)、repeatable read(可重复读)、serializable(可串行化);
数据库配置的隔离级别是read committed(提交读、不可重复读),而spring配置的隔离级别是repeatable read(可重复读),这时,隔离级别是以spring的配置为准,如果spring配置的隔离级别数据库不支持,那么效果取决于数据库。
Spring事务管理提供了一种声明式的事务控制方式,通过注解或XML配置进行声明,使开发者可以将事务管理从业务逻辑中剥离出来。
Spring事务管理的实现方式基于AOP技术,通过拦截带有@Transactional注解的方法来封装事务处理。具体实现方式如下:
1. 当一个被@Transactional注解修饰的方法被调用时,Spring事务管理器会先创建一个连接并将其绑定到当前线程上。
2. Spring事务管理器会判断当前线程上是否已经存在一个事务。如果已经存在,则使用现有的事务。如果不存在,则创建一个新的事务。
3. Spring事务管理器会在方法执行之前开始事务,并对请求进行拦截。
4. 如果方法执行成功,则提交事务。
5. 如果方法执行失败,则回滚事务。
隔离级别决定了一个事务在并发情况下的隔离程度,以及可以避免哪些并发问题。Spring提供了多种隔离级别,可以通过@Transactional注解的isolation属性来指定,如下所示:
1. TransactionDefinition.ISOLATION_DEFAULT:使用底层数据源的默认隔离级别。
2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:未提交读隔离级别,可以读取未提交的数据,存在脏读、不可重复读和幻读等问题。
3. TransactionDefinition.ISOLATION_READ_COMMITTED:提交读隔离级别,只能读取已经提交的数据,会导致不可重复读和幻读等问题。
4. TransactionDefinition.ISOLATION_REPEATABLE_READ:可重复读隔离级别,可以避免不可重复读问题,但是仍然存在幻读问题。
5. TransactionDefinition.ISOLATION_SERIALIZABLE:串行化隔离级别,可以避免所有并发问题,但是会降低并发性能。
总之,Spring事务管理器基于AOP技术实现了声明式的事务控制方式,保证了被@Transactional注解修饰的方法具有事务性和一致性。同时,隔离级别可以根据实际业务需求进行调整,以最大限度地降低并发问题的风险。
spring的事务传播机制就是当前方法对调用者方法事务的态度;
spring默认的事务传播类型为REQUIRED:如果调用者有事务就加入到调用者的事务中,如果调用者没有事务则新建事务;
SUPPORTS:如果调用者有事务就加入到调用者的事务中,如果调用者没有事务,就以非事务的方式执行;
MANDATORY:如果调用者有事务就加入到调用者的事务中,如果调用者没有事务就抛出异常;
REQUIRES_NEW:无论调用者是否有事务,当前方法都会开启事务;
NOT_SUPPORTED:调用者不能有事务,即使有也不会使用调用者的事务;
NEVER:调用者不能有事务,如果有就抛出异常;
NESTED:调用者存在事务则在嵌套事务中执行,否则和 REQUIRED的操作一样会开启一个事务。
Spring事务传播机制是指在多个事务方法相互调用的情况下,事务应该如何传播的规则。Spring提供了7种传播行为,允许开发者根据实际业务需求来设置事务的传播行为。以下是Spring事务传播机制的7种常用传播行为及其含义:
1.REQUIRED(默认):如果当前没有事务,则创建一个新事务;如果当前已经存在事务,则加入到当前事务中,成为当前事务的一部分。如果嵌套使用,则子事务会自己开始一个新事务,它与父事务没有任何关系,父事务被暂停,直到子事务完成,才继续执行。
2.REQUIRES_NEW:每次都创建一个新事务,如果当前已经存在事务,则挂起当前事务,然后创建一个新事务。这样做的好处是让每个方法可以独立地运行在自己的事务中,互不干扰。
3.NOT_SUPPORTED:不支持事务处理,每个方法都在自己的事务中运行,如果其他方法存在事务,则暂停该事务,待当前方法执行完毕后,再恢复该事务。
4.SUPPORTS:如果当前存在事务,则继承该事务;如果当前不存在事务,则不使用事务。
5.MANDATORY:必须在一个已有的事务中运行,否则会抛出异常。
6.NEVER:必须在没有事务的环境中运行,否则会抛出异常。
7.NESTED:嵌套的事务,如果当前存在事务,则开启一个子事务运行。如果子事务抛出异常,则子事务和父事务都回滚;如果父事务抛出异常,则只有父事务回滚,子事务可以选择回滚或不回滚。
总之,Spring事务传播机制能够帮助开发者更加精细地控制事务,在多个事务方法相互调用的场景下保证事务处理的一致性和完整性。开发者可以根据实际业务需求选择合适的传播行为,并结合隔离级别和事务超时等配置,优化事务处理效果。
1.数据库引擎不支持事务(以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB;从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以要注意MySQL的版本)
2.bean对象没有被spring容器管理
3.方法的访问修饰符不是public
4.自身调用问题,比如本类中没有开启事务的方法调用了开启事务的方法,也会导致事务失效。
5.数据源没有配置事务管理器,方法上没有开启事务的注解。
6.异常被捕获
7.异常类型错误,默认情况下,Spring只会对error或者RuntimeException的异常回滚。(可以在注解后加rollbackFor=Exception.class来解决)
8.@Transactional(propagation = Propagation.NOT_SUPPORTED)
ropagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起
Spring事务主要使用了AOP技术,通过代理和动态代理来实现。Spring事务通常会在以下情况下失效:
1. 异常没有正确处理:如果一个事务方法抛出了一个未经处理的异常,那么这个事务会回滚。但是,如果异常被捕获了并且没有正确处理,那么事务就会在异常发生后继续提交,导致事务失效。
2. 使用了ThreadLocal:如果在事务方法中使用ThreadLocal,那么事务就会失效。因为ThreadLocal存储的数据只与当前线程有关,而事务可能会跨越多个线程运行,因此可能会引起线程上下文切换导致的数据不一致性。
3. 事务方法没有被正确配置:如果一个事务方法没有被正确地配置,那么事务就会失效,例如未添加@Transactional注解或注解配置有误,或者配置不支持指定的事务隔离级别和传播行为等。
4. 跨越了多个数据源:如果一个事务跨越了多个数据源,那么事务就会失效。Spring只支持在单个数据源上进行事务管理,在多个数据源上进行事务管理需要使用分布式事务管理器。
5. 使用了底层的API来操作数据库:如果在事务方法中直接使用底层的JDBC、Hibernate或myBatis等API来操作数据库,那么事务就会失效,因为这些API无法与Spring事务管理器进行集成。
总之,Spring事务的失效主要是由于事务边界没有正确地配置或底层API的使用等原因造成的。正确地使用Spring事务管理器和编写符合规范的事务代码可以避免事务失效的风险。
基于XML的配置方式:
Spring提供了多种配置方式来实现事务管理,包括编程式事务管理和声明式事务管理两种方式:
1. 编程式事务管理:通过编写程序代码来实现事务管理。这种方式需要在Java代码中嵌入大量的事务管理代码,因此通常只用于简单的事务处理场景。
2. 声明式事务管理:通过AOP技术将事务处理和业务逻辑解耦合。这种方式使用注解或XML配置来声明事务特性,使得开发者可以将事务处理从业务逻辑中剥离出来,从而提高代码复用性和可维护性。
在声明式事务管理中,Spring提供了两种方式来配置事务:XML配置和注解配置。
1. XML配置:这是一种传统的方式,可以通过XML配置事务管理器、事务通知以及事务切面等信息,比较灵活,但需要手动维护XML文件,不太容易维护和升级。
2. 注解配置:这是一种比较简单、易于维护和升级的方式,可以通过注解的方式将事务管理器、事务通知以及事务切面等信息声明在代码中,相对XML配置更加便捷。
在注解配置中,使用@Transactional注解来声明事务特性,该注解可以出现在类级别或方法级别上。其中,isolation、propagation、readOnly和rollbackFor等属性可以用来定义隔离级别、传播行为、只读事务以及需要回滚的异常类型等信息,具体使用可根据实际业务需求进行配置,以实现对事务的控制。
总之,Spring提供编程式和声明式两种事务管理方式,声明式事务管理又可通过XML配置和注解配置来实现。开发者应该根据具体的业务需求和场景选择合适的方式来实现事务管理。
基于注解的配置方式:@Transactional注解
在Spring中,循环依赖指的是两个或多个Bean相互依赖,形成了循环依赖关系。举例来说,如果Bean A依赖Bean B,而Bean B又依赖Bean A,则形成了循环依赖。
Spring框架支持循环依赖,但是仅限于单例模式的Bean之间的循环依赖,因为Spring可以在一个单例Bean创建的过程中把每个中间状态的Bean实例缓存起来,用于后续递归依赖查找时返回一个早先创建的Bean实例。然而,如果是原型模式的Bean之间形成循环依赖则无法解决。
当Spring框架无法处理循环依赖时,会抛出BeanCurrentlyInCreationException异常。
为了解决循环依赖问题,可以有以下两种方式:
1. 在Bean定义中使用Setter方法进行注入,而不是构造函数注入,这样就可以完成循环依赖了。
2. 使用@Lazy注解来标记Bean的延迟加载,这样Spring容器就会在运行时加载Bean,因此能够在上下文加载完毕后处理循环依赖问题。
需要注意的是,循环依赖可能会导致应用程序在启动时变慢,同时会使Bean之间的依赖关系更加复杂,因此应该尽可能的减少循环依赖的使用。
框架篇-43-循环依赖_铺垫_ProxyFactory_1_哔哩哔哩_bilibili
框架篇-27-spring_webmvc_执行流程_初始化_哔哩哔哩_bilibili
客户端(浏览器)发送请求到前端控制器DispatcherServlet;前端控制器通过HandlerMapping获取对应的Handler(执行链);前端控制器通过Handler获取处理器适配器HandlerAdapter;HandlerAdapter执行Handler并处理相应的业务逻辑;处理器处理完业务后,会返回一个ModelAndView对象;ViewResolver将ModelAndView解析后返回View;前端控制器进行视图渲染(填充数据);前端控制器把View返回给请求者(浏览器)。
客户端发送请求到前端控制器,前端控制器通过请求路径去HandlerMapping中寻找对应的处理器,返回一个包含处理器的执行链,然后通过处理器去寻找对应的处理器适配器,通过处理器适配器调用对应的handler,也就是执行我们的业务代码,执行完成之后返回一个ModelAndView给到前端控制器,前端控制器通过视图解析器解析出对应的视图后,再跟model进行视图渲染,返回给浏览器。
1.中央控制器(DispatcherServlet):主要是负责request和response对象的转发和响应;
2.处理器映射器(HandlerMapping):主要根据URL来匹配出能处理相关请求的类;
3.处理器适配器(HandlerAdapter):主要负责调用响应的处理器来处理请求;
4.视图解析器(ViewResolver):根据处理器返回的视图名称添加前缀和后缀拼接出一个真是的路径。
mapping,对请求url和controller方法进行映射:@RequestMapping(路由映射)@GetMapping(对应GET请求)@PostMapping(对应POST请求)@PutMapping(对应PUT请求)@DeleteMapping(对应DELETE请求),其他注解都是@RequestMapping的衍生;
rest:@Controller(用于将该对象存储到Bean中。)@ResponseBody(因为 SpringMVC 默认是返回一个页面,告诉程序我返回的不是一个页面。)(@RestController=@Controller+@ResponseBody)@RequestBody(用于接收 JSON 对象)@ResponseStatus(改变HTTP响应的状态码,具有value、code、reason 三个属性);
统一处理:@ControllerAdvice(声明一些全局性的东西,比如全局异常处理器)@ExceptionHandler(配置@ControllerAdvice处理全局异常,也可以使用在Controller中进行局部异常处理)@RestControllerAdvice=@ControllerAdvice + @ResponseBody;
参数:@RequestHeader(将请求头中的参数值映射到控制器的参数中)@CookieValue(用来获取Cookie中的值)@RequestParam(接收路径变量传参,作用在方法传递的参数前,用于接收所传参数;例如:http://localhost:8081/selectStudentById?id=01 接收问号后面的参数值(允许多个参数))@PathVariable(接收路径参数);
格式转化:@DateTimeFormat(前台传后台时, 字符串自动封装成日期;例如@DateTimeFormat(pattern="yyyy-MM-dd HH-mm-ss"))@NumberFormat(用来验证输入的数字格式;例如@NumberFormat(pattern="###,###.###"))@JsonFormat(将Date转换成String 一般后台传值给前台时;例如@JsonFormat (pattern = "yyyy-MM-dd HH:mm:ss"));
校验:@Validated表示java对象要进行校验,具体校验还是靠其他注解;
ajax跨域:@CorssOrign 用在类上或方法上表示允许ajax跨域。
SpringMvc接收前端传递的参数,经过【类型转化】,将参数封装到【controller方法的形参】;前端传递不同格式的参数,需要在controller方法中定义不同类型的形参进行参数绑定。
1.引入jackson对xml支持的依赖
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.前端在请求web接口的时候携带请求头
首先文件上传对前端有三个要求:①请求方式必须是POST;②表单的enctype必须是multipart/form-data;③表单中至少要有一个文件上传表单项
其次通过springMVC接收上传的文件:①导入commons-fileupload(springMVC底层依赖的就是Apache的FileUpload);②配置文件上传解析器;③在Controller方法的形参中定义MutipartFile,接收上传的文件,形参要求的名称需要和文件上传表单项名称一致;就可以接收上传的文件;
commons-fileupload
commons-fileupload
1.3.1
/**
* 素材上传
*/
@PostMapping("/upload_picture")
public ResponseResult upload(MultipartFile multipartFile) throws IOException {
//1.接收参数
//2.调用service完成图片上传
//3.响应
return wmMaterialService.upload(multipartFile);
}
@Override
public ResponseResult upload(MultipartFile multipartFile) throws IOException {
//获取源文件的扩展名
String extension =
FilenameUtils.getExtension(multipartFile.getOriginalFilename());
//给文件生成一个UUID文件名
String uuid = UUID.randomUUID().toString();
//1.将文件保存到minio
String url = fileStorageService.uploadImgFile("", uuid + "." + extension,
multipartFile.getInputStream());
//2.将素材信息保存到素材表
WmMaterial wmMaterial = new WmMaterial();
wmMaterial.setUserId(ThreadLocalUtils.getUserId());
wmMaterial.setUrl(url);
wmMaterial.setCreatedTime(new Date());
wmMaterial.setIsCollection(NOT_COLLECT);
wmMaterial.setType(TYPE_IMAGE);
this.save(wmMaterial);
return ResponseResult.okResult(wmMaterial);
}
使用springMVC+spring这种开发模式的时候,会有两个容器,分别是spring容器和springMVC容器,spring容器是父容器,springMVC是子容器;子容器可以访问父容器上的资源,所以我们可以在controller注入service;
springMVC容器管理着controller、handlerMapping、viewResolver;
spring容器管理着service、mapper、dao和datasource;
不是线程安全的,解决方法就是不要再bean中任何有状态的实例变量或者静态变量,如果必须如此,就把这些变量通过TreadLocal变为线程私有的,如果bean的这些变量需要在多个线程之间共享,那么就需要使用到synchronized、lock、CAS等这些锁来实现线程安全了。
restful风格是一种软件架构设计风格;知道程序人员怎么设计一套API(web接口),它简单、便于维护和访问;在restful风格中通过一个url利用不同的http请求动词来表示不同的操作;分别有GET查询、POST添加;DELETE删除、PUT修改;
支持,注解有:@GetMapping、@PostMapping、@DeleteMapping、@PutMapping、@PathVariable、@RequestBody、@ResponseBody
有三种方式;
方式①:在controller方法的形参中可以直接定义HttpServletRequest HttpServletResponse,HttpSession,然后springMVC框架如果发现controller方法定义了以上三个形参,在调用的时候会自动传递。
方式②:通过RequestContextHolder中的静态方法getRequestAttributes()来获取;
方式③:注入HttpServletRequest HttpServletResponse,HttpSession
处理器映射器HandlerMapping是SpringMvc中的一个组件,其作用是根据url获取Handler,spring中定义了HandlerMapping接口,该接口有很多实现类,其中RequestMappingHandlerMapping;
处理器适配器HandlerAdapter是是SpringMvc中的一个组件,起作用是执行Handler,spring中定义了HandlerAdapter接口,该接口中有很多实现类,其中RequestMappingHandlerAdapter。
转发和重定向分别使用forward 和 redirect 关键字在 controller 层进行处理。
转发:方法返回值为String表示逻辑视图,返回ModelAndView,封装逻辑视图和request域中数据;返回forword:url;
重定向:返回redirect:url
//转发
//方式一
@Controller
public class ModelTest {
@RequestMapping("/user/test1")
public String test1(Model model){
//转发
model.addAttribute("msg","SpringMVC方式实现转发");
return "/WEB-INF/jsp/test.jsp";//或者直接return "/index.jsp"
}
//页面显示:SpringMVC方式实现转发
//方式二
@RequestMapping("/user/test2")
public String test2(){
//转发二
return "forward:/index.jsp";
}
//页面跳转到index.jsp,但是地址栏不变http://localhost:8080/m1/t2
//重定向
@RequestMapping("/user/test3")
public String test3(){
//重定向
return "redirect:/index.jsp";
}
//页面跳转到index.jsp,地址栏显示http://localhost:8080/index.jsp
}
全局异常处理器
系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
方式一:可以通过HandlerExceptionResolver接口来实现全局异常处理器,步骤:定义一个类,实现HandlerExceptionResolver接口,重写里面的resolveException方法;类上加@Component注解,将该类交由spring管理。
方式二:全局异常处理器;使用@Controlleradvice + @ ExceptionHandler 注解;定义一个类,类上加@Controlleradvice注解,类中定义方法,返回值按需求返回,参数为Exception e或自定义异常类的对象,方法上加注解@ExceptionHandler(Exception.class/或自定义异常类的字节码文件);
这两种方式都要定义自定义异常类,一般继承RunTimeException;
1.自定义拦截器,实现HandlerInterceptor接口;
2.springMVC配置文件中配置或在配置类中配置;
1.实现原理不同,过滤器和拦截去底层实现方式不同,过滤器是基于函数回调,拦截器基于反射机制也就是动态代理实现的。
2.适用范围不同,过滤器实现的是javax.servlet.Filter接口,而这个接口实在servlet规范中定义的,也就是说过滤器Filter的是同要依赖于Tomcat等容器,导致它只能在web程序中使用。
3.触发时机不同,过滤器filter是在请求进入tomcat服务器后,进入servlet之前进行预处理,在servlet处理完以后请求结束;
拦截器interceptor是在请求进入servlet后,进入controller之前进行预处理,controller中渲染了对应的视图之后请求结束。
4.拦截范围不用,过滤器几乎可以对所有进入tomcat的请求起作用,而拦截器只会对controller中的请求或访问static目录下的资源请求起作用。
Spring Boot是一个快速开发框架,快速的将一些常用的第三方依赖整合(通过Maven子父亲工程的方式),简化xml配置,全部采用注解形式,内置Http服务器(Jetty和Tomcat),最终以Java应用程序进行执行。
起步依赖、自动装配、辅助功能 如内嵌Http服务器如tomcat、Jetty,健康检测;
springboot将各种场景的依赖包制作成一个个的starter,我们只需要在项目中引入对应场景的起步依赖就可以引入该场景所有的依赖包;而且引入起步依赖后,springboot会将该场景相关的bean交由spring管理,我们只需要在该场景中注入相关的bean即可使用。
1.@SpringBootApplication注解, 这个注解表示了一个springboot的工程,它是一个复合式的注解,是另外三个核心注解的组合,这三个核心注解分别是@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan;
@SpringBootConfiguration他其实就是封装了一个@Configuration的注解,表示当前你在运行springboot的start方法时候。你所指定的那个类上面如果有@SpringBootApplication这个注解,而@SpringBootApplication中包含@SpringBootConfiguration,并且@SpringBootConfiguration又包含@Configuration,就表示当前你所用到的那个类是spring的一个配置类;
@SpringBootConfiguration这个注解实际就是一个@Configuration注解,表示启动类也是spring的一个配置类;
@EnableAutoConfiguration,是一个开启自动配置的注解,底层会向spring容器中导入一个selector,用来加载classPath下的SpringFactories文件,这些文件里面就可以定义一些自动配置类,那么spring就会把这些自动配置类加载为配置bean,spring将会解析这些配置bean中定义的其他bean或配置的一些其他信息;
@ComponentScan用来标识扫面路径,因为默认是没有配置实际的扫描路径的,所以springboot扫描的路径是启动类所在的目录;
2.@Bean注解:用来定义bean,类似于xml文件中的
3.还有@Controller、@Service、@ResponseBody、@Autowired等。
Starter起步依赖是Spring Boot的核心功能特性之一;Spring Boot里面的这些特性,都是为了让开发者在开发应用时,只需要关心业务逻辑,减少对配置和外部环境的依赖。
其中,Starter是启步依赖,它的主要作用有:starter组件以功能为维度,来维护对应的jar包的版本依赖,使得开发者可以不用担心版本冲突这些问题;starter组件会把对应功能的所有jar包全部导入,避免开发者自己去引入依赖带来的麻烦;starter组件内部集成了自动装配机制,也就是说在程序中引入了对应的starter组件后,这个组件会自动集成到spring生态下,并且对于相关bean的管理,也是基于自动装配机制来完成的;依赖starter组件后,这个组件对应的功能所需的外部化配置会自动集成到springboot里,我们只需要在application.yaml文件中进行维护就好,比如Redis或mysql的起步依赖,只需要在application配置文件中配置相关的连接信息即可使用。
虽然springboot官方提供了很多starter组件,但不一定维护了所有中间件的starter,所以对于不存在的Starter,第三方组件一般会自己去维护一个。
官方的starter和第三方的starter组件,最大的区别在于命名上,官方维护的starter的以spring-boot-starter开头的前缀;第三方维护的starter是以spring-boot-starter结尾的后缀,这也是一种约定优于配置的体现。
properties、yml、yaml(xml(不推荐))
三种:@Value、@ConfigurationProperties、Environment
1.可以通过配置项 spring.profiles.active 的值来激活对应的环境,实现思路就是使用一个默认的配置文件作为通用配置文件,不同的配置项写在不同环境的配置文件中,部署不同环境时,只需要修改spring.profiles.active的值即可,不同环境对应的配置文件名不同:(application.properties : 通用配置,不区分环境;application-dev.properties : 开发环境;application-test.properties : 测试环境;application-prod.properties : 生产环境)(一般情况下通用配置文件中只保留spring.profiles.active一个配置项,这样的灵活性更高一点)
激活:1.通过部署命令java -jar xxx.jar --spring.profiles.active=xxx 来激活指定的配置项;
2.配置文件的方式,在application.properties或者application.yml文件中通过spring.profiles.active属性来设置,其值对应{profile}值。
如:spring.profiles.active=test就会加载application-test.properties配置文件内容
3.Java系统属性方式也是一种外部配置的方式,在执行java -jar命令时可以通过
-Dspring.profiles.active=test的方式进行激活指定的profiles列表。
如:java -Dspring.profiles.active=dev -jar xxx.jar