⼀句话概括:Spring 是⼀个轻量级、⾮⼊侵式的控制反转 (IoC) 和⾯向切⾯ (AOP) 的框架。
2003
年,⼀个⾳乐家
Rod Johnson
决定发展⼀个轻量级的
Java
开发框架,
Spring
作为
Java
战场
的龙骑兵渐渐崛起,并淘汰了
EJB
这个传统的重装骑兵。
1.
IOC
和
DI
的⽀持
Spring
的核⼼就是⼀个⼤的⼯⼚容器,可以维护所有对象的创建和依赖关系,
Spring
⼯⼚⽤
于⽣成
Bean
,并且管理
Bean
的⽣命周期,实现
⾼内聚低耦合
的设计理念。
2.
AOP
编程的⽀持
Spring
提供了
⾯向切⾯编程
,可以⽅便的实现对程序进⾏权限拦截、运⾏监控等切⾯功能。
3.
声明式事务的⽀持
⽀持通过配置就来完成对事务的管理,⽽不需要通过硬编码的⽅式,以前重复的⼀些事务提
交、回滚的
JDBC
代码,都可以不⽤⾃⼰写了。
4.
快捷测试的⽀持
Spring
对
Junit
提供⽀持,可以通过
注解
快捷地测试
Spring
程序。
5.
快速集成功能
⽅便集成各种优秀框架,
Spring
不排斥各种优秀的开源框架,其内部提供了对各种优秀框架
(如:
Struts
、
Hibernate
、
MyBatis
、
Quartz
等)的直接⽀持。
6.
复杂
API
模板封装
Spring
对
JavaEE
开发中⾮常难⽤的⼀些
API
(
JDBC
、
JavaMail
、远程调⽤等)都提供了模板
化的封装,这些封装
API
的提供使得应⽤难度⼤⼤降低。
2.Spring
有哪些模块呢?
Spring
框架是分模块存在,除了最核⼼的
Spring Core Container
是必要模块之外,其他
模块都是
可选
,⼤约有
20
多个模块。
最主要的七⼤模块:
1.
Spring Core
:
Spring
核⼼,它是框架最基础的部分,提供
IOC
和依赖注⼊
DI
特性。
2.
Spring Context
:
Spring
上下⽂容器,它是
BeanFactory
功能加强的⼀个⼦接⼜。
3.
Spring Web
:它提供
Web
应⽤开发的⽀持。
4.
Spring MVC
:它针对
Web
应⽤中
MVC
思想的实现。
5.
Spring DAO
:提供对
JDBC
抽象层,简化了
JDBC
编码,同时,编码更具有健壮性。
6.
Spring ORM
:它⽀持⽤于流⾏的
ORM
框架的整合,⽐如:
Spring + Hibernate
、
Spring +
iBatis
、
Spring + JDO
的整合等。
7.
Spring AOP
:即⾯向切⾯编程,它提供了与
AOP
联盟兼容的编程实现。
Web
:
@Controller
:组合注解(组合了
@Component
注解),应⽤在
MVC
层(控制层)。
@RestController
:该注解为⼀个组合注解,相当于
@Controller
和
@ResponseBody
的组合,
注解在类上,意味着,该
Controller
的所有⽅法都默认加上了
@ResponseBody
。
@RequestMapping
:⽤于映射
Web
请求,包括访问路径和参数。如果是
Restful
风格接⼜,
还可以根据请求类型使⽤不同的注解:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@ResponseBody
:⽀持将返回值放在
response
内,⽽不是⼀个页⾯,通常⽤户返回
json
数
据。
@RequestBody
:允许
request
的参数在
request
体中,⽽不是在直接连接在地址后⾯。
@PathVariable
:⽤于接收路径参数,⽐如
@RequestMapping(“/hello/{name}”)
申明的路径,
将注解放在参数中前,即可获取该值,通常作为
Restful
的接⼜实现⽅法。
@RestController
:该注解为⼀个组合注解,相当于
@Controller
和
@ResponseBody
的组合,
注解在类上,意味着,该
Controller
的所有⽅法都默认加上了
@ResponseBody
。
容器
:
@Component
:表⽰⼀个带注释的类是⼀个
“
组件
”
,成为
Spring
管理的
Bean
。当使⽤基于
注解的配置和类路径扫描时,这些类被视为⾃动检测的候选对象。同时
@Component
还是
⼀个元注解。
@Service
:组合注解(组合了
@Component
注解),应⽤在
service
层(业务逻辑层)。
@Repository
:组合注解(组合了
@Component
注解),应⽤在
dao
层(数据访问层)。
@Autowired
:
Spring
提供的⼯具(由
Spring
的依赖注⼊⼯具(
BeanPostProcessor
、
BeanFactoryPostProcessor
)⾃动注⼊)。
@Qualifier
:该注解通常跟
@Autowired
⼀起使⽤,当想对注⼊的过程做更多的控制,
@Qualifier
可帮助配置,⽐如两个以上相同类型的
Bean
时
Spring
⽆法抉择,⽤到此注解
@Configuration
:声明当前类是⼀个配置类(相当于⼀个
Spring
配置的
xml
⽂件)
@Value
:可⽤在字段,构造器参数跟⽅法参数,指定⼀个默认值,⽀持
#{}
跟
${}
两个
⽅式。⼀般将
SpringbBoot
中的
application.properties
配置的属性值赋值给变量。
@Bean
:注解在⽅法上,声明当前⽅法的返回值为⼀个
Bean
。返回的
Bean
对应的类中可
以定义
init()
⽅法和
destroy()
⽅法,然后在
@Bean(initMethod=”init”,destroyMethod=”destroy”)
定义,在构造之后执⾏
init
,在销毁之前执⾏
destroy
。
@Scope:
定义我们采⽤什么模式去创建
Bean
(⽅法上,得有
@Bean
) 其设置类型包括:
Singleton
、
Prototype
、
Request
、
Session
、
GlobalSession
。
AOP
:
@Aspect:
声明⼀个切⾯(类上) 使⽤
@After
、
@Before
、
@Around
定义建⾔(
advice
),
可直接将拦截规则(切点)作为参数。
@After
:在⽅法执⾏之后执⾏(⽅法上)。
@Before
: 在⽅法执⾏之前执⾏(⽅法上)。
@Around
: 在⽅法执⾏之前与之后执⾏(⽅法上)。
@PointCut
: 声明切点 在
java
配置类中使⽤
@EnableAspectJAutoProxy
注解开启
Spring
对
AspectJ
代理的⽀持(类上)。
事务:
@Transactional
:在要开启事务的⽅法上使⽤
@Transactional
注解,即可声明式开启事务。
1.
⼯⼚模式
: Spring
容器本质是⼀个⼤⼯⼚,使⽤⼯⼚模式通过
BeanFactory
、
ApplicationContext
创建
bean
对象。
2.
代理模式
: Spring AOP
功能功能就是通过代理模式来实现的,分为动态代理和静态代
理。
3.
单例模式
: Spring
中的
Bean
默认都是单例的,这样有利于容器对
Bean
的管理。
4.
模板模式
: Spring
中
JdbcTemplate
、
RestTemplate
等以
Template
结尾的对数据库、⽹络等
等进⾏操作的模板类,就使⽤到了模板模式。
5.
观察者模式
: Spring
事件驱动模型就是观察者模式很经典的⼀个应⽤。
6.
适配器模式
:Spring AOP
的增强或通知
(Advice)
使⽤到了适配器模式、
Spring MVC
中也
是⽤到了适配器模式适配
Controller
。
7.
策略模式 :
Spring
中有⼀个
Resource
接⼜,它的不同实现类,会根据不同的策略去访问资
源。
IOC
5.
说⼀说什么是
IOC
?什么是
DI?
Java
是⾯向对象的编程语⾔,⼀个个实例对象相互合作组成了业务逻辑,原来,我们都是在
代码⾥创建对象和对象的依赖。
所谓的
IOC
(控制反转):就是由容器来负责控制对象的⽣命周期和对象间的关系。以前是
我们想要什么,就⾃⼰创建什么,现在是我们需要什么,容器就给我们送来什么。
也就是说,控制对象⽣命周期的不再是引⽤它的对象,⽽是容器。对具体对象,以前是它控
制其它对象,现在所有对象都被容器控制,所以这就叫
控制反转
。
DI
(依赖注⼊)
:指的是容器在实例化对象的时候把它依赖的类注⼊给它。有的说法
IOC
和
DI
是⼀回事,有的说法是
IOC
是思想,
DI
是
IOC
的实现。
为什么要使⽤
IOC
呢?
最主要的是两个字
解耦
,硬编码会造成对象间的过度耦合,使⽤
IOC
之后,我们可以不⽤关
⼼对象间的依赖,专⼼开发应⽤就⾏。
6.
能简单说⼀下
Spring IOC
的实现机制吗?
PS:
这道题⽼三在⾯试中被问到过,问法是
“
你有⾃⼰实现过简单的
Spring
吗?
”
Spring
的
IOC
本质就是⼀个⼤⼯⼚,我们想想⼀个⼯⼚是怎么运⾏的呢?
Bean
定义:
Bean
通过⼀个配置⽂件定义,把它解析成⼀个类型。
beans.properties
偷懒,这⾥直接⽤了最⽅便解析的
properties
,这⾥直接⽤⼀个
类型的配置来代
表
Bean
的定义,其中
key
是
beanName
,
value
是
class
userDao
:
cn
.
fighter3
.
bean
.
UserDao
BeanDefinition.java
bean
定义类,配置⽂件中
bean
定义对应的实体
public class
BeanDefinition
{
private
String
beanName
;
private
Class beanClass
;
//
省略
getter
、
setter
}
ResourceLoader.java
资源加载器,⽤来完成配置⽂件中配置的加载
public class
ResourceLoader
{
public static
Map
<
String
,
BeanDefinition
>
getResource
() {
Map
<
String
,
BeanDefinition
>
beanDefinitionMap
=
new
HashMap
<>
(
16
);
Properties properties
=
new
Properties
();
try
{
InputStream inputStream
=
ResourceLoader
.
class
.
getResourceAsStream
(
"/beans.properties"
);
properties
.
load
(
inputStream
);
Iterator
<
String
>
it
=
properties
.
stringPropertyNames
().
iterator
();
while
(
it
.
hasNext
()) {
String
key
=
it
.
next
();
String
className
=
properties
.
getProperty
(
key
);
BeanDefinition beanDefinition
=
new
BeanDefinition
();
beanDefinition
.
setBeanName
(
key
);
Class clazz
=
Class
.
forName
(
className
);
beanDefinition
.
setBeanClass
(
clazz
);
beanDefinitionMap
.
put
(
key
,
beanDefinition
);
}
inputStream
.
close
();
}
catch
(
IOException
|
ClassNotFoundException e
) {
e
.
printStackTrace
();
}
return
beanDefinitionMap
;
}
}
BeanRegister.java
对象注册器,这⾥⽤于单例
bean
的缓存,我们⼤幅简化,默认所有
bean
都是单例的。可以
看到所谓单例注册,也很简单,不过是往
HashMap
⾥存对象。
public class
BeanRegister
{
//
单例
Bean
缓存
private
Map
<
String
,
Object
>
singletonMap
=
new
HashMap
<>
(
32
);
/*
*
获取单例
Bean
*
* @param beanName bean
名称
* @return
*/
public
Object
getSingletonBean
(
String
beanName
) {
return
singletonMap
.
get
(
beanName
);
}
/**
*
注册单例
bean
*
* @param beanName
* @param bean
*/
public
void
registerSingletonBean
(
String
beanName
,
Object
bean
) {
if
(
singletonMap
.
containsKey
(
beanName
)) {
return
;
}
singletonMap
.
put
(
beanName
,
bean
);
}
}
对象⼯⼚,我们最
核⼼
的⼀个类,在它初始化的时候,创建了
bean
注册器,完成了资
源的加载。
获取
bean
的时候,先从单例缓存中取,如果没有取到,就创建并注册⼀个
bean
7.
说说
BeanFactory
和
ApplicantContext?
可以这么形容,
BeanFactory
是
Spring
的
“
⼼脏
”
,
ApplicantContext
是完整的
“
⾝躯
”
。
BeanFactory
(
Bean
⼯⼚)是
Spring
框架的基础设施,⾯向
Spring
本⾝。
ApplicantContext
(应⽤上下⽂)建⽴在
BeanFactoty
基础上,⾯向使⽤
Spring
框架的开发
者。
BeanFactory
接⼜
BeanFactory
是类的通⽤⼯⼚,可以创建并管理各种类的对象。
Spring
为
BeanFactory
提供了很多种实现,最常⽤的是
XmlBeanFactory
,但在
Spring 3.2
中已被
废弃,建议使⽤
XmlBeanDefinitionReader
、
DefaultListableBeanFactory
。
BeanFactory
接⼜位于类结构树的顶端,它最主要的⽅法就是
getBean(String var1)
,这个⽅法从
容器中返回特定名称的
Bean
。
BeanFactory
的功能通过其它的接⼜得到了不断的扩展,⽐如
AbstractAutowireCapableBeanFactory
定义了将容器中的
Bean
按照某种规则(⽐如按名字匹配、
按类型匹配等)进⾏⾃动装配的⽅法。
这⾥看⼀个
XMLBeanFactory
(已过期) 获取
bean
的例⼦:
public class
HelloWorldApp
{
public static
void
main(
String
[] args) {
BeanFactory factory
=
new
XmlBeanFactory (
new
ClassPathResource(
"beans.xml"
));
HelloWorld obj
=
(HelloWorld) factory.getBean(
"helloWorld"
);
obj.getMessage();
}
}
ApplicationContext
接⼜
ApplicationContext
由
BeanFactory
派⽣⽽来,提供了更多⾯向实际应⽤的功能。可以这么说,
使⽤
BeanFactory
就是⼿动档,使⽤
ApplicationContext
就是⾃动档。
ApplicationContext
继承了
HierachicalBeanFactory
和
ListableBeanFactory
接⼜,在此基础上,还
通过其他的接⼜扩展了
BeanFactory
的功能,包括:
Bean instantiation/wiring
Bean
的实例化
/
串联
⾃动的
BeanPostProcessor
注册
⾃动的
BeanFactoryPostProcessor
注册
⽅便的
MessageSource
访问(
i18n
)
ApplicationEvent
的发布与
BeanFactory
懒加载的⽅式不同,它是预加载,所以,每⼀个
bean
都在
ApplicationContext
启动之后实例化
这是
ApplicationContext
的使⽤例⼦:
8.
你知道
Spring
容器启动阶段会⼲什么吗?
PS
:这道题⽼三⾯试被问到过
Spring
的
IOC
容器⼯作的过程,其实可以划分为两个阶段:
容器启动阶段
和
Bean
实例化阶
段
。
其中容器启动阶段主要做的⼯作是加载和解析配置⽂件,保存到对应的
Bean
定义中。
容器启动开始,⾸先会通过某种途径加载
Congiguration MetaData
,在⼤部分情况下,容器需
要依赖某些⼯具类(
BeanDefinitionReader
)对加载的
Congiguration MetaData
进⾏解析和分
析,并将分析后的信息组为相应的
BeanDefinition
。
最后把这些保存了
Bean
定义必要信息的
BeanDefinition
,注册到相应的
BeanDefinitionRegistry
,
这样容器启动就完成了。
9.
能说⼀下
Spring Bean
⽣命周期吗?
可以看看:
Spring Bean
⽣命周期,好像⼈的⼀⽣。。
在
Spring
中,基本容器
BeanFactory
和扩展容器
ApplicationContext
的实例化时机不太⼀样,
BeanFactory
采⽤的是延迟初始化的⽅式,也就是只有在第⼀次
getBean()
的时候,才会实例化
Bean
;
ApplicationContext
启动之后会实例化所有的
Bean
定义。
Spring IOC
中
Bean
的⽣命周期⼤致分为四个阶段:
实例化
(
Instantiation
)、
属性赋值
(
Populate
)、
初始化
(
Initialization
)、
销毁
(
Destruction
)。
我们再来看⼀个稍微详细⼀些的过程:
实例化 :第
1
步,实例化⼀个
Bean
对象
属性赋值 :第
2
步,为
Bean
设置相关属性和依赖
初始化 :初始化的阶段的步骤⽐较多,
5
、
6
步是真正的初始化,第
3
、
4
步为在初始化前
执⾏,第
7
步在初始化后执⾏,初始化完成之后,
Bean
就可以被使⽤了
销毁 :第
8~10
步,第
8
步其实也可以算到销毁阶段,但不是真正意义上的销毁,⽽是先
在使⽤前注册了销毁的相关调⽤接⼜,为了后⾯第
9
、
10
步真正销毁
Bean
时再执⾏相应的
⽅法
10.Bean
定义和依赖定义有哪些⽅式?
有三种⽅式:
直接编码⽅式
、
配置⽂件⽅式
、
注解⽅式
。
直接编码⽅式:我们⼀般接触不到直接编码的⽅式,但其实其它的⽅式最终都要通过直接
编码来实现。
配置⽂件⽅式:通过
xml
、
propreties
类型的配置⽂件,配置相应的依赖关系,
Spring
读取
配置⽂件,完成依赖关系的注⼊。
注解⽅式:注解⽅式应该是我们⽤的最多的⼀种⽅式了,在相应的地⽅使⽤注解修饰,
Spring
会扫描注解,完成依赖关系的注⼊。
11.
有哪些依赖注⼊的⽅法?
Spring
⽀持
构造⽅法注⼊
、
属性注⼊
、
⼯⼚⽅法注⼊
,
其中⼯⼚⽅法注⼊,又可以分为
静态⼯
⼚⽅法注⼊
和
⾮静态⼯⼚⽅法注⼊
。
什么是⾃动装配?
Spring IOC
容器知道所有
Bean
的配置信息,此外,通过
Java
反射机制还可以获知实现类的结构
信息,如构造⽅法的结构、属性等信息。掌握所有
Bean
的这些信息后,
Spring IOC
容器就可以
按照某种规则对容器中的
Bean
进⾏⾃动装配,⽽⽆须通过显式的⽅式进⾏依赖配置。
Spring
提供的这种⽅式,可以按照某些规则进⾏
Bean
的⾃动装配,元素提供了⼀个指定⾃动装
配类型的属性:
autowire="<
⾃动装配类型
>"
byName
:根据名称进⾏⾃动匹配,假设
Boss
又⼀个名为
car
的属性,如果容器中刚好有⼀
个名为
car
的
bean
,
Spring
就会⾃动将其装配给
Boss
的
car
属性
byType
:根据类型进⾏⾃动匹配,假设
Boss
有⼀个
Car
类型的属性,如果容器中刚好有⼀
个
Car
类型的
Bean
,
Spring
就会⾃动将其装配给
Boss
这个属性
constructor
:与
byType
类似, 只不过它是针对构造函数注⼊⽽⾔的。如果
Boss
有⼀个构
造函数,构造函数包含⼀个
Car
类型的⼊参,如果容器中有⼀个
Car
类型的
Bean
,则
Spring
将⾃动把这个
Bean
作为
Boss
构造函数的⼊参;如果容器中没有找到和构造函数⼊参匹配类
型的
Bean
,则
Spring
将抛出异常。
autodetect
:根据
Bean
的⾃省机制决定采⽤
byType
还是
constructor
进⾏⾃动装配,如果
Bean
提供了默认的构造函数,则采⽤
byType
,否则采⽤
constructor
。
singleton :
在
Spring
容器仅存在⼀个
Bean
实例,
Bean
以单实例的⽅式存在,是
Bean
默认的
作⽤域。
prototype :
每次从容器重调⽤
Bean
时,都会返回⼀个新的实例。
以下三个作⽤域于只在
Web
应⽤中适⽤:
request :
每⼀次
HTTP
请求都会产⽣⼀个新的
Bean
,该
Bean
仅在当前
HTTP Request
内有
效。
session :
同⼀个
HTTP Session
共享⼀个
Bean
,不同的
HTTP Session
使⽤不同的
Bean
。
globalSession
:同⼀个全局
Session
共享⼀个
Bean
,只⽤于基于
Protlet
的
Web
应⽤,
Spring5
中已经不存在了。
14.Spring
中的单例
Bean
会存在线程安全问题吗?
⾸先结论在这:
Spring
中的单例
Bean
不是线程安全的
。
因为单例
Bean
,是全局只有⼀个
Bean
,所有线程共享。如果说单例
Bean
,是⼀个⽆状态的,
也就是线程中的操作不会对
Bean
中的成员变量执⾏
查询
以外的操作,那么这个单例
Bean
是线
程安全的。⽐如
Spring mvc
的
Controller
、
Service
、
Dao
等,这些
Bean
⼤多是⽆状态的,只关
注于⽅法本⾝。
假如这个
Bean
是有状态的,也就是会对
Bean
中的成员变量进⾏写操作,那么可能就存在线程
安全的问题。
单例
Bean
线程安全问题怎么解决呢?
常见的有这么些解决办法:
1.
将
Bean
定义为多例
这样每⼀个线程请求过来都会创建⼀个新的
Bean
,但是这样容器就不好管理
Bean
,不能
这么办。
2.
在
Bean
对象中尽量避免定义可变的成员变量
削⾜适履了属于是,也不能这么⼲。
3.
将
Bean
中的成员变量保存在
ThreadLocal
中
⭐
我们知道
ThredLoca
能保证多线程下变量的隔离,可以在类中定义⼀个
ThreadLocal
成员变
量,将需要的可变成员变量保存在
ThreadLocal
⾥,这是推荐的⼀种⽅式。
只有单例的
Bean
才存在循环依赖的情况,
原型
(Prototype)
情况下,
Spring
会直接抛出异常。原
因很简单,
AB
循环依赖,
A
实例化的时候,发现依赖
B
,创建
B
实例,创建
B
的时候发现需要
A
,创建
A1
实例
……
⽆限套娃,直接把系统⼲垮。
Spring
可以解决哪些情况的循环依赖?
Spring
不⽀持基于构造器注⼊的循环依赖,但是假如
AB
循环依赖,如果⼀个是构造器注⼊,
⼀个是
setter
注⼊呢?
看看⼏种情形:
第四种可以⽽第五种不可以的原因是
Spring
在创建
Bean
时默认会根据⾃然排序进⾏创建,
所以
A
会先于
B
进⾏创建。
所以简单总结,当循环依赖的实例都采⽤
setter
⽅法注⼊的时候,
Spring
可以⽀持,都采⽤构造
器注⼊的时候,不⽀持,构造器注⼊和
setter
注⼊同时存在的时候,看天。
16.
那
Spring
怎么解决循环依赖的呢?
PS
:其实正确答案是开发⼈员做好设计,别让
Bean
循环依赖,但是没办法,⾯试官不想
听这个。
我们都知道,单例
Bean
初始化完成,要经历三步:
注⼊就发⽣在第⼆步,
属性赋值
,结合这个过程,
Spring
通过
三级缓存
解决了循环依赖:
1.
⼀级缓存
: Map singletonObjects
,单例池,⽤于保存实例化、属性赋值
(注⼊)、初始化完成的
bean
实例
2.
⼆级缓存
: Map earlySingletonObjects
,早期曝光对象,⽤于保存实例化完
成的
bean
实例
3.
三级缓存
: Map> singletonFactories
,早期曝光对象⼯⼚,⽤于保
存
bean
创建⼯⼚,以便于后⾯扩展有机会创建代理对象。
我们来看⼀下三级缓存解决循环依赖的过程:
当
A
、
B
两个类发⽣循环依赖时:
A
实例的初始化过程:
1.
创建
A
实例,实例化的时候把
A
对象⼯⼚放⼊三级缓存,表⽰
A
开始实例化了,虽然我这
个对象还不完整,但是先曝光出来让⼤家知道
2.
A
注⼊属性时,发现依赖
B
,此时
B
还没有被创建出来,所以去实例化
B
3.
同样,
B
注⼊属性时发现依赖
A
,它就会从缓存⾥找
A
对象。依次从⼀级到三级缓存查询
A
,从三级缓存通过对象⼯⼚拿到
A
,发现
A
虽然不太完善,但是存在,把
A
放⼊⼆级缓
存,同时删除三级缓存中的
A
,此时,
B
已经实例化并且初始化完成,把
B
放⼊⼀级缓存。
4.
接着
A
继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的
B
对象,
A
对象创建也
完成,删除⼆级缓存中的
A
,同时把
A
放⼊⼀级缓存
5.
最后,⼀级缓存中保存着实例化、初始化都完成的
A
、
B
对象
所以,我们就知道为什么
Spring
能解决
setter
注⼊的循环依赖了,因为实例化和属性赋值是分开
的,所以⾥⾯有操作的空间。如果都是构造器注⼊的化,那么都得在实例化这⼀步完成注
⼊,所以⾃然是⽆法⽀持了。
17.
为什么要三级缓存?⼆级不⾏吗?
不⾏,主要是为了
⽣成代理对象
。如果是没有代理的情况下,使⽤⼆级缓存解决循环依赖也
是
OK
的。但是如果存在代理,三级没有问题,⼆级就不⾏了。
因为三级缓存中放的是⽣成具体对象的匿名内部类,获取
Object
的时候,它可以⽣成代理对
象,也可以返回普通对象。使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对
象。
假设只有⼆级缓存的情况,往⼆级缓存中放的显⽰⼀个普通的
Bean
对象,
Bean
初始化过程
中,通过
BeanPostProcessor
去⽣成代理对象之后,覆盖掉⼆级缓存中的普通
Bean
对象,那么
可能就导致取到的
Bean
对象不⼀致了。
19.
说说什么是
AOP
?
AOP
:⾯向切⾯编程。简单说,就是把⼀些业务逻辑中的相同的代码抽取到⼀个独⽴的模块
中,让业务逻辑更加清爽。
我们可以把
⽇志记录
和
数据校验
可重⽤的功能模块分离出来,然后在程序的执⾏的合适的地
⽅动态地植⼊这些代码并执⾏。这样就简化了代码的书写。
业务逻辑代码中没有参和通⽤逻辑的代码,业务模块更简洁,只包含核⼼业务代码。实现了
业务逻辑和通⽤逻辑的代码分离,便于维护和升级,降低了业务逻辑和通⽤逻辑的耦合性。
AOP
可以将遍布应⽤各处的功能分离出来形成可重⽤的组件。在编译期间、装载期间或运⾏
期间实现在不修改源代码的情况下给程序动态添加功能。从⽽实现对业务逻辑的隔离,提⾼
代码的模块化能⼒。
AOP
的核⼼其实就是
动态代理
,如果是实现了接⼜的话就会使⽤
JDK
动态代理,否则使⽤
CGLIB
代理,主要应⽤于处理⼀些具有横切性质的系统级服务,如⽇志收集、事务管理、安
全检查、缓存、对象池管理等。
AOP
有哪些核⼼概念?
切⾯
(
Aspect
):类是对物体特征的抽象,切⾯就是对横切关注点的抽象
连接点
(
Joinpoint
):被拦截到的点,因为
Spring
只⽀持⽅法类型的连接点,所以在
Spring
中连接点指的就是被拦截到的⽅法,实际上连接点还可以是字段或者构造器
切点
(
Pointcut
):对连接点进⾏拦截的定位
通知
(
Advice
):所谓通知指的就是指拦截到连接点之后要执⾏的代码,也可以称作
增
强
⽬标对象
(
Target
):代理的⽬标对象
织⼊
(
Weabing
):织⼊是将增强添加到⽬标类的具体连接点上的过程。
编译期织⼊:切⾯在⽬标类编译时被织⼊
类加载期织⼊:切⾯在⽬标类加载到
JVM
时被织⼊。需要特殊的类加载器,它可以在
⽬标类被引⼊应⽤之前增强该⽬标类的字节码。
运⾏期织⼊:切⾯在应⽤运⾏的某个时刻被织⼊。⼀般情况下,在织⼊切⾯时,
AOP
容器会为⽬标对象动态地创建⼀个代理对象。
SpringAOP
就是以这种⽅式织⼊切⾯。
Spring
采⽤运⾏期织⼊,⽽
AspectJ
采⽤编译期织⼊和类加载器织⼊。
引介
(
introduction
):引介是⼀种特殊的增强,可以动态地为类添加⼀些属性和⽅法
AOP
有哪些环绕⽅式?
AOP
⼀般有
5
种
环绕⽅式:
前置通知
(@Before)
返回通知
(@AfterReturning)
异常通知
(@AfterThrowing)
后置通知
(@After)
环绕通知
(@Around)
多个切⾯的情况下,可以通过
@Order
指定先后顺序,数字越⼩,优先级越⾼。
21.
说说
JDK
动态代理和
CGLIB
代理 ?
Spring
的
AOP
是通过
动态代理
来实现的,动态代理主要有两种⽅式
JDK
动态代理和
Cglib
动态
代理,这两种动态代理的使⽤和原理有些不同。
JDK
动态代理
1.
Interface
:对于
JDK
动态代理,⽬标类需要实现⼀个
Interface
。
2.
InvocationHandler
:
InvocationHandler
是⼀个接⼜,可以通过实现这个接⼜,定义横切逻
辑,再通过反射机制(
invoke
)调⽤⽬标类的代码,在次过程,可能包装逻辑,对⽬标⽅
法进⾏前置后置处理。
3.
Proxy
:
Proxy
利⽤
InvocationHandler
动态创建⼀个符合⽬标类实现的接⼜的实例,⽣成⽬
标类的代理对象。
CgLib
动态代理
1.
使⽤
JDK
创建代理有⼀⼤限制,它只能为接⼜创建代理实例,⽽
CgLib
动态代理就没有这
个限制。
2.
CgLib
动态代理是使⽤字节码处理框架
ASM
,其原理是通过字节码技术为⼀个类创建⼦
类,并在⼦类中采⽤⽅法拦截的技术拦截所有⽗类⽅法的调⽤,顺势织⼊横切逻辑。
3.
CgLib
创建的动态代理对象性能⽐
JDK
创建的动态代理对象的性能⾼不少,但是
CGLib
在创建代理对象时所花费的时间却⽐
JDK
多得多,所以对于单例的对象,因为⽆需频繁
创建对象,⽤
CGLib
合适,反之,使⽤
JDK
⽅式要更为合适⼀些。同时,由于
CGLib
由
于是采⽤动态创建⼦类的⽅法,对于
final
⽅法,⽆法进⾏代理。
22.
说说
Spring AOP
和
AspectJ AOP
区别
?
Spring AOP
Spring AOP
属于
运⾏时增强
,主要具有如下特点:
1.
基于动态代理来实现,默认如果使⽤接⼜的,⽤
JDK
提供的动态代理实现,如果是⽅法
则使⽤
CGLIB
实现
2.
Spring AOP
需要依赖
IOC
容器来管理,并且只能作⽤于
Spring
容器,使⽤纯
Java
代码实
现
3.
在性能上,由于
Spring AOP
是基于 动态代理 来实现的,在容器启动时需要⽣成代理实
例,在⽅法调⽤上也会增加栈的深度,使得
Spring AOP
的性能不如
AspectJ
的那么好。
4.
Spring AOP
致⼒于解决企业级开发中最普遍的
AOP(
⽅法织⼊
)
。
AspectJ
AspectJ
是⼀个易⽤的功能强⼤的
AOP
框架,属于
编译时增强
, 可以单独使⽤,也可以整合
到其它框架中,是
AOP
编程的完全解决⽅案。
AspectJ
需要⽤到单独的编译器
ajc
。
AspectJ
属于
静态织⼊
,通过修改代码来实现,在实际运⾏之前就完成了织⼊,所以说它⽣成
的类是没有额外运⾏时开销的,⼀般有如下⼏个织⼊的时机:
1.
编译期织⼊(
Compile-time weaving
):如类
A
使⽤
AspectJ
添加了⼀个属性,类
B
引⽤
了它,这个场景就需要编译期的时候就进⾏织⼊,否则没法编译类
B
。
2.
编译后织⼊(
Post-compile weaving
):也就是已经⽣成了
.class
⽂件,或已经打成
jar
包
了,这种情况我们需要增强处理的话,就要⽤到编译后织⼊。
3.
类加载后织⼊(
Load-time weaving
):指的是在加载类的时候进⾏织⼊,要实现这个时期
的织⼊,有⼏种常见的⽅法
事务
Spring
事务的本质其实就是数据库对事务的⽀持,没有数据库的事务⽀持,
Spring
是⽆法提
供事务功能的。
Spring
只提供统⼀事务管理接⼜,具体实现都是由各数据库⾃⼰实现,数据
库事务的提交和回滚是通过数据库⾃⼰的事务机制实现。
23.Spring
事务的种类?
Spring
⽀持
编程式事务
管理和
声明式
事务管理两种⽅式
1.
编程式事务
编程式事务管理使⽤
TransactionTemplate
,需要显式执⾏事务。
2.
声明式事务
3.
声明式事务管理建⽴在
AOP
之上的。其本质是通过
AOP
功能,对⽅法前后进⾏拦截,将
事务处理的功能编织到拦截的⽅法中,也就是在⽬标⽅法开始之前启动⼀个事务,在执⾏
完⽬标⽅法之后根据执⾏情况提交或者回滚事务
4.
优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置⽂件中做相关的事务规
则声明或通过
@Transactional
注解的⽅式,便可以将事务规则应⽤到业务逻辑中,减少业
务代码的污染。唯⼀不⾜地⽅是,最细粒度只能作⽤到⽅法级别,⽆法做到像编程式事务
那样可以作⽤到代码块级别。
24.Spring
的事务隔离级别?
Spring
的接⼜
TransactionDefinition
中定义了表⽰隔离级别的常量,当然其实主要还是对应数据
库的事务隔离级别:
1.
ISOLATION_DEFAULT
:使⽤后端数据库默认的隔离界别,
MySQL
默认可重复读,
Oracle
默认读已提交。
2.
ISOLATION_READ_UNCOMMITTED
:读未提交
3.
ISOLATION_READ_COMMITTED
:读已提交
4.
ISOLATION_REPEATABLE_READ
:可重复读
5.
ISOLATION_SERIALIZABLE
:串⾏化
25.Spring
的事务传播机制?
Spring
事务的传播机制说的是,当多个事务同时存在的时候
——
⼀般指的是多个事务⽅法相
互调⽤时,
Spring
如何处理这些事务的⾏为。
事务传播机制是使⽤简单的
ThreadLocal
实现的,所以,如果调⽤的⽅法是在新线程调⽤的,
事务传播实际上是会失效的。
Spring
默认的事务传播⾏为是
PROPAFATION_REQUIRED
,它适合绝⼤多数情况,如果多个
ServiceX#methodX()
都⼯作在事务环境下(均被
Spring
事务增强),且程序中存在调⽤链
Service1#method1()->Service2#method2()->Service3#method3()
,那么这
3
个服务类的三个⽅法通
过
Spring
的事务传播机制都⼯作在同⼀个事务中。
26.
声明式事务实现原理了解吗?
就是通过
AOP/
动态代理。
在
Bean
初始化阶段创建代理对象
:
Spring
容器在初始化每个单例
bean
的时候,会遍历容器
中的所有
BeanPostProcessor
实现类,并执⾏其
postProcessAfterInitialization
⽅法,在执⾏
AbstractAutoProxyCreator
类的
postProcessAfterInitialization
⽅法时会遍历容器中所有的切
⾯,查找与当前实例化
bean
匹配的切⾯,这⾥会获取事务属性切⾯,查找
@Transactional
注解及其属性值,然后根据得到的切⾯创建⼀个代理对象,默认是使⽤
JDK
动态代理创建
代理,如果⽬标类是接⼜,则使⽤
JDK
动态代理,否则使⽤
Cglib
。
在执⾏⽬标⽅法时进⾏事务增强操作
:当通过代理对象调⽤
Bean
⽅法的时候,会触发对
应的
AOP
增强拦截器,声明式事务是⼀种环绕增强,对应接⼜
为
MethodInterceptor
,事务增强对该接⼜的实现为
TransactionInterceptor
,类
图如下:
1
、
@Transactional
应⽤在⾮
public
修饰的⽅法上
如果
Transactional
注解应⽤在⾮
public
修饰的⽅法上,
Transactional
将会失效。
是因为在
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
的属性
配置信息。
2
、
@Transactional
注解属性
propagation
设置错误
TransactionDefinition.PROPAGATION_SUPPORTS
:如果当前存在事务,则加⼊该事务;
如果当前没有事务,则以⾮事务的⽅式继续运⾏。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
:以⾮事务⽅式运⾏,如果当前
存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER
:以⾮事务⽅式运⾏,如果当前存在事务,
则抛出异常。
3
、
@Transactional
注解属性
rollbackFor
设置错误
rollbackFor
可以指定能够触发事务回滚的异常类型。
Spring
默认抛出了未检查
unchecked
异常
(继承⾃
RuntimeException
的异常)或者
Error
才回滚事务,其他异常不会触发回滚事务。
//
希望⾃定义的异常可以进⾏回滚
@Transactional
(propagation
=
Propagation.REQUIRED,rollbackFor
=
MyException.
class
若在⽬标⽅法中抛出的异常是
rollbackFor
指定的异常的⼦类,事务同样会回滚。
4
、同⼀个类中⽅法调⽤,导致
@Transactional
失效
开发中避免不了会对同⼀个类⾥⾯的⽅法调⽤,⽐如有⼀个类
Test
,它的⼀个⽅法
A
,
A
再调
⽤本类的⽅法
B
(不论⽅法
B
是⽤
public
还是
private
修饰),但⽅法
A
没有声明注解事务,⽽
B
⽅法有。则外部调⽤⽅法
A
之后,⽅法
B
的事务是不会起作⽤的。这也是经常犯错误的⼀个地
⽅。
那为啥会出现这种情况?其实这还是由于使⽤
Spring AOP
代理造成的,因为只有当事务⽅法
被当前类以外的代码调⽤时,才会由
Spring
⽣成的代理对象来管理。
//@Transactional
@GetMapping
(
"/test"
)
private
Integer
A
()
throws
Exception {
CityInfoDict cityInfoDict
=
new
CityInfoDict();
cityInfoDict.setCityName(
"2"
);
/**
* B
插⼊字段为
3
的数据
*/
this
.insertB();
/**
* A
插⼊字段为
2
的数据
*/
int
insert
=
cityInfoDictMapper.insert(cityInfoDict);
return
insert;
}
@Transactional
()
public
Integer
insertB
()
throws
Exception {
CityInfoDict cityInfoDict
=
new
CityInfoDict();
cityInfoDict.setCityName(
"3"
);
cityInfoDict.setParentCityId(
3
);
return
cityInfoDictMapper.insert(cityInfoDict);
}
这种情况是最常见的⼀种
@Transactional
注解失效场景
@Transactional
private
Integer
A
()
throws
Exception {
int
insert
=
0
;
try
{
CityInfoDict cityInfoDict
=
new
CityInfoDict();
cityInfoDict.setCityName(
"2"
);
cityInfoDict.setParentCityId(
2
);
/**
* A
插⼊字段为
2
的数据
*/
insert
=
cityInfoDictMapper.insert(cityInfoDict);
/**
* B
插⼊字段为
3
的数据
*/
b.insertB();
}
catch
(Exception e) {
e.printStackTrace();
}
}
如果
B
⽅法内部抛了异常,⽽
A
⽅法此时
try catch
了
B
⽅法的异常,那这个事务就不能正常回滚
了,会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction
rolled back because it has been marked as rollback
-
only
28.Spring MVC
的核⼼组件?
1.
DispatcherServlet
:前置控制器,是整个流程控制的 核⼼ ,控制其他组件的执⾏,进⾏
统⼀调度,降低组件之间的耦合性,相当于总指挥。
2.
Handler
:处理器,完成具体的业务逻辑,相当于
Servlet
或
Action
。
3.
HandlerMapping
:
DispatcherServlet
接收到请求之后,通过
HandlerMapping
将不同的请求
映射到不同的
Handler
。
4.
HandlerInterceptor
:处理器拦截器,是⼀个接⼜,如果需要完成⼀些拦截处理,可以实现
该接⼜。
5.
HandlerExecutionChain
:处理器执⾏链,包括两部分内容:
Handler
和
HandlerInterceptor
(系统会有⼀个默认的
HandlerInterceptor
,如果需要额外设置拦截,可以添加拦截器)。
6.
HandlerAdapter
:处理器适配器,
Handler
执⾏业务⽅法之前,需要进⾏⼀系列的操作,
包括表单数据的验证、数据类型的转换、将表单数据封装到
JavaBean
等,这些操作都是
由
HandlerApater
来完成,开发者只需将注意⼒集中业务逻辑的处理上,
DispatcherServlet
通过
HandlerAdapter
执⾏不同的
Handler
。
7.
ModelAndView
:装载了模型数据和视图信息,作为
Handler
的处理结果,返回给
DispatcherServlet
。
8.
ViewResolver
:视图解析器,
DispatcheServlet
通过它将逻辑视图解析为物理视图,最终
将渲染结果响应给客户端。
29.Spring MVC
的⼯作流程?
1.
客户端向服务端发送⼀次请求,这个请求会先到前端控制器
DispatcherServlet(
也叫中央控
制器
)
。
2.
DispatcherServlet
接收到请求后会调⽤
HandlerMapping
处理器映射器。由此得知,该请求该
由哪个
Controller
来处理(并未调⽤
Controller
,只是得知)
3.
DispatcherServlet
调⽤
HandlerAdapter
处理器适配器,告诉处理器适配器应该要去执⾏哪个
Controller
4.
HandlerAdapter
处理器适配器去执⾏
Controller
并得到
ModelAndView(
数据和视图
)
,并层层
返回给
DispatcherServlet
5.
DispatcherServlet
将
ModelAndView
交给
ViewReslover
视图解析器解析,然后返回真正的视
6.
DispatcherServlet
将模型数据填充到视图中
7.
DispatcherServlet
将结果响应给客户端
Spring MVC
虽然整体流程复杂,但是实际开发中很简单,⼤部分的组件不需要开发⼈员创
建和管理,只需要通过配置⽂件的⽅式完成配置即可,真正需要开发⼈员进⾏处理的只有
Handler
(
Controller
)
、
View
、
Model
。
当然我们现在⼤部分的开发都是前后端分离,
Restful
风格接⼜,后端只需要返回
Json
数据就⾏
了。
1.
客户端向服务端发送⼀次请求,这个请求会先到前端控制器
DispatcherServlet
2.
DispatcherServlet
接收到请求后会调⽤
HandlerMapping
处理器映射器。由此得知,该请求该
由哪个
Controller
来处理
3.
DispatcherServlet
调⽤
HandlerAdapter
处理器适配器,告诉处理器适配器应该要去执⾏哪个
Controller
4.
Controller
被封装成了
ServletInvocableHandlerMethod
,
HandlerAdapter
处理器适配器去执⾏
invokeAndHandle
⽅法,完成对
Controller
的请求处理
5.
HandlerAdapter
执⾏完对
Controller
的请求,会调⽤
HandlerMethodReturnValueHandler
去处
理返回值,主要的过程:
5.1.
调⽤
RequestResponseBodyMethodProcessor
,创建
ServletServerHttpResponse
(
Spring
对
原⽣
ServerHttpResponse
的封装)实例
5.2.
使⽤
HttpMessageConverter
的
write
⽅法,将返回值写⼊
ServletServerHttpResponse
的
OutputStream
输出流中
5.3.
在写⼊的过程中,会使⽤
JsonGenerator
(默认使⽤
Jackson
框架)对返回值进⾏
Json
序
列化
6.
执⾏完请求后,返回的
ModealAndView
为
null
,
ServletServerHttpResponse
⾥也已经写⼊了
响应,所以不⽤关⼼
View
的处理
Spring Boot
以
约定⼤于配置
核⼼思想开展⼯作,相⽐
Spring
具有如下优势:
1.
Spring Boot
可以快速创建独⽴的
Spring
应⽤程序。
2.
Spring Boot
内嵌了如
Tomcat
,
Jetty
和
Undertow
这样的容器,也就是说可以直接跑起来,⽤
不着再做部署⼯作了。
3.
Spring Boot
⽆需再像
Spring
⼀样使⽤⼀堆繁琐的
xml
⽂件配置。
4.
Spring Boot
可以⾃动配置
(
核⼼
)Spring
。
SpringBoot
将原有的
XML
配置改为
Java
配置,将
bean
注⼊改为使⽤注解注⼊的⽅式
(@Autowire)
,并将多个
xml
、
properties
配置浓缩在⼀个
appliaction.yml
配置⽂件中。
5.
Spring Boot
提供了⼀些现有的功能,如量度⼯具,表单数据验证以及⼀些外部配置这样
的⼀些第三⽅功能。
6.
Spring Boot
可以快速整合常⽤依赖(开发库,例如
spring-webmvc
、
jackson-json
、
validation-api
和
tomcat
等),提供的
POM
可以简化
Maven
的配置。当我们引⼊核⼼依赖时,
SpringBoot
会⾃引⼊其他依赖。
32.SpringBoot
⾃动配置原理了解吗?
SpringBoot
开启⾃动配置的注解是
@EnableAutoConfiguration
,启动类上的注解
@SpringBootApplication
是⼀个复合注解,包含了
@EnableAutoConfiguration
:
EnableAutoConfiguration
只是⼀个简单的注解,⾃动装配核⼼功能的实现实际是通
过
AutoConfigurationImportSelector
类
@AutoConfigurationPackage
//
将
main
同级的包下的所有组件注册到容器中
@Import
({
AutoConfigurationImportSelector
.
class
})
//
加载⾃动装配类
xxxAutoconfiguration
public @interface
EnableAutoConfiguration
{
String
ENABLED_OVERRIDE_PROPERTY
=
"spring.boot.enableautoconfiguration"
;
Class
>
[]
exclude
()
default
{};
String
[]
excludeName
()
default
{};
}
AutoConfigurationImportSelector
实现了
ImportSelector
接⼜,这个接⼜的作
⽤就是收集需要导⼊的配置类,配合
@Import(
)
就可以将相应的类导⼊到
Spring
容器中
获取注⼊类的⽅法是
selectImports()
,它实际调⽤的是
getAutoConfigurationEntry
,
这个⽅法是获取⾃动装配类的关键,主要流程可以分为这么⼏步:
1.
获取注解的属性,⽤于后⾯的排除
2.
获取所有需要⾃动装配的配置类的路径 :这⼀步是最关键的,从
META
INF/spring.factories
获取⾃动配置类的路径
3.
去掉重复的配置类和需要排除的重复类,把需要⾃动加载的配置类的路径存储起来
protected
AutoConfigurationImportSelector.AutoConfigurationEntry
getAutoConfigurationEntry
(AnnotationMetadata annotationMetadata) {
if
(
!
this
.isEnabled(annotationMetadata)) {
return
EMPTY_ENTRY;
}
else
{
//1.
获取到注解的属性
AnnotationAttributes attributes
=
this
.getAttributes(annotationMetadata);
//2.
获取需要⾃动装配的所有配置类,读取
META-INF/spring.factories
,获取
⾃动配置类路径
List
<
String
>
configurations
=
this
.getCandidateConfigurations(annotationMetadata, attributes);
//3.1.
移除重复的配置
configurations
=
this
.removeDuplicates(configurations);
//3.2.
处理需要排除的配置
Set
<
String
>
exclusions
=
this
.getExclusions(annotationMetadata, attributes);
this
.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations
=
this
.getConfigurationClassFilter().filter(configurations);
this
.fireAutoConfigurationImportEvents(configurations,
exclusions);
return new
AutoConfigurationImportSelector.AutoConfigurationEntry(configurations,
exclusions);
}
}
34.Springboot
启动原理?
SpringApplication
这个类主要做了以下四件事情:
1.
推断应⽤的类型是普通的项⽬还是
Web
项⽬
2.
查找并加载所有可⽤初始化器 , 设置到
initializers
属性中
3.
找出所有的应⽤程序监听器,设置到
listeners
属性中
4.
推断并设置
main
⽅法的定义类,找到运⾏的主类
什么是微服务?
1.
2014
年
Martin Fowler
提出的⼀种新的架构形式。微服务架构是⼀种 架构模式 ,提倡将
单⼀应⽤程序划分成⼀组⼩的服务,服务之间相互协调,互相配合,为⽤户提供最终价
值。每个服务运⾏在其独⽴的进程中,服务与服务之间采⽤轻量级的通信机制
(
如
HTTP
或
Dubbo)
互相协作,每个服务都围绕着具体的业务进⾏构建,并且能够被独⽴的部署到⽣产
环境中,另外,应尽量避免统⼀的,集中式的服务管理机制,对具体的⼀个服务⽽⾔,应
根据业务上下⽂,选择合适的语⾔、⼯具
(
如
Maven)
对其进⾏构建。
2.
微服务化的核⼼就是将传统的⼀站式应⽤,根据业务拆分成⼀个⼀个的服务,彻底地去耦
合,每⼀个微服务提供单个业务功能的服务,⼀个服务做⼀件事情,从技术⾓度看就是⼀
种⼩⽽独⽴的处理过程,类似进程的概念,能够⾃⾏单独启动或销毁,拥有⾃⼰独⽴的数
据库。
微服务架构主要要解决哪些问题?
1.
服务很多,客户端怎么访问,如何提供对外⽹关
?
2.
这么多服务,服务之间如何通信
? HTTP
还是
RPC?
3.
这么多服务,如何治理
?
服务的注册和发现。
4.
服务挂了怎么办?熔断机制。
有哪些主流微服务框架?
1.
Spring Cloud Netflix
2.
Spring Cloud Alibaba
3.
SpringBoot + Dubbo + ZooKeeper