1.ApplicationContext refresh 的流程
ApplicationContext 为 Spring 的核心容器
refresh 是 ApplicationContext 的中的一个方法,其中会调用到 12 个方法
1.1.prepareRefresh
要点
- 这一步创建和准备了 Environment 对象
- 要理解 Environment 对象的作用
总结
- Environment 的作用之一是为后续 @Value,值注入时提供键值
- 调试代码技巧:可以在关键步骤前后加断点、观察变化;可以用 Evaluate Expression 动态执行代码
代码事例:
// 如何获得和解析 @Value 内容
public class TestEnvironment {
public static void main(String[] args) throws NoSuchFieldException, IOException {
// 1) 获得 @Value 的值
System.out.println("=======================> 仅获取 @Value 值");
QualifierAnnotationAutowireCandidateResolver resolver = new QualifierAnnotationAutowireCandidateResolver();
Object name = resolver.getSuggestedValue(new DependencyDescriptor(Bean1.class.getDeclaredField("name"), false));
System.out.println(name);
// 2) 解析 @Value 的值
System.out.println("=======================> 获取 @Value 值, 并解析${}");
Object javaHome = resolver.getSuggestedValue(new DependencyDescriptor(Bean1.class.getDeclaredField("javaHome"), false));
System.out.println(javaHome);
System.out.println(getEnvironment().resolvePlaceholders(javaHome.toString()));
// 3) 解析 SpEL 表达式
System.out.println("=======================> 获取 @Value 值, 并解析#{}");
Object expression = resolver.getSuggestedValue(new DependencyDescriptor(Bean1.class.getDeclaredField("expression"), false));
System.out.println(expression);
String v1 = getEnvironment().resolvePlaceholders(expression.toString());
System.out.println(v1);
System.out.println(new StandardBeanExpressionResolver().evaluate(v1, new BeanExpressionContext(new DefaultListableBeanFactory(),null)));
}
private static Environment getEnvironment() throws IOException {
StandardEnvironment env = new StandardEnvironment();
env.getPropertySources().addLast(new ResourcePropertySource("jdbc", new ClassPathResource("jdbc.properties")));
return env;
}
static class Bean1 {
@Value("hello")
private String name;
@Value("${jdbc.username}")
private String javaHome;
@Value("#{'class version:' + '${java.class.version}'}")
private String expression;
}
}
1.2.obtainFreshBeanFactory
要点
- 这一步获取(或创建) BeanFactory
- 理解 BeanFactory 的作用
- 理解 BeanDefinition 的作用
- BeanDefinition 从何而来
ApplicationContext 与 BeanFactory 属于组合关系
ApplicationContext 在 bean 的管理上借助了 BeanFactory 的功能
总结
- BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化
- BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
- BeanDefinition 的来源有多种多样,可以是通过 xml 获得、通过配置类获得、通过组件扫描获得,也可以是编程添加
关于 BeanDefinition 的来源事例代码
public class TestBeanDefinition {
public static void main(String[] args) {
System.out.println("========================> 一开始");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println(Arrays.toString(beanFactory.getBeanDefinitionNames()));
System.out.println("========================> 1) 从 xml 获取 ");
XmlBeanDefinitionReader reader1 = new XmlBeanDefinitionReader(beanFactory);
reader1.loadBeanDefinitions(new ClassPathResource("bd.xml"));
System.out.println(Arrays.toString(beanFactory.getBeanDefinitionNames()));
System.out.println("========================> 2) 从配置类获取 ");
beanFactory.registerBeanDefinition("config1", BeanDefinitionBuilder.genericBeanDefinition(Config1.class).getBeanDefinition());
ConfigurationClassPostProcessor postProcessor = new ConfigurationClassPostProcessor();
postProcessor.postProcessBeanDefinitionRegistry(beanFactory);
System.out.println(Arrays.toString(beanFactory.getBeanDefinitionNames()));
System.out.println("========================> 3) 扫描获取 ");
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("day04.refresh.sub");
System.out.println(Arrays.toString(beanFactory.getBeanDefinitionNames()));
}
static class Bean1 {
}
static class Bean2 {
}
static class Config1 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
}
1.3.prepareBeanFactory
要点:
- 完善 BeanFactory
- 了解谁来解析 SpEL
- 了解谁来执行类型转换
- 了解特殊 bean 的注入
- 两个内置的 BeanPostProcessor 的作用
总结
- StandardBeanExpressionResolver 来解析 SpEL
- ResourceEditorRegistrar 会注册类型转换器,并应用 ApplicationContext 提供的 Environment 完成 ${ } 解析
- 特殊 bean 指 beanFactory 以及 ApplicationContext,通过 registerResolvableDependency 来注册它们
- beanPostProcessors 实现对 Bean 的创建,依赖注入,进行增强。如:ApplicationContextAwareProcessor 用来解析 Aware 接口
ApplicationListenerDetector 用来识别容器中 ApplicationListener 类型的 bean
1.4.postProcessBeanFactory
要点
- 这一步是空实现,留给子类扩展
- 掌握对应的设计模式
总结
- 一般 Web 环境的 ApplicationContext 都要利用它注册新的 Scope,完善 Web 下的 BeanFactory
- 体现的是模板方法设计模式
1.5.invokeBeanFactoryPostProcessors
对 Bean 整个工厂进行增强的
要点
- 理解 beanFactory 后处理器的作用
- 掌握常见的 beanFactory 后处理器
总结
- beanFactory 后处理器,充当 beanFactory 的扩展点,可以用来补充或修改 BeanDefinition
- ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource
等 - PropertySourcesPlaceHolderConfigurer – 替换 BeanDefinition 中的 ${ }
- MapperScannerConfigurer – 补充 Mapper 接口对应的 BeanDefinition
1.6.registerBeanPostProcessors
对 Bean 创建过程中进行增强,添加相应的后处理器
要点
- 理解 bean 后处理器的作用
- 掌握常见的 bean 后处理器
总结
- bean 后处理器,充当 bean 的扩展点,可以工作在 bean 的实例化、依赖注入、初始化阶段
- AutowiredAnnotationBeanPostProcessor 功能有:解析 @Autowired,@Value 注解
- CommonAnnotationBeanPostProcessor 功能有:解析 @Resource,@PostConstruct,@PreDestroy
- AnnotationAwareAspectJAutoProxyCreator 功能有:为符合切点的目标 bean 自动创建代理
事例代码:
public class TestBeanPostProcessor {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.registerBeanDefinition("bean1", BeanDefinitionBuilder.genericBeanDefinition(Bean1.class).getBeanDefinition());
beanFactory.registerBeanDefinition("bean2", BeanDefinitionBuilder.genericBeanDefinition(Bean2.class).getBeanDefinition());
beanFactory.registerBeanDefinition("bean3", BeanDefinitionBuilder.genericBeanDefinition(Bean3.class).getBeanDefinition());
beanFactory.registerBeanDefinition("aspect1", BeanDefinitionBuilder.genericBeanDefinition(Aspect1.class).getBeanDefinition());
beanFactory.registerBeanDefinition("processor1",
BeanDefinitionBuilder.genericBeanDefinition(AutowiredAnnotationBeanPostProcessor.class).getBeanDefinition());
beanFactory.registerBeanDefinition("processor2",
BeanDefinitionBuilder.genericBeanDefinition(CommonAnnotationBeanPostProcessor.class).getBeanDefinition());
beanFactory.registerBeanDefinition("processor3",
BeanDefinitionBuilder.genericBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class).getBeanDefinition());
context.refresh();
beanFactory.getBean(Bean1.class).foo();
}
static class Bean1 {
Bean2 bean2;
Bean3 bean3;
@Autowired
public void setBean2(Bean2 bean2) {
System.out.println("发生了依赖注入..." + bean2);
this.bean2 = bean2;
}
@Resource
public void setBean3(Bean3 bean3) {
System.out.println("发生了依赖注入..." + bean3);
this.bean3 = bean3;
}
public void foo() {
System.out.println("foo");
}
}
static class Bean2 {
}
static class Bean3 {
}
@Aspect
static class Aspect1 {
@Before("execution(* foo())")
public void before() {
System.out.println("before...");
}
}
}
1.7.initMessageSource
要点
- 理解 MessageSource 的作用
- MessageSource 从何而来
总结
- 实现国际化
- 容器中一个名为 messageSource 的 bean,如果没有,则提供空的 MessageSource 实现
1.8.initApplicationEventMulticaster
要点
- 理解事件广播器的作用
- 事件广播器从何而来
- 如何发布事件
总结
- 用来发布事件给监听器
- 可以从容器中找名为 applicationEventMulticaster 的 bean 作为事件广播器,若没有,也会新建默认的事件广播器
- 可以调用 ApplicationContext.publishEvent(事件对象) 来发布事件
1.9.onRefresh
要点
- 这一步是空实现,留给子类扩展
- 掌握对应的设计模式
总结
- SpringBoot 中的子类可以在这里准备 WebServer,即内嵌 web 容器
- 体现的是模板方法设计模式
1.10.registerListeners
要点
- 理解事件监听器作用
- 监听器从何而来
- 如何接收事件
总结
- 用来接收事件
- 一部分监听器是事先编程添加的、另一部分监听器来自容器中的 bean、还有一部分来自于 @EventListener 的解析
- 实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e)方法即可
1.11.finishBeanFactoryInitialization
要点
- 了解 conversionService
- 了解内嵌值解析器
- 单例池 - singletonObjects
总结
- conversionService 也是一套转换机制,作为对 PropertyEditor 的补充
- 内嵌值解析器用来解析 @Value 中的 ${ },借用的是 Environment 的功能
- 单例池用来缓存所有单例对象,对象的创建都分三个阶段,每一阶段都有不同的 bean 后处理器参与进来,扩展功能
1.12.finishRefresh
要点
- 了解 lifecycleProcessor
- 它从何而来
- 如何控制 lifecycle?
- 发布 ContextRefreshed 事件
总结
- 用来控制容器内需要生命周期管理的 bean
- 如果容器中有名称为 lifecycleProcessor 的 bean 就用它,否则创建默认的生命周期管理器
- 调用 context 的 start,即可触发所有实现 LifeCycle 接口 bean 的 start
- 调用 context 的 stop,即可触发所有实现 LifeCycle 接口 bean 的 stop
refresh 12 个步骤
1.prepareRefresh – 做好准备工作
2.obtainFreshBeanFactory – 创建或获取 BeanFactory
3.prepareBeanFactory – 准备 BeanFactory
4.postProcessBeanFactory – 子类扩展 BeanFactory
5.invokeBeanFactoryPostProcessors – 后处理器扩展 BeanFactory
6.registerBeanPostProcessors – 准备 Bean 后处理器
7.initMessageSource – 为 ApplicationContext 提供国际化功能
8.initApplicationEventMulticaster – 为 ApplicationContext 提供事件发布器
9.onRefresh – 留给子类扩展
10.registerListeners – 为 ApplicationContext 准备监听器
11.finishBeanFactoryInitialization – 初始化单例 Bean,执行 Bean 后处理器扩展
12.finishRefresh – 准备生命周期管理器,发布 ContextRefreshed 事件
2.Spring bean 的生命周期
spring 从创建到销毁主要干了哪些事情?
阶段 1:处理名称,检查缓存
要点
- 掌握别名处理
- 了解 FactoryBean 的名字规范
- 掌握三级缓存的概念
总结
- 先把别名解析为实际名称,再进行后续处理
- 若要 FactoryBean 本身,需要使用 & 名称获取
- singletonObjects 是一级缓存,放单例成品对象
- singletonFactories 是三级缓存,放单例工厂
- earlySingletonObjects 是二级缓存,放单例工厂的产品,可称为提前单例对象
阶段 2:处理父子容器
要点
- 了解有父容器时的查找规则
总结
- 父子容器的 bean 名称可以重复
- 优先找子容器的 bean,找到了直接返回,找不到继续到父容器找
阶段 3:处理 dependsOn
要点
- 了解有 dependsOn 时的 bean 初始化顺序
- 了解 @Conditional 的解析时机
- 了解 beanName 的解析时机
- 掌握 @Bean 的解析
- 了解 @DpendsOn,@Lazy,@Primay 的解析时机
- 了解 @Scope 代理的解析
总结
-
dependsOn 用在非显式依赖的 bean 的创建顺序控制
-
@Conditional 由 ConditionEvaluator 解析看是否满足装配条件
-
beanName 的解析分情况
- 组件扫描 - AnnotationBeanNameGenerator
- @Import - FullyQualifiedAnnotationBeanNameGenerator
- @Bean - ConfigurationClassBeanDefinitionReader
-
@Bean 相当于工厂方法,所在类相当于工厂
-
这些注解由 AnnotationConfigUtils 补充为 BeanDefinition
-
Scope 标注的 bean 会为之生成 ScopedProxyFactoryBean 的 BeanDefinition 取代原有,原有 BeanDefinition 成为内嵌定义
阶段 4:选择 scope 策略
要点
- 理解三种 scope
总结
- scope 理解为从 xxx 范围内找这个 bean 更加贴切
- singleton scope 表示从单例池范围内获取 bean,如果没有,则创建并放入单例池
- prototype scope 表示从不缓存 bean,每次都创建新的
- request scope 表示从 request 对象范围内获取 bean,如果没有,则创建并放入 request …
单例 bean,首次被 getBean 的时候才会被创建,在容器关闭的时候被销毁
多例 bean,首次被 getBean 的时候才会被创建,调用 BeanFactory 的 destroyBean 才被销毁(手动)
request bean,首次被 getBean 的时候才会被创建,在特定的请求域结束前销毁
public class TestScope {
public static void main(String[] args) {
testRequestScope();
}
// 单例 bean 从 refresh 被创建, 到 close 被销毁, BeanFactory 会记录哪些 bean 要调用销毁方法
private static void testSingletonScope() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean1", Bean1.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.refresh(); // getBean
context.close();
}
// 多例 bean 从首次 getBean 被创建, 到调用 BeanFactory 的 destroyBean 被销毁
private static void testPrototypeScope() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("bean1", Bean1.class, bd -> bd.setScope("prototype"));
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.refresh();
Bean1 bean = context.getBean(Bean1.class);
// 没谁记录该 bean 要调用销毁方法, 需要我们自行调用
context.getDefaultListableBeanFactory().destroyBean(bean);
context.close();
}
// request bean 从首次 getBean 被创建, 到 request 结束前被销毁
private static void testRequestScope() {
GenericApplicationContext context = new GenericApplicationContext();
context.getDefaultListableBeanFactory().registerScope("request", new RequestScope());
context.registerBean("bean1", Bean1.class, bd -> bd.setScope("request"));
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.refresh();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
MockHttpServletRequest request = new MockHttpServletRequest();
// 每个 webRequest 对象会记录哪些 bean 要调用销毁方法
ServletWebRequest webRequest = new ServletWebRequest(request);
RequestContextHolder.setRequestAttributes(webRequest);
Bean1 bean = context.getBean(Bean1.class);
LoggerUtils.get().debug("{}", bean);
LoggerUtils.get().debug("{}", request.getAttribute("bean1"));
// request 请求结束前调用这些销毁方法
webRequest.requestCompleted();
}).start();
}
}
static class Bean1 {
@PostConstruct
public void init() {
LoggerUtils.get().debug("{} - init", this);
}
@PreDestroy
public void destroy() {
LoggerUtils.get().debug("{} - destroy", this);
}
}
}
阶段 5:创建 bean
阶段 5-1:创建 bean 实例
要点
- AutowiredAnnotationBeanPostProcessor 选择构造
- 优先选择带 @ Autowired 注解的构造
- 若有唯一的带参构造, 也会入选选择所有公共构造
- 采用默认构造
- 如果上面的后处理器和 BeanDefiniation 都没找到构造,采用默认构造,即使是私有的
阶段 5-2:依赖注入
要点
-
AutowiredAnnotationBeanPostProcessor(注解匹配)
- 识别 @Autowired 及 @Value 标注的成员,封装为 InjectionMetadata 进行依赖注入
-
CommonAnnotationBeanPostProcessor(注解匹配)
- 识别 @Resource 标注的成员,封装为 InjectionMetadata 进行依赖注入
-
AUTOWIRE_BY_NAME(根据名字匹配)
- 根据成员名字找 bean 对象,修改 mbd 的 propertyalues,不会考虑简单类型的成员
-
AUTOWIRE_BY_TYPE(根据类型匹配)
- 根据成员类型执行 resolveDependency 找到依赖注入的值,修改 mbd 的 propertyValues
-
applyPropertyValues(即 xml 中 <property name ref|value/>)(精确指定)
- 根据 mbd 的 propertyValues 进行依赖注入
依赖注入的优先级:
// 测试如果对同一属性进行的 @Autowired 注入、AUTOWIRE_BY_NAME、精确指定注入名称, 优先级是怎样的
public class TestInjection {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean("bean1", Bean1.class, bd -> {
// 优先级最高的:精确指定注入 bean 的名称 <property name="bean3" ref="bean2"/>
bd.getPropertyValues().add("bean3", new RuntimeBeanReference("bean2"));
// 优先级次之的:通过 AUTOWIRE_BY_NAME 匹配 ,会将set去掉,Bean3
((RootBeanDefinition) bd).setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
});
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
context.registerBean("bean4", Bean4.class);
context.refresh();
}
static class Bean1 {
MyInterface bean;
// 优先级最低的:@Autowired 匹配
@Autowired @Qualifier("bean4")
public void setBean3(MyInterface bean) {
System.out.println(bean);
this.bean = bean;
}
}
interface MyInterface {
}
static class Bean2 implements MyInterface {
}
static class Bean3 implements MyInterface {
}
static class Bean4 implements MyInterface {
}
}
阶段 5-3:初始化
要点
-
内置 Aware 接口的装配
- 包括 BeanNameAware,BeanFactoryAware 等
-
扩展 Aware 接口的装配
- 由 ApplicationContextAwareProcessor 解析,执行时机在 postProcessBeforelnitialization
-
@PostConstruct
- 由 CommonAnnotationBeanPostProcessor 解析,执行时机在 postProcessBeforelnitialization
-
InitializingBean
- 通过接口回调执行初始化
-
initMethod(即或 @Bean(initMethod))
- 根据 BeanDefinition 得到的初始化方法执行初始化
-
创建 aop 代理
- 由 AnnotationAwareAspectJAutoProxyCreator 创建,执行时机在 postProcessAfterlnitialization
阶段 5-4:注册可销毁 bean
要点
- 判断并登记可销毁 bean
总结
- 判断依据
- 如果实现了 DisposableBean 或 AutoCloseable 接口,则为可销毁 bean
- 如果自定义了 destroyMethod,则为可销毁 bean
- 如果采用 @Bean 没有指定 destroyMethod,则采用自动推断方式获取销毁方法名(close,shutdown)
- 如果有 @PreDestroy 标注的方法
- 存储位置
- singleton scope 的可销毁 bean 会存储于 beanFactory 的成员当中
- 自定义 scope 的可销毁 bean 会存储于对应的域对象当中
- prototype scope 不会存储,需要自己找到此对象销毁
- 存储时都会封装为 DisposableBeanAdapter 类型对销毁方法的调用进行适配
阶段 6:类型转换
要点
如果 getBean 的 requiredType 参数与实际得到的对象类型不同,会尝试进行类型转换
阶段 7:销毁 bean
要点
- singleton bean 的销毁时机
- 自定义 scope bean 的销毁时机
- prototype bean 的销毁时机
- 同一 bean 中不同形式销毁方法的调用次序
总结
-
singletonbean 的销毁在 ApplicationContext.close 时,此时会找到所有 DisposableBean 的名字,逐一销毁
-
自定义 scope bean 的销毁在作用域对象生命周期结束时
-
prototype bean 的销毁可以通过自己手动调用 AutowireCapableBeanFactory.destroyBean 方法执行销毁
-
同一 bean 中不同形式销毁方法的调用次序
- 优先后处理器销毁,即 @PreDestroy
- 其次 DisposableBean 接口销毁
- 最后 destroyMethod 销毁(包括自定义名称,推断名称,AutoCloseable 接口多选一)
面试题: Spring bean 的生命周期
- 阶段 1:处理名称,检查缓存
- 阶段 2:检查父工厂
- 阶段 3:检查 DependsOn
- 阶段 4:按 Scope 创建 bean
- 创建 singleton
- 创建 prototype
- 创建其它 scope
- 阶段 5:创建 bean
- 创建 bean 实例 - @Autowired,唯一带参构造,默认构造
- 依赖注入 - @Autowired @Value,@Resource,ByName ByType,精确指定
- 初始化 – Aware 接口处理,@PostConstruct,InitializingBean,initMethod,创建代理
- 登记可销毁 bean
- 阶段 6:类型转换
- 阶段 7:销毁 bean
3.spring_tx_事务&spring_webmvc
面试题: Spring 事务失效的几种场景及原因
1. 抛出检查异常导致事务不能正确回滚
@Transactional(rollbackFor = Exception.class),当出现异常的时候就会触发回滚的操作
没加 rollbackFor 的话,抛出检查异常并不会正确回滚,只会回滚非检查异常(RuntimeException,Error 的子类)
@Service
public class Service1 {
@Autowired
private AccountMapper accountMapper;
@Transactional(rollbackFor = Exception.class)
public void transfer(int from, int to, int amount) throws FileNotFoundException {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
new FileInputStream("aaa");
accountMapper.update(to, amount);
}
}
}
2. 业务方法内自己 try-catch 异常导致事务不能正确回滚
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
解法 1:异常原样抛出
解法 2:手动设置
TransactionStatus.setRollbackOnly()
@Service
public class Service2 {
@Autowired
private AccountMapper accountMapper;
@Transactional(rollbackFor = Exception.class)
public void transfer(int from, int to, int amount) {
try {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
new FileInputStream("aaa");
accountMapper.update(to, amount);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
// throw new RuntimeException(e); //将异常抛出
TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); // 手动设置
}
}
}
3. aop 切面顺序导致导致事务不能正确回滚
原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常…
解法:同情况 2
@Aspect
@Order(Ordered.LOWEST_PRECEDENCE - 1)
static class MyAspect {
@Around("execution(* transfer(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
LoggerUtils.get().debug("log:{}", pjp.getTarget());
try {
return pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}
4. 非 public 方法导致的事务失效
原因:Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
解法:改为 public 方法
@Transactional
void transfer(int from, int to, int amount) throws FileNotFoundException {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
accountMapper.update(to, amount);
}
}
5. 父子容器导致的事务失效
原因:子容器扫描范围过大,把未加事务配置的 service 扫描进来
解法 1:各扫描各的,不要图简便
解法 2:不要用父子容器,所有 bean 放在同一容器
public class TestService5 {
public static void main(String[] args) throws FileNotFoundException {
GenericApplicationContext parent = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(parent.getDefaultListableBeanFactory());
ConfigurationPropertiesBindingPostProcessor.register(parent.getDefaultListableBeanFactory());
parent.registerBean(AppConfig.class);
parent.refresh();
GenericApplicationContext child = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(child.getDefaultListableBeanFactory());
child.setParent(parent);
child.registerBean(WebConfig.class);
child.refresh();
AccountController bean = child.getBean(AccountController.class);
bean.transfer(1, 2, 500);
}
}
6. 调用本类方法导致传播行为失效
原因:本类方法调用不经过代理,因此无法增强
解法 1:依赖注入自己(代理)来调用
解法 2:通过 AopContext 拿到代理对象,来调用
解法 3:通过 CTW,LTW 实现功能增强
@Service
public class Service6 {
// @Autowired
// private Service6 proxy;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void foo() throws FileNotFoundException {
LoggerUtils.get().debug("foo");
// System.out.println(proxy.getClass());
// proxy.bar();
((Service6) AopContext.currentProxy()).bar();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void bar() throws FileNotFoundException {
LoggerUtils.get().debug("bar");
}
}
7. @Transactional 没有保证原子行为
原因:事务的原子性仅涵盖 insert、update、delete、select… for update 语句,select 方法并不阻塞
8. @Transactional 方法导致的 synchronized 失效
原因: synchronized 保证的仅是目标方法的原子性,环绕目标方法的还有 commit 等操作,它们并未处于 sync 块内
解法 1:synchronized 范围应扩大至代理方法调用
解法 2:使用 select … for update 替换 select
面试题:SpringMVC 执行流程
SpringMVC 中的 DispatcherServlet 由 tomcat 创建与初始化的
初始化阶段:
1.在 Web 容器第一次用到 DispatcherServlet 的时候,会创建其对象并执行 init 方法
2.init 方法内会创建 Spring Web 容器,并调用容器 refresh 方法
3.refresh 过程中会创建并初始化 SpringMVC 中的重要组件,例如 MultipartResolver(文件上传时处理表单数据),HandlerMapping(请求映射),HandlerAdapter(调用和执行控制器方法),HandlerExceptionResolver(异常处理)、ViewResolver(解析成视图对象)等
4.容器初始化后,会将上一步初始化好的重要组件,赋值给 DispatcherServlet 的成员变量,留待后用
匹配阶段:
1.用户发送的请求统一到达前端控制器 DispatcherServlet
2.DispatcherServlet 遍历所有 HandlerMapping,找到与路径匹配的处理器
①HandlerMapping 有多个,每个 HandlerMapping 会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别 @RequestMapping 的优先级最高
② 对应 @RequestMapping 的处理器是 HandlerMethod,它包含了控制器对象和控制器方法信息
③ 其中路径与处理器的映射关系在 HandlerMapping 初始化时就会建立好
3.将 HandlerMethod 连同匹配到的拦截器,生成调用链对象 HandlerExecutionChain 返回
4.遍历 HandlerAdapter 处理器适配器,找到能处理 HandlerMethod 的适配器对象,开始调用
执行阶段:
1.执行拦截器 preHandle
2.由 HandlerAdapter 调用 HandlerMethod
① 调用前处理不同类型的参数
② 调用后处理不同类型的返回值
3.第 2 步没有异常
① 返回 ModelAndView
② 执行拦截器 postHandle 方法
③ 解析视图,得到 View 对象,进行视图渲染
4.第 2 步有异常,进入 HandlerExceptionResolver 异常处理流程
5.最后都会执行拦截器的 afterCompletion 方法
6.如果控制器方法标注了 @ResponseBody 注解,则在第 2 步,就会生成 json 结果,并标记 ModelAndView 已处理,这样就不会执行第 3 步的视图渲染
4. Spring 注解
@Configuration
注意点 1: 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法
注意点 2: @Bean 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法(如果参数需要注入的值越多,优先级越高)
注意点 3: @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
添加后处理的时候,在配置类中直接使用 @Value 是存在问题的,
正常来说配置类是在 finishBeanFactoryInitialization 被创建的,
但后处理器在 invokeBeanFactoryPostProcessors 的时候就得被创建,因为该配置文件中的后处理器是成员变量,所以该配置类也应该在该阶段创建,导致 @Value 读取不到数据
解决方法:1.该成静态工厂方法(推荐) 2.将注入的参数加载对应的方法参数上
public class TestConfiguration {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean("myConfig", MyConfig.class);
context.refresh();
// System.out.println(context.getBean(MyConfig.class).getClass());
}
@Configuration
@MapperScan("aaa")
// 注意点1: 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法
static class MyConfig {
// 注意点2: @Bean 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法
// 注意点3: @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
/*@Bean
public Bean1 bean1() {
System.out.println("bean1()");
System.out.println(bean2()); //每次调用的都是同一个,保证单例
System.out.println(bean2());
System.out.println(bean2());
return new Bean1();
}*/
/*@Bean
public Bean1 bean1(@Value("${java.class.version}") String a) {
System.out.println("bean1(" + a + ")");
return new Bean1();
}
// 选择参数最多的这个
@Bean
public Bean1 bean1(@Value("${java.class.version}") String a, @Value("${JAVA_HOME}") String b) {
System.out.println("bean1(" + a + ", " + b + ")");
return new Bean1();
}*/
/*@Bean
public Bean2 bean2() {
System.out.println("bean2()");
return new Bean2();
}*/
// 注意点4: @Configuration 中如果含有 BeanFactory 后处理器, 则实例工厂方法会导致 MyConfig 提前创建, 造成其依赖注入失败
// 解决方法是该用静态工厂方法或直接为 @Bean 的方法参数依赖注入, 针对 MapperScanner 可以改用注解方式
@Value("${java.class.version}")
private String version;
/*@Bean
public static MapperScannerConfigurer configurer() {
MapperScannerConfigurer scanner = new MapperScannerConfigurer();
scanner.setBasePackage("aaa");
return scanner;
}*/
@Bean
public Bean3 bean3() {
System.out.println("bean3() " + version);
return new Bean3();
}
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
}
@Import
@Import(Bean1.class) 1. 引入单个 bean
@Import(OtherConfig.class) 2. 引入一个配置类
@Import(MySelector.class) 3. 通过 Selector 引入多个类(不会将继承方法的类加载进去)
@Import(MyRegistrar.class) 4. 通过 beanDefinition 注册器(不会将继承方法的类加载进去)
public class TestImport {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean(MyConfig.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration
// @Import(Bean1.class) // 1. 引入单个 bean
// @Import(OtherConfig.class) // 2. 引入一个配置类
@Import(MySelector.class) // 3. 通过 Selector 引入多个类
// @Import(MyRegistrar.class) // 4. 通过 beanDefinition 注册器
static class MyConfig {
}
static class MySelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Bean3.class.getName(), Bean4.class.getName()};
}
}
static class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("bean5", BeanDefinitionBuilder.genericBeanDefinition(Bean5.class).getBeanDefinition());
}
}
static class Bean5 {
}
static class Bean3 {
}
static class Bean4 {
}
static class Bean1 {
}
@Configuration
static class OtherConfig {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean2 {
}
}
DeferredImportSelector 使用
- 同一配置类中, @Import 先解析 @Bean 后解析
- 同名定义, 默认后面解析的会覆盖前面解析的
- 不允许覆盖的情况下, 如何能够让 MyConfig(主配置类) 的配置优先? (虽然覆盖方式能解决)
- DeferredImportSelector 最后工作, 可以简单认为先解析 @Bean, 再 Import
public class TestDeferredImport {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAllowBeanDefinitionOverriding(false); // 不允许同名定义覆盖
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
context.registerBean(MyConfig.class);
context.refresh();
System.out.println(context.getBean(MyBean.class));
}
// 1. 同一配置类中, @Import 先解析 @Bean 后解析
// 2. 同名定义, 默认后面解析的会覆盖前面解析的
// 3. 不允许覆盖的情况下, 如何能够让 MyConfig(主配置类) 的配置优先? (虽然覆盖方式能解决)
// 4. DeferredImportSelector 最后工作, 可以简单认为先解析 @Bean, 再 Import
@Configuration
@Import(MySelector.class)
static class MyConfig { // 主配置 - 程序员编写的
@Bean
public MyBean myBean() {
return new Bean1();
}
}
// DeferredImportSelector可以简单认为先解析 @Bean, 再 Import
static class MySelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{OtherConfig.class.getName()};
}
}
@Configuration
static class OtherConfig { // 从属配置 - 自动配置
@Bean
@ConditionalOnMissingBean // 当配置中没有就创建,有就不创建
public MyBean myBean() {
return new Bean2();
}
}
interface MyBean {
}
static class Bean1 implements MyBean {
}
static class Bean2 implements MyBean {
}
}
5. SpringBoot 自动配置原理
自动配置原理
@SpringBootApplication 是一个组合注解,由 @ComponentScan、@EnableAutoConfiguration 和 @SpringBootConfiguration 组成
-
@SpringBootConfiguration 与普通 @Configuration 相比,唯一区别是前者要求整个 app 中只出现一次
-
@ComponentScan
- excludeFilters - 用来在组件扫描时进行排除,也会排除自动配置类
-
public class TestExcludeFilter { public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory()); context.registerBean(MyConfig.class); context.registerBean(MyFilter.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } } @Configuration @ComponentScan(basePackages = {"day04.boot.sub"}, excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)}) static class MyConfig { } // 配合 @ComponentScan 使用, 对扫描到的元数据进行过滤, 返回 true 表示过滤掉, false 表示保留 static class MyFilter extends TypeExcludeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); LoggerUtils.get().debug("{}", className); if (className.equals(Bean1.class.getName())) { return true; } return false; } } }
-
@EnableAutoConfiguration 也是一个组合注解,由下面注解组成
- @AutoConfigurationPackage – 用来记住扫描的起始包
- @Import(AutoConfigurationImportSelector.class) 用来加载
META-INF/spring.factories
中的自动配置类 - 特点
- 分离主配置和从属配置,防止它们之间存在强耦合
- 执行优先级比较低,保证组配置先执行
-
public class TestAutoConfiguration { public static void main(String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory()); // context.getEnvironment().getPropertySources().addLast(new ResourcePropertySource("application.properties")); context.registerBean(MyConfig.class); context.refresh(); // System.out.println(AutoConfigurationPackages.get(context.getDefaultListableBeanFactory())); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } // System.out.println(SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, EnableAutoConfiguration.class.getClassLoader()).stream() // .filter(name -> !name.equals(OtherConfig.class.getName())).map(name -> "\"" + name + "\"").collect(Collectors.joining(",","{","}"))); } @Configuration // @AutoConfigurationPackage @EnableAutoConfiguration(excludeName = {"org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration", "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration", "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration", "org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration", "org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration", "org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration", "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration", "org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration", "org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration", "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration", "org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration", "org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration", "org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration", "org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration", "org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration", "org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration", "org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration", "org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration", "org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration", "org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration", "org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration", "org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration", "org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration", "org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration", "org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration", "org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration", "org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration", "org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration", "org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration", "org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration", "org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration", "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration", "org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration", "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration", "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration", "org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration", "org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration", "org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration", "org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration", "org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration", "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration", "org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration", "org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration", "org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration", "org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration", "org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration", "org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration", "org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration", "org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration", "org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration", "org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration", "org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration", "org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration", "org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration", "org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration", "org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration", "org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration", "org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration", "org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration", "org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration", "org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration", "org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration", "org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration", "org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration", "org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration", "org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration", "org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration", "org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration", "org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration", "org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration", "org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration", "org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration", "org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration", "org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration", "org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration", "org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration", "org.springframework.boot.autoconfigure.session.SessionAutoConfiguration", "org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration", "org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration", "org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration", "org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration", "org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration", "org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration", "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration", "org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration", "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration", "org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration", "org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration", "org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration", "org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration", "org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration", "org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration", "org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration", "org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration", "org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration", "org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration", "org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration", "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration", "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration", "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration", "org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration", "org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration", "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration", "org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration", "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration", "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration", "org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration", "org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration", "org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration", "org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration"}) // @Import(OtherConfig.class) static class MyConfig { // 主配置 @Bean public Bean1 bean1() { System.out.println("MyConfig bean1()"); return new Bean1(); } } @Configuration static class OtherConfig { // 从属配置(自动配置、默认配置) @Bean @ConditionalOnMissingBean public Bean1 bean1() { System.out.println("OtherConfig bean1()"); return new Bean1(); } @Bean public Bean2 bean2() { return new Bean2(); } } static class Bean1 { } static class Bean2 { } }
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于