关于“对比”类型的面试题,建议回答时包括:
变量和对象是两个不同概念
Object a = new Object()
//a 就是变量 在内存中实际存在的数据就是对象
所有引用类型的变量值都是引用地址
==与equals() 相同 / 相似之处:这两者都是用于比较两个变量是否“相同”的
==与equals() 区别:
==
是基本用算符,适用于所有类型的变量与变量的对比equals()
是Object类定义的方法,用于Object是Java的基类(所有类的父类),所以任何对象都可调用equals()
方法实现对比,但是基本数据类型并不是对象,无法调用该方法实现对比equals()
只是一个方法,到底返回true
或false
取决于方法的实现,默认情况(根据Object的定义)它与==
结果相同,方法可以被重写,在Java中,许多类都重写了equals()
,如String
、包装类、日期等,尽管是不同的对象,但equals()
结果为true
由于Java对常量池的特殊处理,直接声明的字符串使用==
对比也是成立的
在[-128,127]区间的值对整型包装类对象使用==
对比也是成立的
==
可用于对比所有数据,而equals()
只能被对象调用,==
对比的是变量值是否相同,所以基本数据类型的变量只要字面值相同就为true
,引用类型的变量仅当引用地址相同时才返回true
,equals()
是Object
定义的,默认使用==
实现对比,所以,当该方法没有被重写时,执行效果与==
相同,如果被重写,则取决于重写的代码,如String
类,在执行equals()
将逐一对比字符串中的每个字符,所以,只要两个String
对象的字符完全相同,两个String
对象使用equals()
对比返回结果为true
Byte
/Short
/Integer
/Long
类型的对象赋值时,使用==
对比的结果为true
==
进行对比,因为基本数据类型变量不可调用equals()
,对于引用数据类型的变量推荐使用equals()
进行对比,且在必要的情况下,重写equals()
,使之返回结果的规则符合当前编码要求,重写时,保证同一个对象的对比结果为true
,即如果==
对比为true
,则equals()
对比返回true
通常,口头描述的hashCode
指的是hashCode()
方法,或该方法的返回值,hashCode()
方法是由Object
类定义的,所以在Java中,所有类都有该方法,并且所有类都可重写该方法
HashMap
提供的哈希表hashCode
方法就必须始终返回相同的整数,前提是在对象的equals
比较中使用的信息没有被修改。对于同一个应用程序而言,某一次的执行与另一次执行时,该值需要保持一致equals(Obiect)
方法对比的结果是相等的,那么在这两个对象上调用hashCode
方法必须产生相同的整数结果equals(Obiect)
方法,两个对象不相等,那么在这两个对象上调用hashCode
方法必须产生不同的整数结果。然而,程序员应该知道,为不相等的对象产生不同的整数结果可能会提高哈希表的性能hashCode
就是对象的内存地址hash
)一般指散列算法,也叫哈希算法,在Object
类的实现中,哈希码(hashCode
)是通过哈希算法得到的一个整型结果,本质与内存地址没关系Object
类的hashCode()
实现,每个对象的hashCode
值(理论上)都不同,通常可以用于判断两个变量是否引用同一个对象hashCode
无法表示对象的内存地址
hashCode
值并不会变-2147483648~2147483647
,Java管理的内存已经超过4GB,所以,hashCode
不可能是内存地址hashCode
HashMap
提供的哈希表,hashCode
的设计是提供给JVM管理对象时使用的,并不是给开发者自行使用的hashCode
定位需要使用的对象,典型的Hash容器:HashSet
、HashMap
、HashTable
、ConcurrentHashMap
。再次强调hashCode
不是对象的内存地址hashCode
来排除两个不相同的对象,例如向HashSet
的元素、HashMap
的Key
等都要求“唯一”,如果即将添加的元素的hashCode
与集合中已有的每个元素的hashCode
均不同,则可以视为“当前集合中尚不存在即将添加的元素。如果两个对象的hashCode
相同。Hash容器还会调用equals()
方法,仅当equals()
也返回true
时,才会视为“相同”hashCode()
是Object
定义的方法,它将返回一个整型值,它并不代表对象在内存中的地址,它存在的价值是为Hash容器处理数据时提供支持,Hash容器可以根据hashCode
定位需要使用的对象,也可以根据hashCode
来排除两个不相同的对象,即:hashCode
不同,则视为两个对象不同hashCode()
时,应该遵循Java SE的官方指导:
equals()
对比的结果为true
,则这两个对象的hashCode()
返回的结果应该相同equals()
对比的结果为false
,则这两个对象的hashCode()
返回的结果应该不同equals()
方法和hashCode()
方法,而是使用IDE生成,例如Eclipse、IntelliJIDEA,它们生成的方法是符合以上指导意见的char[]
实现的CharSequence
接口replace()
、indexOf()
等String
的“不可变”特性:每个字符串对象都是不可变的,其特性是由于其内部通过管理一个char[]
决定的,Java语言中,数组在内存中必须是连接的,长度是不可变的
String s = "hello";
s = "hello, world !";
//以上代码中,声明了1个变量,创建了2个对象
String的“不可变”特性与该类声明中的final
无关。final
只是表示这个类不可以继承,String的“不可变”特性,在String的API中,所有修改字符串的方法都将返回新的String对象,基于String的“不可变”特性,其修改操作的效率非常低下,需要寻址、创建新对象,还可能将原有的char[]的某部分复制到新的char[]中
StringBuffer
和StringBuilder
从一开始就会使用长度更长的char[],哪怕只用于存放少量的几个字符。其length()
方法会返回实际存放的字符数量
在许多调整字符串的操作中,StringBuffer
和StringBuilder
只需要直接调整内部的char[]
即可,不需要频繁的寻址、创建新对象等操作,所以,实际执行效率远高于String
类!
当然,如果默认的char[]
长度(实际长度)不足以满足运算需求时,会自动扩容,也需要创建新的对象
CharSequence
接口replace()
、indexOf()
等StringBuffer
和StringBuilder
会保证管理的char[]
的长度始终高于实际存入的字符长度,在处理字符串操作时,效率远高于StringStringBuffer
是线程安全的,而StringBuilder
不是StringBuffer
和StringBuilder
在处理字符串时的效率远高于String
,但并不是每个String都需要频繁的改变,相比之下,使用String的语法更加简洁、直观,实际占用的存储空间更小,所以,当字符串不需要频繁的改变时,优先使用String。如果字符串需要频繁改变,原则上来说,仅当单线程运行时,或已经采取措施保障线程安全时,优先使用StringBuilder
,因为它的执行效率高于StringBuffer
,事实上,尽管StringBuilder
的执行效率比StringBuffer
高,但差距并不大,为了避免后续调整带来的安全隐患,当字符串可能频繁改变时,一般使用StringBuffer
。volatile
是Java语言中的一个关键字,可以修饰类的属性,英译一般为:不稳定的
在代码没有依赖关系的前提下,出于优化的目的,CPU和编译器均可能会对指令进行重新排序,可能导致执行顺序与源代码顺序并不相同
//以下2行代码的执行先后顺序可以被改变,并且不会出现任何错误
int x = 5;
int y = 8;
//以下2行代码的执行先后顺序不会被改变
int x = 5;
int y = x + 8;
但是在多线程下,指令重排可能导致运行结果不符合预期
volatile
关键字进行修饰线程更新了共享变量的值,但其它线程仍使用工作内存中缓存的值,出现属性可见性问题,添加volatile
即可解决此问题
volatile
,它是一个关键字,用于修饰类的成员,主要作用是禁止指令重排,确保多线程时属性的可见性synchronized
和volatile
均不可替代彼此,虽然两者都是用于解决多线程相关问题的,但问题的情景并不相同,通常,使用synchronized
解决的问题大多是“多个线程执行相同的代码”的情景,而使用volatile
解决的问题大多是“多个线程执行的代码不同,但使用到了相同的共享变量”的情景volatile
关键字run()
方法会尝试调用Runnable
对象(如果存在的话)的run()
方法, 否则什么都不执行,也不返回任何值Runnable
对象作为构造方法的参数,当线程启动会调用Runnable
对象的run()
方法。所以,你应该在Runnable
实现类中实现run()
方法Thread
子类的对象,则应该在Thread
子类中重写run()
方法Runnable
表示“可执行的”,创建Thread
对象时,可以使用Runnable
接口类型的对象作为构造方法的参数,并且,在子线程中执行的确实是Runnable
实现类中的run()
方法,但是,Runnable
自身并不是线程接口,事实上,还有许多其它类都可能使用到Runnable,但与线程完全没有关系。都是List接口的实现类,都是序列的,可存储相同元素,绝大多数情况下不需要关心特有方法
LinkedList
的存储结构TreeSet
、LinkedHashSet
的各元素就可以表现出“有序”的特征hashCode()
返回值相同,且equals()
对比结果为true
时,视为“相同”Set
集合不可以存储相同元素ArrayList
的底层实现是基于数组的
LinkedList
的底层实现是基于双向链表的
因为LinkedList
的底层实现基于双向链表,当添加元素时,本质是基于新元素创建“节点”,每个节点需要记录指向前一个节点和后一个节点的引用,占用的存储空间更多。
无论是
无论是ArrayList
,还是LinkedList
,都是线程不安全的,当在多线程中需要使用List时,应该使用CopyOnWriteArrayList
ArrayList
,修改使用LinkedList
ArrrayList
易读难写,但是没有写入数据就无从读起LinkedList
易写难读,但是光写入,不读取就毫无意义ArrayList
和LinkedList
这两者之间没有继承关系,不可互相转换ArrayList
或LinkedList
ArrayList
的底层实现是基于数组的,所以查询效率高,修改效率低LinkedList
的底层实现是基于双向链表的,所以查询效率低,修改效率高,另外内部本质上管理的是多个节点,每个节点需要记录指向前一个节点和后一个节点的引用,占用的存储空间更多ArrayList
和LinkedList
的性能差异并不明显(在绝大部分情况下,使用List时的元素数量都不超过100个,尽管元素数据更加复杂),并且,不可以单纯的只读不写,或只写不读,同时,基于ArrayList
占用的存储空间更少,一般使用ArrayList
即可,仅当需要极致的追求性能时,再根据读写频率来区分使用,但是当需要考虑线程安全问题时,则使用CopyOnWriteArrayList
范式:Normal Form
,可缩写为NF
在设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范(例如第1、第2、第3),越高的范式数据库冗余越小
早期提倡的有三大范式,目前已经发展到6个范式,但一般只讨论最初的三大范式即可
解读:在现实中,各“班级”都是归属于“学院”的,即:只要是“JSD001”学院就一定是“软件工程学院”,所以,“学院”依赖于“班级”,形成“非主属性”依赖于另一个“非主属性”的情况,不符合第三范式的要求
只要是“可以不存”、“可以使用id表示” 、“存了多次(非id) ”的数据,却直接存储到数据库里,都可以称之为“冗余”
冗余的缺点主要在于:
占用较多的存储空间:如果可以不存,却存了,肯定多占用了空间,如果可以存id,却存的是较长的字符串,也会多占用存储空间
不便于管理维护:如果使用关联,其它表中只存id,当数据需要修改时,只修改数据所在的表即可,如果存的不是id,而是数据本身,则修改时需要将所有表都同时修改,删除也会有类似的问题
遵守数据库范式,设计不冗余的数据表,同时,也会把原本冗余的数据表中的数据拆分到多张表中去,导致“单一的数据表不足于表现数据”,如当需要查看订单信息时,仅仅只是查询“订单信息表”是完全不够的,因为即使知道商品id是P001也不知道这到底是什么商品,更不足以显示到软件的界面中去,为了查询到完整的信息,必须查询多张表
当表的关联越复杂,查询时需要关联的表就越多,但是,如果采取的是“冗余”的设计方案,只需要查询“订单信息表”这1张表就能查询到完整的信息。
可见,即使是“冗余”的设计方案,它也是有优点的,就是“简单! 快!”,所以冗余不一-定是缺点,适当冗余可提高查询性能,所以,数据库范式不是必须完全遵循的,应该根据实际情况来决定!
varchar
类型的字段的值的长度相对可控,推荐使用char
替代,因为char
的效率更高一些varchar
类型的理论长度可达65535,但如果可能超过5000 (部分企业可能约定为其它值),建议将其设计到另张表中去, 并与当前表关联可能建议将字段类型改为text
数据库三范式:
数据库范式的核心思想包括“消除冗余”,冗余的缺点在于占用较多的存储空间,不便于管理维护。但是,适当冗余可提高查询性能。数据库范式不是必须严格遵守的
在属性的声明之前添加@Autowired
注解。该类必须是Spring管理对象的
public class UserController {
@Autowired
private IUserService use rService;
}
@Autowired
是不安全的,在执行单元测试(不依赖于任何非测试环境,包括Spring环境, 如果加载了非测试环境,则称之为集成测试)时,由于不加载Spring环境,属性将不会被注入值,则相关代码会出现NPE
(NullPointerException
),或者,无论是任何原因导致未加载Spring环境的运行,都会导致NPE
在需要被Spring调用的Setter
方法的声明之前添加@Autowired
注解该类必须是Spring管理对象的
public class UserController {
private IUserService userService;
@Autowired
public void setUserService ( IUserService userService) {
this.userService = userService;
}
}
配置类中的@Bean
方法也是Spring自动调用的,Spring也会尝试从容器中查找匹配的对象并用于调用@Bean
方法
private
的,在没有加载Spring环境时,也可以手动调用Setter
方法以避免出现NPE
问题,将属性声明为private
是开发规范,应该遵守lombok
,源代码中根本没有Setter
方法,无法添加注解,在没有加载Spring环境时,如果没有手动调用Setter
方法,仍会导致NPE
在需要被Spring调用的构造方法的声明之前添加@Autowired
注解该类必须是Spring管理对象的
public class UserController {
private IUserService userService;
@Autowired
public UserController(IUserService userService) {
this.userService = userService;
}
}
仅当类中有多个构造方法时才需要添加该注解,如果仅有1个构造方法,Spring会自动调用
NPE
问题使用@Autowired
时,可以通过属性注入、Setter注 入和构造方法注入这3种方式,Spring本身并不关心你使用哪种方式,只要使用方式没有问题,都是可以装配的
NPE
问题private
的,有Setter
方法时,即使不加载Spring环境,也可以手动调用,以避免出现NPE
问题private
的,没有可为属性赋值的构造方法,也没有Setter
方法,当不加载Spring环境时,必然出现NPE
问题Setter
注入还是构造方法注入,Spring官方在VSP
(VMware Spring Professional
)培训文档中明确指出:Spring doesn't care (can use either), Constructor injection is generally preferred
NPE
问题,即出现安全问题Setter
注入方式比较中庸,并且使用lombok
时不可行,并不推荐使用NPE
安全问题,但是需要保证类中仅有1个将用于对各需要注入值的属性赋值的构造方法,而且,会导致构造方法的参数列表可能很长,并且,必须结合构造方法,才可以明确哪些属性将被注入值,增减属性都需要做相应的调整。总的来说,相对麻烦,编写代码成本略高,总的来说,虽然安全,但缺点也比较多,是对代码安全性的要求非常高时的唯一方案@Autowired
和@Resource
的区别在使用Spring框架及基于它的进阶框架(Spring MVC
、Spring Security
、Spring Boot
)时,多可以实现自动装配
public class UserController{
@Autowired
private IUserService userService;
}
public class UserController{
@Resource
private IUserService userService;
}
都可以用于属性或Setter
方法,以实现装配
public class UserController{
@Autowired //或 @Resource 等效
private IUserService userService;
}
public class UserController{
private IUserService userService;
@Autowired //或 @Resource 等效
public void setUserService(IUserService userService){
this.userService = userService;
}
}
@Autowired
是Spring框架中定义的注解
@Resource
是javax包的注解
@Autowired
可以添加构造方法的声明之前@Resource
不可以添加在构造方法的声明之前
@Resource
还可以添加在类的声明之前,但不会装配属性的值@Autowired
是先尝试根据类型装配,再尝试根据名称进行装配
@Autowired
的required属性:ture:抛出异常,false不装配,即属性值为null@Resource
是先尝试根据名称装配,再尝试根据类型装配:能装配,则装配,最后仍然装配不了的则抛出异常误区:使用@Resource
取代@Autowired
误区产生原因:
@Autowired
会报错,但是使用@Resource
不会@Autowired
注解解读:以IntelliJ IDEA v2020.1.4
为例,当尝试注入使用Mybatis
时定义的接口对象时,添加@Autowired
会报错(无此对象),但是使用@Resource
不会
这是由于某些版本的IntelliJ IDEA
的误判导致的,事实上,这样的代码(正在报错)可以正常运行,或者,将此代码导入到Eclipse等其它开发工具中,并不会提示任何错误,也可以正常运行。并且,当你在持久层(存储层)接口的声明之前添加@Repository
注解后,在lntelliJIDEA
中使用@Autowired
时的错误也就消失了。
以IntelliJlDEA v2020.1.4
为例,当项目中使用Spring Security
,且尝试使用@Autowired
注入UserDetailsService
类型的对象时提示错误(有多个同类型对象),但是使用@Resource
不会
解决:补充@Qualifier
注解,或将类型声明为UserDetailsService
接口的实现类的类型,将不再提示错误
在属性上使用@Autowired
确实是不安全的,在执行单元测试(不依赖于任何非测试环境,包括Spring环境,如界加载了非测试环境,则称为:集成测试)时,由于不加载Spring环境,属性将不会注入值,则相关代码会出现(NullPointerException),或者,无论是任何原因导致未加载Spring环境的运行,都会导致NPE,在面临这样的问题时,即使改为使用@Resource
也不会有任何变化。正确的做法是使用构造方法注入,而不是使用@Resource
替换@Autowired
关于使用构造方法注入属性的值:
如果当前类仅有1个构造方法,Spring会自动调用,无论它有多少参数
如果当前类中有多个构造方法(超过1个),你应该在你希望Spring调用的构造方法的声明之前添加@Autowired
注解,否则,Spring会自动调用无参数的构造方法,如果既没有无参数的构造方法,也没有使用@Autowired
注解,则会报错
当使用@Resource
装配属性的值时,如果存在多个类型相同的对象,且名称均不匹配,可以配置注解属性name的值,以指定某个对象
public class UserServiceImpl {
@Resource( name = "userJdbcRepository")
private IUserRepository userRepository;
}
当使用@Autowired
装配属性的值时,如果存在多个类型相同的对象,且名称均不匹配,需要结合@Qualifier
一起使用
public class UserServiceImpl {
@Autowired
@Qualifier("userJdbcRepository")
private IUserRepository userRepository;
}
public class UserServiceImpl {
private IUserRepository userRepository;
@Autowired
public void setUserRepository( @Qualifier("userJdbcRepository") IUserRepository userRepository){
this.userRepository = userRepository
}
}
使用@Autowired
时,可以通过属性注入、Setter
注入和构造万法注入以3种方式,Spring本身并不关心你使用哪种万式,只要使用方式没问题,都可以装配。
理论上的选取原则:构造方法注入> Setter注入>属性注入
private
的,有Setter方法时,即使不加载Spring环境,也可以手动调用,以避免出现NPE问题private
的,没有可为属性赋值的构造方法,也没有Setter方法,当不加载Spring环境时,必然出现NPE问题到底使用Setter
注入还是构造方法注入,Spring5官方在VSP
培训文档中明确指出: Spring doesn't care (can use either),Constructorinjection is generally preferred
@Autowired
是Spring框架中定义的注解,@Resource
是javax包中的注解@Autowired
可用于构造方法,@Resource
不可以@Autowired
必须结合@Qualifer
注解以指定Bean id/name
,用于属性时,在属性的声明之前同时添加这2个注解,用于方法时,在方法的声明之前添加@Autowired
,在方法的参数之前添加@Qualifier
注解,而使用@Resource
时则是配置该注解的name属性@Autowired
是先尝试根据类型装配,再尝试根据名称进行装配先查找匹配类型的对象的数量
@Autowired
的required属性:
@Resource
是先尝试根据名称装配,再尝试根据类型装配一能装配,则装配,最后仍装配不了,则抛也异常@Autowired
也是Spring的注解,所以,优先使用@Autowired
实现装配,而@Resource
注解是javax包中的,则不优先考虑@Autowired
注解,则Spring会自动调用它,这样做可以避免单元测试时出现NPE问题@Qualifier
注解一起使用,以显式的指定Bean id/name
@Autowired
注解,以实现自动装配,如果要装配的是持久层(存储层)接口类型的对象,并且该接口对象是框架自动生成的,应该在接口的声明之前添加@Repository
注解,以避免某些版本的IntelliJlDEA
报错Connection
、Statement / PreparedStatement
等,所有的具体实现代码都不需要编写Mybatis
时需要声明各数据访问功能的接口与抽象方法Mybatis
实现,接口和抽象方法并不需要调整Mybatis
也支持将SQL语句写在抽象方法的@lnsert
等注解中,但不推荐Mybatis
也可以使用,但需要编写较多配置#{}
格式的占位符,其实现本质是通过PreparedStatement
实现了SQL语句的预编译
节点编写代码片段,在其它位置可以复用
节点,以配置字段名与属性名的映射
、
等标签使用动态SQLMybatis
的缺点,主要集中在“需要编写SQL语句”方面,它确实导致了编写代码量较多、数据库移植性较差的问题,但是,这也是缺点的同时,也是优点。SQL语句的执行效率是非常重要的,由框架生成的SQL语句都是模版化的,其执行效率必然低于自定义SQL语句的执行效率,所以,“不需要编写SQL语句”虽然可以减少编写的代码量,也可以解决数据库移植性的问题,但却带米了“执仃效率偏低”的问题@Query
注解自定义SQL语句,为了保证执行效率,使用JPA时也应该自行编写SQL语句,其它可以不需要编写SQL语句的数据库编程框架也大多是这样baomidou
开发了Mybatis-Plus
框架,很好的解决了Mybatis
的缺点,你只需要将你使用Mybatis
时创建的接口继承自该框架的接口,并添加简单的配置(关于Mybaits-Plus
的配置),即可得到常规的数据访问功能。Mybaits-Plus
在追求编码效率时,完全不需要编写常规的数据访问功能,在追求执行效率时,依然按照Mybatis
的使用方式即可,开发人员可以自主选择Mybatis
的优点有:
Mybatis
的缺点有:
Mybatis
的缺点并不是严重问题,编写SQL语句的工作量,在整个项目开发中占比并不大,一般也极少更换数据库,并且,这个缺点可以通过Mybatis-Plus
来弥补,在实际应用中,简单的数据访问功能直接使用Mybatis-Plus
提供的功能即可,完全不需要自行编写,相比复杂的数据访问则自行编写都写在SQL语句中,都用于表示参数值
<select id="findById" resultMap="BaseResultMap">
select * from user where id=#{id}
select>
<select id="findById" resultMap="BaseResultMap">
select * from user where id=${id}
select>
SQL语句中存在一些参数,在实际运行时,传入的参数值可能改变语义,导致执行的SOL语句与预期的(设计的)不同
select * from user where id=?
Mybatis
在处理SQL语句中的#{}
占位符时,会将其替换成问号,并通过预编译的方式(使用PreparedStatement
)进行处理的
Mybatis
在处理SQL语句中的${}
占位符时,是单纯的把占位符替换成参数值,再进行后续的处理
#{}
格式的占位符将被替换为问号,是通过预编译进行处理的,所以,使用#{}
只能表示某个值,而不能表示SQL语句中的某个片段,同时,由于是预编译的,所以不需要考虑参数值的数据类型问题,也没有SQL注入风险${}
格式的占位符将直接被替换为参数值,所以,只要保证最终得到的SQL语句是合法的,它可以表示SQL语句中的任何片段,但是,需要考虑参数值中涉及的各值的数据类型,例如字符串类型的值需要在两侧添加单引号,并且,由于在编译SQL语句之前就已经将参数值添加到SQL语句中了,需要考虑SQL注入的风险${}
格式的占位符有SQL注入风险,如果执行数据访问之前没有对参数进行完整的检验,是不安全的,并且,还要考虑各值的数据类型问题,相对麻烦,所以,一般使用#{}
格式的占位符。Spring Boot
是基于Spring
框架的,理论上来说,Spring
框架已经定义的注解,不应该归类于“Spring Boot的核心注解”
@Configuration
、@Autowired
等Spring Boot
没有添加Web
的starter
时,并不包含Spring MVC
框架的相关技术,所以,Spring MVC
框架定义的注解,也不应该归类于“Spring Boot的核心注解”
@RestController
、@GetMapping
等starter
才被集成的框架也是同理@SpringBootApplication
@SpringBootApplication
:每个Spring Boot
项目中的启动类都应该添加@SpringBootApplication
注解。每个基于Spring Boot
的项目或Module
应该有且仅有1个类添加该注解
@SpringBootApplication
public class SpringBootDemoApplication {}
注意:@SpringBootConfiguration
、@EnableAutoConfiguration
也应该归类为Spring Boot
特有注解
@SpringBootTest
在Spring Boot
项目的每个测试类之前都应该添加@SpringBootTest
注解,在执行测试(执行整个测试类,或任何一个测试方法)之前,都会加载Spring Boot
的自动配置、自定义配置,在执行测试之后,会释放这些资源。如果测试不需要加载Spring Boot
的自动配置(包括自定义配置),则不需要添加该注解
Spring Boot的
核心注解有:
@SpringBootApplication
Spring Boot
的项目或Module
应该有且仅有1个类添加该注解@SpringBootConfiguration
@SpringBootApplication
的元注解。@Configuration
,使得添加了@SpringBootApplication
注解的类是启动类的同时还是配置类@EnableAutoConfiguration
@SpringBootTest
Spring Boot
项目的每个测试类之前都应该添加@SpringBootTest
注解,在执行测试(执行整个测试类,或任何一个测试方法)之前,都会加载SpringBoot
的自动配置、自定义配置,在执行测试之后,会释放这些资源Spring Boot
的自动配置(包括自定义配置),则不需要添加该注解Properties
)ApplicationContext
组件类Web
测试环境基于Spring / Spring MVC
等基础框架的项目,在创建出来之后,在编写代码之前,需要完成许多配置,在Spring Boot
中,设计了许多starter
,用于整合SpringBoot
和其它基础框架,完成通用配置,并且,当启动Spring Boot
项目时,会自动加载这些配置,使得各框架“开箱即用”
starter
既包含了所使用的依赖,也包含了通用配置Mybatis
为例,在使用Spring
框架进行整合时,需要自行配置DataSource
、SqlSessionFactoryBean
等,在Spring Boot
中,添加了对应的starter
之后,不必自行配置,甚至其它配置(例如连接数据库的参数)也只需要按照指定的属性名称来配置值,并不需要自行读取配置如果在面试时,面试官出了这道题,其考察的目标应该是“你用过哪些starter”,以了解你在开发时使用到了哪些技术
spring-boot-starter-web
:用于整合Spring MVC
spring-boot-starter-test
:用于整合JUnit
及相关测试环境spring-boot-starter-freemarker
:使用Mybatis Plus Generator
时将需要spring-boot-starter-validation
:用于整合Hibernate Validator
来检验请求参数的有效性spring-boot-starter-thymeleaf
:用于整合Thymeleaf
,仅当“非响应正文”时使用spring-boot-starter-security
:用于整合Spring Security
spring-boot-starter-data-redis
:用于整合Spring Data Redis
,处理项目中使用Redis
缓存数据spring-boot-starter-data-elasticsearch
:用于整合Spring Data ElasticSearch
,处理项目中使用ElasticSearch
实现搜索功能Spring Cloud
相关框架的starter
:
spring-cloud-starter-netflix-eureka-server
:用于整合Spring Cloud
中的Eureka
服务器端spring-cloud-starter-netflix-eureka-client
:用于整合Spring Cloud
中的Eureka
客户端Eureka
,请更换为你使用的,如Nacos
spring-cloud-starter-netflix-zuul
:用于整合Spring Cloud
中的Zuul
,实现网关路由等功能Zuul
,请更换为你使用的,如Gateway
mybatis-spring-boot-starter
:用于整合Mybatis
,由于不是Spring Boot
团队开发的,所以命名风格略有不同pagehelper-spring-boot-starter
:用于整合Page Helper
,处理Mybatis
查询分页,由于不是Spring Boot
团队开发的,所以命名风格略有不同