Spring5源码 - Spring IOC 注解复习

简介: Spring5源码 - Spring IOC 注解复习


Pre

为了更好地学习源码,我们有必要对基础知识进行一次简单的复习,只有在知道如何使用的基础上,再去阅读源码才能明白spring这些源码是对哪些功能的支持。

这里简单的梳理一下


xml配置文件

【配置文件 】

<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="artisan"  class="com.artisan.base.Artisan"/>
</beans>

【读取Bean】

ClassPathXmlApplicationContext cx = new ClassPathXmlApplicationContext("classpath:spring.xml");
System.out.println(cx.getBean("artisan").getClass().getSimpleName());
  }

【输出】


JavaConfig

【POJO】

public class Bean1 {
}

【配置文件 】

@Configuration
public class MainConfig {
  @Bean
  public Bean1 bean1(){
    return new Bean1();
  }
}

【读取Bean— 传入配置类】

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
    System.out.println(ac.getBean("bean1"));
  }

@Bean的形式, bean的默认名称是方法名,若@Bean(value=“bean的名称”) ,那么bean的名称是指定的名称。

【测试结果】


@CompentScan

在配置类上写@CompentScan注解来进行包扫描

【配置类】

package com.artisan.base.componentscan.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.artisan.base.componentscan")
public class CSMainConfig {
}

【c - s -d 】

package com.artisan.base.componentscan.controller;
import org.springframework.stereotype.Controller;
@Controller
public class ArtisanInfoController {
}
package com.artisan.base.componentscan.service;
import org.springframework.stereotype.Service;
@Service
public class ArtisanService {
}
package com.artisan.base.componentscan.dao;
import org.springframework.stereotype.Repository;
@Repository
public class ArtisanDao {
}

【测试类】

package com.artisan.base.componentscan;
import com.artisan.base.componentscan.config.CSMainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CMTest {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CSMainConfig.class);
    // 输出 单例池中的单例bean的名字
    for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
      System.out.println("bdName:" + beanDefinitionName);
    }
  }
}

【输出】


excludeFilters

【配置类】

import com.artisan.base.componentscan.service.ArtisanService;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(
    basePackages = {"com.artisan.base.componentscan"},
    excludeFilters = {
      @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
      @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
})
public class CSMainConfig {
}

重点配置

@ComponentScan(
    basePackages = {"com.artisan.base.componentscan"},
    excludeFilters = {
      @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
      @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
})

excludeFilters(排除@Controller注解的,和ArtisanService的)

【测试结果】


includeFilters

@Configuration
@ComponentScan(
    basePackages = {"com.artisan.base.componentscan"},
    includeFilters =
        {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})},
    useDefaultFilters = false)、
public class CSMainConfig {
}

若使用包含的用法, 需要把useDefaultFilters属性设置为false(true表示扫描全部的)

【测试结果】


@ComponentScan.Filter type的类型

看下源码

public enum FilterType {
  /**
   * Filter candidates marked with a given annotation.
   * @see org.springframework.core.type.filter.AnnotationTypeFilter
   */
  ANNOTATION,
  /**
   * Filter candidates assignable to a given type.
   * @see org.springframework.core.type.filter.AssignableTypeFilter
   */
  ASSIGNABLE_TYPE,
  /**
   * Filter candidates matching a given AspectJ type pattern expression.
   * @see org.springframework.core.type.filter.AspectJTypeFilter
   */
  ASPECTJ,
  /**
   * Filter candidates matching a given regex pattern.
   * @see org.springframework.core.type.filter.RegexPatternTypeFilter
   */
  REGEX,
  /** Filter candidates using a given custom
   * {@link org.springframework.core.type.filter.TypeFilter} implementation.
   */
  CUSTOM
}
  • ANNOTATION 注解形式 举个例子比如 @Compent @Controller @Service @Repository .......
  • ASSIGNABLE_TYPE 指定类型的 FilterType.ASSIGNABLE_TYPE 举个例子
@ComponentScan.Filter(type =
FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
  • ASPECTJ 类型的 FilterType.ASPECTJ 不常用
  • REGEX 正则表达式的 FilterType.REGEX 不常用
  • CUSTOM 自定义类型

使用自定义过滤器CUSTOM

package com.artisan.base.componentscan.customFilterType;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class ArtisanFilterType  implements TypeFilter {
  @Overridez
  public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    //获取当前类的注解源信息
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    //获取当前类的class的源信息
    ClassMetadata classMetadata = metadataReader.getClassMetadata();
    //获取当前类的资源信息
    Resource resource =  metadataReader.getResource();
    System.out.println("类的路径:"+classMetadata.getClassName());
    // 排除包含Artisan的Bean
    if(classMetadata.getClassName().contains("Artisan")) {
      return true;
    }
    return false;
  }
}

【配置类】

@Configuration
@ComponentScan(basePackages = {"com.artisan.base.componentscan"},
    excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {ArtisanFilterType.class})})
public class CSMainConfig {
}

【测试结果】


@Scope

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes


public class Bean2 {
  public Bean2() {
    System.out.println("Bean2 Created");
  }
}
public class Bean3 {
  public Bean3() {
    System.out.println("Bean3 Created");
  }
}

【config】

package com.artisan.base.scope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class Config {
  @Bean()
  public Bean2 bean2(){
    return new Bean2();
  }
  @Bean()
  @Scope(value = "prototype")
  public Bean3 bean3(){
    return new Bean3();
  }
}

【测试bean的加载】

package com.artisan.base.scope;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestScope {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
    System.out.println("========================");
    // 每次调用都会实例化一个新的Bean3+
    System.out.println(ac.getBean("bean3"));
    System.out.println(ac.getBean("bean3"));
  }
}

【输出】


【结论】

  • 在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载 即 容器启动实例就创建好了
  • 指定@Scope为 prototype 表示为原型bean,而且还是懒汉模式加载 , 即IOC容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建 ,并且每次调用,都会实例化一个新的对象

@Lazy

Bean的懒加载@Lazy 主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象 ,后续调用不会新建对象,而是从单例池中获取缓存的bean。

继续使用上面的例子 ,给Bean2 加上Lazy注解

@Bean()
  @Lazy
  public Bean2 bean2(){
    return new Bean2();
  }

【测试】

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestScope {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
    System.out.println(ac.getBean("bean2"));
    System.out.println(ac.getBean("bean2"));
  }
}

【结果】


@Conditional 条件判断

需求: 只有容器中有bean5 才装载bean6


value 是一个class数组, 需要实现Condition 接口 , 那我们也弄个呗

【自定义Condition 】

package com.artisan.base.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ArtisanCondition implements Condition {
  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    //判断容器中是否有Bean5 ,有的话 返回true
    if(context.getBeanFactory().containsBean("bean5")) {
      return true;
    }
    return false;
  }
}

【config 】

package com.artisan.base.condition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDConfig {
  @Bean
  public Bean5 bean5(){
    return new Bean5();
  }
  // Conditional的条件返回true,才装载Bean6
  @Bean
  @Conditional(value = ArtisanCondition.class)
  public Bean6 bean6(){
    return new Bean6();
  }
}

【beans】

public class Bean5 {
  public Bean5() {
    System.out.println("Bean5 Created");
  }
}
public class Bean6 {
  public Bean6() {
    System.out.println("Bean6 Created");
  }
}

【正常情况】

package com.artisan.base.condition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CDTest {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CDConfig.class);
    for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
      System.out.println(beanDefinitionName);
    }
  }
}

我们看到了 CDConfig中 bean5 上标注了@Bean

现在我们把Bean5的@Bean去掉


相关文章
|
20天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
41 2
|
25天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
39 4
|
25天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
77 2
|
25天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
41 2
|
28天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
36 2
|
29天前
|
JSON Java 数据格式
springboot常用注解
@RestController :修饰类,该控制器会返回Json数据 @RequestMapping(“/path”) :修饰类,该控制器的请求路径 @Autowired : 修饰属性,按照类型进行依赖注入 @PathVariable : 修饰参数,将路径值映射到参数上 @ResponseBody :修饰方法,该方法会返回Json数据 @RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中 @Controller@Service@Compont: 将类注册到ioc容器
|
25天前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
64 0
|
4月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
6月前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
50 1
|
3月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
252 18