框架篇

本贴最后更新于 476 天前,其中的信息可能已经渤澥桑田

wallhavenjxvk7q1920x1080.png

1.ApplicationContext refresh 的流程

ApplicationContext 为 Spring 的核心容器

refresh 是 ApplicationContext 的中的一个方法,其中会调用到 12 个方法

1.1.prepareRefresh

要点

  • 这一步创建和准备了 Environment 对象
  • 要理解 Environment 对象的作用

image.png

总结

  • 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 从何而来

image.png

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 的作用

image.png

总结

  • 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 后处理器

image.png

总结

  • beanFactory 后处理器,充当 beanFactory 的扩展点,可以用来补充或修改 BeanDefinition
  • ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource
  • PropertySourcesPlaceHolderConfigurer – 替换 BeanDefinition 中的 ${ }
  • MapperScannerConfigurer – 补充 Mapper 接口对应的 BeanDefinition

1.6.registerBeanPostProcessors

对 Bean 创建过程中进行增强,添加相应的后处理器

要点

  • 理解 bean 后处理器的作用
  • 掌握常见的 bean 后处理器

image.png

总结

  • 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 从何而来

image.png

总结

  • 实现国际化
  • 容器中一个名为 messageSource 的 bean,如果没有,则提供空的 MessageSource 实现

1.8.initApplicationEventMulticaster

要点

  • 理解事件广播器的作用
  • 事件广播器从何而来
  • 如何发布事件

image.png

总结

  • 用来发布事件给监听器
  • 可以从容器中找名为 applicationEventMulticaster 的 bean 作为事件广播器,若没有,也会新建默认的事件广播器
  • 可以调用 ApplicationContext.publishEvent(事件对象) 来发布事件

1.9.onRefresh

要点

  • 这一步是空实现,留给子类扩展
  • 掌握对应的设计模式

总结

  • SpringBoot 中的子类可以在这里准备 WebServer,即内嵌 web 容器
  • 体现的是模板方法设计模式

1.10.registerListeners

要点

  • 理解事件监听器作用
  • 监听器从何而来
  • 如何接收事件

image.png

总结

  • 用来接收事件
  • 一部分监听器是事先编程添加的、另一部分监听器来自容器中的 bean、还有一部分来自于 @EventListener 的解析
  • 实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e)方法即可

1.11.finishBeanFactoryInitialization

要点

  • 了解 conversionService
  • 了解内嵌值解析器
  • 单例池 - singletonObjects

image.png

总结

  • conversionService 也是一套转换机制,作为对 PropertyEditor 的补充
  • 内嵌值解析器用来解析 @Value 中的 ${ },借用的是 Environment 的功能
  • 单例池用来缓存所有单例对象,对象的创建都分三个阶段,每一阶段都有不同的 bean 后处理器参与进来,扩展功能

1.12.finishRefresh

要点

  • 了解 lifecycleProcessor
  • 它从何而来
  • 如何控制 lifecycle?
  • 发布 ContextRefreshed 事件

image.png

总结

  • 用来控制容器内需要生命周期管理的 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

image.png

阶段 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 切面顺序导致导致事务不能正确回滚

image.png

原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常…

解法:同情况 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 方法导致的事务失效

image.png

原因: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. 父子容器导致的事务失效

image.png

原因:子容器扫描范围过大,把未加事务配置的 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

image.png

面试题:SpringMVC 执行流程

SpringMVC 中的 DispatcherServlet 由 tomcat 创建与初始化的

image.png

初始化阶段:

1.在 Web 容器第一次用到 DispatcherServlet 的时候,会创建其对象并执行 init 方法

2.init 方法内会创建 Spring Web 容器,并调用容器 refresh 方法

3.refresh 过程中会创建并初始化 SpringMVC 中的重要组件,例如 MultipartResolver(文件上传时处理表单数据),HandlerMapping(请求映射),HandlerAdapter(调用和执行控制器方法),HandlerExceptionResolver(异常处理)、ViewResolver(解析成视图对象)等

4.容器初始化后,会将上一步初始化好的重要组件,赋值给 DispatcherServlet 的成员变量,留待后用

匹配阶段:

1.用户发送的请求统一到达前端控制器 DispatcherServlet

image.png

2.DispatcherServlet 遍历所有 HandlerMapping,找到与路径匹配的处理器

①HandlerMapping 有多个,每个 HandlerMapping 会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别 @RequestMapping 的优先级最高

② 对应 @RequestMapping 的处理器是 HandlerMethod,它包含了控制器对象和控制器方法信息

③ 其中路径与处理器的映射关系在 HandlerMapping 初始化时就会建立好

image.png

3.将 HandlerMethod 连同匹配到的拦截器,生成调用链对象 HandlerExecutionChain 返回

4.遍历 HandlerAdapter 处理器适配器,找到能处理 HandlerMethod 的适配器对象,开始调用

执行阶段:

1.执行拦截器 preHandle

2.由 HandlerAdapter 调用 HandlerMethod

① 调用前处理不同类型的参数

② 调用后处理不同类型的返回值

3.第 2 步没有异常

① 返回 ModelAndView

② 执行拦截器 postHandle 方法

③ 解析视图,得到 View 对象,进行视图渲染

image.png

4.第 2 步有异常,进入 HandlerExceptionResolver 异常处理流程

image.png

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 {
      
          }
      }
      
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    944 引用 • 1459 回帖 • 18 关注

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...