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去掉


相关文章
|
6天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
12天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
22 0
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
8 2
|
2天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
4天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
17 2
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
6天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
16 2
|
12天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
22 0
|
5月前
|
XML Java 数据格式
④【Spring】IOC - 基于注解方式 管理bean
④【Spring】IOC - 基于注解方式 管理bean
49 0