• spring 框架理论


    Spring Ioc

    对于 Spring IoC 的理解

    IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。

    为什么叫控制反转?

    • 控制 :指的是对象创建(实例化、管理)的权力
    • 反转 :控制权交给外部环境(Spring 框架、IoC 容器)

    将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。

    在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。


    DI(注入)

    Spring提供了三种注入方式:

    • 构造方法注入。
    • setter方法注入。
    • 注解方式注入。
    什么是 Spring Bean?

    简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象

    我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。

    <!-- Constructor-arg with 'value' attribute -->
    <bean id="..." class="...">
       <constructor-arg value="..."/>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    将一个类声明为 Bean 的注解有哪些?
    • @Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component注解标注。
    • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
    • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
    • @Controller : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。

    @Component 和 @Bean 的区别是什么?
    • @Component 注解作用于类,而@Bean注解作用于方法。

    • @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan注解定义要扫描的路径从中找出标识了需要装配的类自动装配Springbean 容器中)。@Bean注解通常是我们在标有该注解的方法中定义产生这个bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。

    • @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

      @Bean注解使用示例:

      @Configuration
      public class AppConfig {
          @Bean
          public TransferService transferService() {
              return new TransferServiceImpl();
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      上面的代码相当于下面的 xml 配置

      <beans>
          <bean id="transferService" class="com.acme.TransferServiceImpl"/>
      </beans>
      
      • 1
      • 2
      • 3

      下面这个例子是通过 @Component 无法实现的。

      @Bean
      public OneService getService(status) {
          case (status)  {
              when 1:
                      return new serviceImpl1();
              when 2:
                      return new serviceImpl2();
              when 3:
                      return new serviceImpl3();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

    注入 Bean 的注解有哪些?

    Spring 内置的 @Autowired 以及 JDK 内置的 @Resource@Inject 都可以用于注入 Bean。
    在这里插入图片描述
    @Autowired 和@Resource使用的比较多一些。

    @Autowired 和 @Resource 的区别是什么?

    Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。

    这会有什么问题呢? 当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。

    这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说下面代码中的 smsService 就是我这里所说的名称

    // smsService 就是我们上面所说的名称
    @Autowired
    private SmsService smsService;
    
    • 1
    • 2
    • 3

    举个例子,SmsService 接口有两个实现类: SmsServiceImpl1SmsServiceImpl2,且它们都已经被 Spring 容器所管理。

    // 报错,byName 和 byType 都无法匹配到 bean
    @Autowired
    private SmsService smsService;
    // 正确注入 SmsServiceImpl1 对象对应的 bean
    @Autowired
    private SmsService smsServiceImpl1;
    // 正确注入  SmsServiceImpl1 对象对应的 bean
    // smsServiceImpl1 就是我们上面所说的名称
    @Autowired
    @Qualifier(value = "smsServiceImpl1")
    private SmsService smsService;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们还是建议通过 @Qualifier 注解来显示指定名称而不是依赖变量的名称。

    @Resource属于 JDK 提供的注解,默认注入方式为 byName。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType

    @Resource 有两个比较重要且日常开发常用的属性:name(名称)、type(类型)。

    public @interface Resource {
        String name() default "";
        Class<?> type() default Object.class;
    }
    
    • 1
    • 2
    • 3
    • 4

    如果仅指定 name 属性则注入方式为byName,如果仅指定type属性则注入方式为byType,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName

    // 报错,byName 和 byType 都无法匹配到 bean
    @Resource
    private SmsService smsService;
    // 正确注入 SmsServiceImpl1 对象对应的 bean
    @Resource
    private SmsService smsServiceImpl1;
    // 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
    @Resource(name = "smsServiceImpl1")
    private SmsService smsService;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    简单总结一下:

    • @AutowiredSpring 提供的注解,@ResourceJDK 提供的注解。
    • Autowired 默认的注入方式为byType(根据类型进行匹配),@Resource默认注入方式为 byName(根据名称进行匹配)。
    • 当一个接口存在多个实现类的情况下,@Autowired@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显示指定名称,@Resource可以通过 name属性来显示指定名称。

    Bean 的作用域有哪些?

    Spring 中 Bean 的作用域通常有下面几种:

    • singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
    • prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean
      实例。
    • request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前
      HTTP request 内有效。
    • session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话
      bean),该 bean 仅在当前 HTTP session 内有效。
    • application/global-session (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用
      Bean),,该 bean 仅在当前应用启动时间内有效。
    • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

    如何配置 bean 的作用域呢?
    xml 方式:

    <bean id="..." class="..." scope="singleton"></bean>
    
    
    • 1
    • 2

    注解方式:

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Person personPrototype() {
        return new Person();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    单例 Bean 的线程安全问题了解吗?

    大部分时候我们并没有在项目中使用多线程,所以很少有人会关注这个问题。单例 Bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。

    常见的有两种解决办法:

    • 1、在 Bean 中尽量避免定义可变的成员变量。
    • 2、在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

    不过,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。


    Bean 的生命周期了解么?

    对于普通的Java对象来说,它们的生命周期就是:

    • 实例化
    • 对象不再被使用时通过垃圾回收机制进行回收

    而对于Spring Bean的生命周期来说,可以分为四个阶段,其中初始化完成之后,就代表这个Bean可以使用了:

    • 实例化 Instantiation
    • 属性赋值 Populate
    • 初始化 Initialization
    • 销毁 Destruction

    Bean实例化的时机也分为两种,BeanFactory管理的Bean是在使用到Bean的时候才会实例化Bean,ApplicantContext管理的Bean在容器初始化的时候就回完成Bean实例化。

    Bean详细生命周期
    在这里插入图片描述
    Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。
    在这里插入图片描述
    在这里插入图片描述

    说明:

    • Bean 容器找到配置文件中 Spring Bean 的定义。
    • Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
    • 如果涉及到一些属性值 利用 set()方法设置一些属性值。
    • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
    • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入ClassLoader对象的实例。
    • 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入BeanFactory对象的实例。
    • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
    • 如果有和加载这个 BeanSpring 容器相关的 BeanPostProcessor对象,执行postProcessBeforeInitialization() 方法
    • 如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
    • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
    • 如果有和加载这个 BeanSpring 容器相关的 BeanPostProcessor对象,执行postProcessAfterInitialization() 方法
    • 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
    • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

    代码演示(基本跟上图是一样的流程):

    package com.example.demo.test.beantest;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.*;
    
    public class Person implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
        private String name;
        private String address;
        private int phone;
    
        private BeanFactory beanFactory;
        private String beanName;
    
        public Person() {
            System.out.println("7、【构造器】调用Person的构造器实例化");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            System.out.println("10、【注入属性】注入属性name");
            this.name = name;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            System.out.println("9、【注入属性】注入属性address");
            this.address = address;
        }
    
        public int getPhone() {
            return phone;
        }
    
        public void setPhone(int phone) {
            System.out.println("11、【注入属性】注入属性phone");
            this.phone = phone;
        }
    
        @Override
        public String toString() {
            return "Person [address=" + address + ", name=" + name + ", phone="
                    + phone + "]";
        }
    
        // 这是BeanFactoryAware接口方法
        @Override
        public void setBeanFactory(BeanFactory arg0) throws BeansException {
            System.out.println("13、【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory()");
            this.beanFactory = arg0;
        }
    
        // 这是BeanNameAware接口方法
        @Override
        public void setBeanName(String arg0) {
            System.out.println("12、【BeanNameAware接口】调用BeanNameAware.setBeanName()");
            this.beanName = arg0;
        }
    
        // 这是InitializingBean接口方法
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("15、【InitializingBean接口】调用InitializingBean.afterPropertiesSet()");
        }
    
        // 这是DiposibleBean接口方法
        @Override
        public void destroy() throws Exception {
            System.out.println("22、【DiposibleBean接口】调用DiposibleBean.destory()");
        }
    
        // 通过的init-method属性指定的初始化方法
        public void myInit() {
            System.out.println("16、【init-method】调用的init-method属性指定的初始化方法");
        }
    
        // 通过的destroy-method属性指定的初始化方法
        public void myDestory() {
            System.out.println("23、【destroy-method】调用的destroy-method属性指定的初始化方法");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    package com.example.demo.test.beantest;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        public MyBeanFactoryPostProcessor() {
            super();
            System.out.println("2、这是BeanFactoryPostProcessor实现类构造器!!");
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
                throws BeansException {
            System.out.println("3、BeanFactoryPostProcessor调用postProcessBeanFactory方法");
            BeanDefinition bd = arg0.getBeanDefinition("person");
            bd.getPropertyValues().addPropertyValue("phone", "110");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    package com.example.demo.test.beantest;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPostProcessor implements BeanPostProcessor {
        public MyBeanPostProcessor() {
            super();
            System.out.println("4、这是BeanPostProcessor实现类构造器!!");
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public Object postProcessAfterInitialization(Object arg0, String arg1)
                throws BeansException {
            System.out.println("17、BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!");
            return arg0;
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object arg0, String arg1)
                throws BeansException {
            System.out.println("14、BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!");
            return arg0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    package com.example.demo.test.beantest;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.PropertyValues;
    import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
    
    import java.beans.PropertyDescriptor;
    
    public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
        public MyInstantiationAwareBeanPostProcessor() {
            super();
            System.out.println("5、这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!!");
        }
    
        // 接口方法、实例化Bean之前调用
        @Override
        public Object postProcessBeforeInstantiation(Class beanClass,
                                                     String beanName) throws BeansException {
            System.out.println("6、InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法");
            return null;
        }
    
        // 接口方法、实例化Bean之后调用
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException {
            System.out.println("18、InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法");
            return bean;
        }
    
        // 接口方法、设置某个属性时调用
        @Override
        public PropertyValues postProcessPropertyValues(PropertyValues pvs,
                                                        PropertyDescriptor[] pds, Object bean, String beanName)
                throws BeansException {
            System.out.println("8、InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法");
            return pvs;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
           xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    
        <bean id="beanPostProcessor" class="com.example.demo.test.beantest.MyBeanPostProcessor">
        </bean>
    
        <bean id="instantiationAwareBeanPostProcessor" class="com.example.demo.test.beantest.MyInstantiationAwareBeanPostProcessor">
        </bean>
    
        <bean id="beanFactoryPostProcessor" class="com.example.demo.test.beantest.MyBeanFactoryPostProcessor">
        </bean>
    
        <bean id="person" class="com.example.demo.test.beantest.Person" init-method="myInit"
              destroy-method="myDestory" scope="singleton" p:name="张三" p:address="广州"
              p:phone="1590000" />
    
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    package com.example.demo.test.beantest;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class BeanLifeCycle {
        public static void main(String[] args) {
    
            System.out.println("1、现在开始初始化容器");
    
            ApplicationContext factory = new ClassPathXmlApplicationContext("classpath*:beans.xml");
            System.out.println("19、容器初始化成功");
            //得到Preson,并使用
            Person person = factory.getBean("person",Person.class);
            System.out.println(person); // 20
    
            System.out.println("21、现在开始关闭容器!");
            ((ClassPathXmlApplicationContext)factory).registerShutdownHook();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行结果:

    1、现在开始初始化容器
    	15:19:30.624 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@707f7052
    	15:19:30.765 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 4 bean definitions from URL [file:/E:/workspace/datasource/demo/target/classes/beans.xml]
    	15:19:30.797 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanFactoryPostProcessor'
    2、这是BeanFactoryPostProcessor实现类构造器!!
    3、BeanFactoryPostProcessor调用postProcessBeanFactory方法
    	15:19:30.820 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanPostProcessor'
    4、这是BeanPostProcessor实现类构造器!!
    	15:19:30.820 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'instantiationAwareBeanPostProcessor'
    5、这是InstantiationAwareBeanPostProcessorAdapter实现类构造器!!
    	15:19:30.824 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'person'
    6、InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法
    7、【构造器】调用Person的构造器实例化
    8、InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法
    9、【注入属性】注入属性address
    10、【注入属性】注入属性name
    11、【注入属性】注入属性phone
    12、【BeanNameAware接口】调用BeanNameAware.setBeanName()
    13、【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory()
    14、BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!
    15、【InitializingBean接口】调用InitializingBean.afterPropertiesSet()
    16、【init-method】调用<bean>的init-method属性指定的初始化方法
    17、BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!
    18、InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法
    19、容器初始化成功
    Person [address=广州, name=张三, phone=110]
    21、现在开始关闭容器!
    	15:19:30.888 [SpringContextShutdownHook] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@707f7052, started on Sat Nov 05 15:19:30 CST 2022
    22、【DiposibleBean接口】调用DiposibleBean.destory()
    23、【destroy-method】调用<bean>的destroy-method属性指定的初始化方法
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    循环依赖问题以及解决方案

    方法一:懒加载的形式——@Lazy

    (未论证)使用懒加载的方式,所有待注入的对象都是在直接调用的时候创建,比如说A中注入B在创建A的时候并不是直接创建A对象而是返回A的代理对象,在代理A的过程中我们不创建B对象,而是将getB()方法前面添加this.b = new B();通过这样,达到只有当我们使用对象时才去创建它,因此就不存在循环问题了。
    在这里插入图片描述
    方法二:改用Setter方式注入
    所谓的循环依赖指的就是在A对象的构造方法中Spring要注入B,而在B对象中Spring要注入A。这个时候会形成一个闭环因为Spring不知道该先注入哪一个接着会抛出异常。而Spring建议的处理方式是说如果遇到这种情况的话就改用Setter方式注入。


    读取配置文件(application.properties/yml)信息

    可参考:https://blog.csdn.net/a924382407/article/details/125906079
    数据源application.yml内容如下

    my-profile:
      name: dfdd
      email: zhuwang@163.com
    
    library:
      location: 中华人名共和国
      books:
        - name: 天才基本法
          description: 防辐射的发挥是是
        - name: 时间的秩序
          description: 发顺丰是粉红色的和
        - name: 了不起的我
          description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻?
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1、@Value(常用)
    使用 @Value("${property}") 读取比较简单的配置信息:

    @Value("${server.port}")
    private Integer serverPort;
    
    • 1
    • 2

    2、@ConfigurationProperties(常用)

    @ConfigurationProperties主要是一个读取配置文件信息的注解,可以按照配置的前缀信息读取对应的配置信息。可以作用于方法上。

    • application.properties中写入以下信息

      test.properties.name=1111
      test.properties.age=12
      test.properties.note=gwgasfsdfa
      test.properties.location-info=vsgfrqwfw
      
      • 1
      • 2
      • 3
      • 4
    • 创建对应的实体类(加在类上面)

      /**
       * 1)注解@ConfigurationProperties中的prefix用于设置前缀
       * 2)下方的属性名称必须和要获取的配置信息名称一致,比如必须叫port,否则获取值为null
       */
      @ConfigurationProperties(prefix = "server")//这个注解是用找到类
      @Component  //生效的两种方式:方式1:配置@Component,方式2:启动类添加 @EnableConfigurationProperties(ReadProperties.class)		 
      @Data
      public class MyConfigurationProperties {
          String name;
          int age;
          String note;
       
          /**
           * 驼峰命名
           * driver-class-name”这种带横杠的情况,在POJO里面的命名规则是 下划线转驼峰 就可以绑定成功,所以就是“driverClassName”
           */
          String locationInfo;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    • 或者可以加在(方法)上面

      @Configuration
      public class PropertiesConfiguration {
       
          @Bean("myConfigurationProperties")
          @ConfigurationProperties(prefix = "test.properties")
          public MyConfigurationProperties myConfigurationProperties(){
              return new MyConfigurationProperties();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 使用

      @Autowired
      MyConfigurationProperties myConfigurationProperties;
      
      • 1
      • 2
    @Component
    @ConfigurationProperties(prefix = "library")
    class LibraryProperties {
        @NotEmpty
        private String location;
        private List<Book> books;
    
        @Setter
        @Getter
        @ToString
        static class Book {
            String name;
            String description;
        }
      省略getter/setter
      ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3、@PropertySource(不常用)
    @PropertySource读取指定 properties 文件

    4、Environment获取属性值

    # 属性配置类的
    server:
      port: 8082
    
    spring:
      main:
        banner-mode: console
    
    # 自定义
    alipay:
      pay:
        appid: 123456
        notify: http://www.xxx.com
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    @RestController
    public class ReadPropertiesEnvironment {
    
        @Autowired
        private Environment environment;
    
    
        @GetMapping("/read/file")
        public Map<String,Object> readInfo(){
            Map<String,Object> map = new HashMap<>();
            map.put("port",environment.getProperty("server.port"));
            map.put("appid",environment.getProperty("alipay.pay.appid"));
            map.put("notify",environment.getProperty("alipay.pay.notify"));
            map.put("javaversion",environment.getProperty("java.version"));
            map.put("javahome",environment.getProperty("JAVA_HOME"));
            map.put("mavenhome",environment.getProperty("MAVEN_HOME"));
            return  map;
        }
    
        public static void main(String[] args) {
            Properties properties = System.getProperties();
            Set<String> strings = properties.stringPropertyNames();
            for (String string : strings) {
                System.out.println(string+"===>"+properties.get(string));
            }
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    5、使用原生方式读取配置文件

    @SpringBootApplication
    public class DemoApplication implements InitializingBean {
     
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
     
        @Override
        public void afterPropertiesSet() throws Exception {
            Properties props = new Properties();
            try {
                InputStreamReader inputStreamReader = new InputStreamReader(
                        this.getClass().getClassLoader().getResourceAsStream("application.properties"),
                        StandardCharsets.UTF_8);
                props.load(inputStreamReader);
            } catch (IOException e1) {
                System.out.println(e1);
            }
            System.out.println("Properties Name:" + props.getProperty("profile.name"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    微服务开发中,使用AOP和自定义注解实现对权限的校验
    C++11之委派构造函数
    Excel中实用的3个数据透视表操作技巧,简单高效!
    Linux Command echo
    200个经典C语言程序
    Windows电脑上的多开软件是否安全?
    【ACL 2022】用于多标签文本分类的对比学习增强最近邻机制
    借助PLC-Recorder,汇川中型PLC(AM、AC系列,CODESYS平台)2ms高速采集的方法
    48 路径总和 III
    IntelliJ IDEA 2022.2 正式发布:已完全支持 Spring 6 和 Spring Boot 3了吗?
  • 原文地址:https://blog.csdn.net/weixin_45080272/article/details/127702609