一.基本概念

组件:具有一定功能的对象

容器:管理组件(创建、获取、保存、销毁)

IOC:Inversion of Control(控制反转)

  • 控制:资源的创建、获取、销毁
  • 反转:和传统方式不同

DI:Dependency Injection(依赖注入)

  • 依赖:组件的依赖关系
  • 注入:通过setter方法、构造器等方式自动注入

二.组件注册

1.主类

运行时返回ioc容器

@SpringBootApplication
public class Demo01Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext ioc =SpringApplication.run(Demo01Application.class, args);
    }
​
}

使用以下命令获得所有组件名称

String[] beanNames = ioc.getBeanDefinitionNames();

2.bean组件

a.注册bean组件

首先在bean文件夹中创建类(@Data注解为lombok自动补全)

@Data
public class Person {
     private String name;
     private int age;
     private String gender;
}

然后在主类中创建方法,返回该类

加上@Bean(“名字”)注解,此时组件就注册好了,组件名默认是方法名

@Bean
public Person zhangsan() {
  Person person = new Person();
  person.setName("zhang");
  person.setAge(18);
  person.setGender("男");
  return person;
}
  • 组件的创建时期:容器的启动过程中就会创建组件对象
  • 组件的默认模式:单例

b.手动获取bean组件

名字获取bean组件,默认为object对象,需要时可以强转为指定类型

Object zhangsan = ioc.getBean("zhangsan");

类型获取单一bean组件,默认为原类型,无须强转

Person bean = ioc.getBean(Person.class);

类型获取所有bean组件,默认为map集合

Map<String, Person> beansOfType = ioc.getBeansOfType(Person.class);

按照类型+名字获取单一bean组件,默认原类型

异常情况:
  • 组件不存在,抛异常
  • 组件不唯一
    • 类型只获取一个组件,抛异常
    • 名字只获取一个组件,不会抛异常,会返回排在最前面的方法

3.Configuration配置类

@Configuration 标注配置类,可用于配置bean组件

@Configuration
public class PersonConfig {
  @Bean
  public Person zhangsan() {
      return new Person("zhangsan", 20, "男");
  }
}

4.SpringMVC分层注解

给人看的,实际都是Component

@Controller控制层
@Service服务层
@Repository持久层
@Component组件

5.ComponentScan组件扫描

@ComponentScan(basePackages = "com.zhang.demo1")

写在配置类上

扫描当前包及其子包,注册了的组件

6.Import导入外部类

外部类无法标注解,只能导入

@Import(xxx.class)

写在配置类上

7.Scope作用域

@Scope("prototype")非单实例
@Scope("singleton")单实例,默认值
@Scope("request")同一请求单实例
@Scope("session")同一会话单实例

8.Lazy懒加载

默认为饿汉式加载,修改为懒汉式加载

@Lazy

标注在@Bean的上方

懒汉加载是指在首次使用时才创建对象实例,而饿汉加载则是在类加载时就创建对象实例。

9.FactoryBean工厂

代替@Bean注解标注组件

适用场景:制造对象复杂的时候

@Component
public class BYD implements FactoryBean {

  @Override
  public Object getObject() throws Exception {
      return new Car();
  }

  @Override
  public Class<?> getObjectType() {
      return Car.class;
  }

  @Override
  public boolean isSingleton() {
      return FactoryBean.super.isSingleton();
  }
}

10.Conditional条件注册

@Contitional(xxx.class)

标注在@bean的上方,需要添加一个条件类

public class windowsCondition implements Condition {
  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      return context.getEnvironment().getProperty("os").contains("mac");
  }
}

此外还有多种特殊条件注解

  1. @Conditional:这是最基本的条件注解,它可以接受一个或多个Condition接口的实现类作为参数。只有当所有条件都满足时,才会创建或注册Bean。
  2. @ConditionalOnBean:这个注解用于检查指定的Bean是否存在于Spring容器中。如果存在,则创建或注册当前Bean。
  3. @ConditionalOnMissingBean:与@ConditionalOnBean相反,这个注解用于检查指定的Bean是否不存在于Spring容器中。如果不存在,则创建或注册当前Bean。
  4. @ConditionalOnClass:这个注解用于检查指定的类是否存在于类路径中。如果存在,则创建或注册当前Bean。
  5. @ConditionalOnMissingClass:与@ConditionalOnClass相反,这个注解用于检查指定的类是否不存在于类路径中。如果不存在,则创建或注册当前Bean。
  6. @ConditionalOnProperty:这个注解用于检查指定的属性是否存在于配置文件中,并且其值是否满足指定的条件。如果满足条件,则创建或注册当前Bean。
  7. @ConditionalOnResource:这个注解用于检查指定的资源是否存在于类路径中。如果存在,则创建或注册当前Bean。
  8. @ConditionalOnWebApplication:这个注解用于检查当前应用是否是一个Web应用。如果是,则创建或注册当前Bean。
  9. @ConditionalOnNotWebApplication:与@ConditionalOnWebApplication相反,这个注解用于检查当前应用是否不是一个Web应用。如果不是,则创建或注册当前Bean。

三.组件注入

1.基本注解

  1. @Autowired: 注入方式:默认按照类型(byType)进行注入。如果有多个相同类型的Bean,Spring会尝试按照名称(byName)进行注入。如果仍然无法确定唯一的Bean,Spring会抛出异常。 使用场景:适用于大多数情况,尤其是当只有一个匹配的Bean时。
  2. @Resource: 注入方式:默认按照名称(byName)进行注入。如果没有找到匹配的名称,则按照类型(byType)进行注入。 使用场景:适用于需要明确指定名称进行注入的情况,或者在Java标准库中使用。
  3. @Qualifier: 注入方式:与@Autowired或@Inject一起使用,用于指定具体要注入的Bean。 使用场景:当有多个相同类型的Bean时,通过指定名称或其他限定符来明确注入的Bean。
  4. @Primary: 注入方式:当有多个相同类型的Bean时,使用@Primary注解来指定首选的Bean。当没有使用@Qualifier注解时,Spring会优先注入使用@Primary注解的Bean。 使用场景:适用于有多个相同类型的Bean,但希望有一个默认的首选Bean的情况。

2.构造器注入:

@Component
public class dog {
  private Person person;
   
  public dog(Person person) {
      this.person = person;
  }
}

组件中可以根据构造方法的参数自动注入

在参数前可以用@Qualifier

3.xxxAware感知接口

@Component
public class dog implements EnvironmentAware {

  @Override
  public void setEnvironment(Environment environment) {
       
  }
}
  1. BeanNameAware:获取Bean的名称。
  2. BeanFactoryAware:获取BeanFactory实例。
  3. ApplicationContextAware:获取ApplicationContext实例。
  4. MessageSourceAware:获取国际化消息源。
  5. ApplicationEventPublisherAware:获取事件发布器。
  6. ResourceLoaderAware:获取资源加载器

4.Value属性赋值

加在组件的属性上方

1.直接赋值

@Value("指定值")

2.从配置文件xxx.properties获取值

@Value("${配置文件key}")

设置默认值
以:分割,当取不到cat.name时,默认为Tom

@Value("${cat.name:Tom}")

3.进行计算赋值

@Value("#{10*20}")

4.调用方法赋值

@Value("'zhangsan'.toUpperCase()")

5.静态调用类赋值

@Value("T(java.lang.Math).random()")

5.属性来源

写在类上,注明@Value取值的文件

classpath:从项目路径下找

classpath*:从所有包路径下找

@PropertySource("classpath:application.properties")

6.多环境

在指定环境下加载组件

在类或者方法上标注

@Profile("环境标识,环境标识...")

项目默认为default环境

可以在配置文件中修改激活环境

spring.profiles.active=环境

四、组件生命周期

1.@Bean自定义初始化/销毁方法

在类中定义相关方法

public void initUser() {
  System.out.println("初始化---------------");
}

public void destroyUser() {
  System.out.println("销毁----------------");
}

@Bean注册为组件时,指明方法

@Bean(initMethod = "initUser", destroyMethod = "destroyUser", name = "myUser")
public Users user() {
  return new Users();
}

2.intializingBean,disposableBean接口

intializingBean:在init之前执行

disposableBean:在destory之前执行


@Data
public class Users implements InitializingBean, DisposableBean{
    private String name;
    private int age;
​
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化");
    }
​
    @Override
    public void destroy() throws Exception {
        System.out.println("销毁");
    }
}

3.@PostConstruct,@PreDestory方法

@PostConstruct(构造器后置处理钩子):在intializingBean之前

@PreDestory(销毁预处理钩子):在disposableBean之前

@PostConstruct
public void init(){
  System.out.println("PostConstruct");
}

@PreDestroy
public void destroy(){
  System.out.println("PreDestroy");
}

4.BeanPostProcessor后置处理器

BeanPostProcessor是一个拦截所有Bean的后置处理器

@Component
public class beanPost implements BeanPostProcessor {
     @Override
      public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
           System.out.println("postProcessBeforeInitialization====="+beanName);
           return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName)
}
​
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization====="+beanName);
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
将其实现后放到容器中,将拦截所有注册的Bean

@Autowired是如何实现的?
1、专门有一个处理@Autowired注解的AutowiredAnnotationBeanPostProcessor
2、每个Bean创建以后,会调用BeanPostProcessBeforeInitalization方法
3、postProcessBeforeInitialization里面就会利用反射,得到当前Bean的所有属性,利用反射,得到Bean属性上标注的所有注解,看有没有@Autowired注解
4、如果有,去容器中找到这个属性对应的组件(按类型、名字)找到。

5.总结

  1. @Bean创建对象
  2. 调用类构造器
  3. postProcessBeforeInitialization
  4. @Autowired set属性注入
  5. PostConstruct
  6. InitializingBean(afterPropertiesSet属性设置之后)
  7. init
  8. postProcessAfterInitialization
  9. —————–ioc容器创建完毕——————-
  10. PreDestroy
  11. DisposableBean
  12. destroy

补充:@Autowired 是如何实现的?

  1. 专门有一个处理@Autowired注解的AutowiredAnnotationBeanPostProcessor
  2. 每个 Bean 创建以后,会调用BeanPostProcessorpostProcessBeforeInitialization方法。
  3. postProcessBeforeInitialization里面就会利用反射,得到当前 Bean 的所有属性,利用反射,得到 Bean 属性上标注的所有注解,看有没有@Autowired注解。
  4. 如果有,去容器中找到这个属性对应的组件(按类型,按名字)找到。

——————-容器篇结束——————–

天下繁华,唯有一心
最后更新于 2025-03-17