spring依赖查找、依赖注入深入学习及源码分析

news2025/2/24 5:05:53

文章目录

  • 一、依赖查找
    • 1、单一类型依赖查找(接口 - BeanFactory)
      • 根据Bean名称查找
      • 根据Bean类型实时查找
      • 根据Bean类型延迟查找(Spring 5.1)
      • 根据Bean名称+类型查找
    • 2、集合类型依赖查找(接口 - ListableBeanFactory)
      • 根据Bean类型查找
      • 通过注解类型查找 Spring 4.0
    • 3、层次性依赖查找(接口 - HierarchicalBeanFactory)
      • 层次性容器使用场景
      • 层次性依赖查找实例
      • 使用BeanFactoryUtils查找Bean
    • 4、延迟依赖查找(接口 - ObjectFactory)
      • 理解什么是延迟依赖查找
      • 熟悉ObjectFactory的子接口-ObjectProvider接口
      • 延迟加载ObjectProvider使用示例
    • 5、依赖查找的异常安全性
      • 安全依赖查找代码实例
      • 由依赖查找的异常安全性做出的总结
    • 6、Spring内建可查找的依赖
      • AbstractApplicationContext 内建可查找的依赖
      • 注解驱动 Spring 应用上下文内建可查找的依赖
    • 7、依赖查找中的经典异常
    • 附1:各个BeanFactory的继承关系
    • 附2:ApplicationContext继承关系
  • 二、依赖注入
    • 1、自动绑定(Autowiring-区别于@Autowired)
    • 2、Setter 方法注入
      • 自动Setter注入
      • 手动Setter注入
    • 3、构造器注入(官方推荐)
      • 自动构造器注入
      • 手动构造器注入
    • 4、字段注入
    • 5、方法注入
    • 6、接口回调注入
    • 7、各种依赖注入比较
    • 8、基础类型的注入
    • 9、集合类型注入
    • 10、限定注入
      • 使用注解 @Qualifier 通过Bean 名称限定
      • 使用注解 @Qualifier 通过分组限定
      • 基于注解 @Qualifier 扩展限定
      • 案例
    • 11、延迟依赖注入
    • 12、Optional类型注入
    • 13、依赖注入处理的过程源码分析
    • 14、详解@Autowired 注入
    • 15、JSR-330 @Inject 注入
    • 16、Java通用注解注入原理
    • 17、自定义依赖注入注解
      • 基于@Autowired元注解创建自定义注解
      • 基于 AutowiredAnnotationBeanPostProcessor 实现
      • 自定义实现(略)
    • 19、附:关于加static的bean会提前注册问题

一、依赖查找

1、单一类型依赖查找(接口 - BeanFactory)

用于查找已知类型或名称的Bean对象。

根据Bean名称查找

// getBean(String) 根据名称获取bean
Object bean = beanfactory.getBean("beanName");

// Spring 2.5 覆盖默认参数:getBean(String,Object...)
Object bean = beanfactory.getBean("beanName", args ...);

根据Bean类型实时查找

// Spring 3.0 getBean(Class)
User bean = beanfactory.getBean(User.class);

// Spring 4.1 覆盖默认参数:getBean(Class,Object...)
User bean = beanfactory.getBean(User.class, args ...);

根据Bean类型延迟查找(Spring 5.1)

// getBeanProvider(Class)
ObjectProvider<User> objectProvider = applicationContext.getBeanProvider(User.class);
User bean = beanfactory.getObject();
// getBeanProvider(ResolvableType)

根据Bean名称+类型查找

// getBean(String,Class)
User bean = beanfactory.getBean("beanName", User.class);

2、集合类型依赖查找(接口 - ListableBeanFactory)

查找已知类型多个Bean的集合。

ListableBeanFactory接口继承了BeanFactory接口,在原来的BeanFactory接口的基础上做了增强。

对于集合类型依赖查找,通过ListableBeanFactory#getBeanNamesForType 和ListableBeanFactory#getBeansForType,两个依赖查找方法,前者不会强制bean的初始化,而是通过BeanDefinition和FactoryBean的getClassType进行判断;后者会强制Bean的初始化。

根据Bean类型查找

// 获取同类型 Bean 名称列表 getBeanNamesForType(Class)
String[] beans = factory.getBeanNamesForType(User.class);

// 获取同类型 Bean 名称列表 Spring 4.2 
getBeanNamesForType(ResolvableType)

// 获取同类型 Bean 实例列表 getBeansOfType(Class) 以及重载方法
Map<String, User> usersMap = context.getBeansOfType(User.class);

通过注解类型查找 Spring 4.0

可以查找到标注了某注解的bean。

// 获取标注类型 Bean 名称列表
getBeanNamesForAnnotation(Class<? extends Annotation>)

// 获取标注类型 Bean 实例列表
getBeansWithAnnotation(Class<? extends Annotation>)

// 获取指定名称 + 标注类型 Bean 实例
findAnnotationOnBean(String,Class<? extends Annotation>)

3、层次性依赖查找(接口 - HierarchicalBeanFactory)

层次性容器使用场景

容器的层次关系主要的目的是实现 Bean 复用,假设一个应用存在一个 Root ApplicationContext,内部的 Bean 来自于一个 jar 包,那么,这个jar 包可能被两个不同的 Servlet 应用使用,这时,ServletContext A 和 ServletContext B 同时复用了这个 parent ApplicationContext,而它自己也有 ApplicationContext,这也是 Spring Web MVC 所涉及的应用上下文架构。

层次性依赖查找实例

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 层次性依赖查找示例
 */
public class HierarchicalDependencyLookupDemo {

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
        applicationContext.register(ObjectProviderDemo.class); // 不包含user

        // 1. 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory(子) <- ConfigurableListableBeanFactory(子)
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
//        System.out.println("当前 BeanFactory 的 Parent BeanFactory : " + beanFactory.getParentBeanFactory());

        // 2. 设置 Parent BeanFactory
        HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory(); // 包含user
        beanFactory.setParentBeanFactory(parentBeanFactory);
//        System.out.println("当前 BeanFactory 的 Parent BeanFactory : " + beanFactory.getParentBeanFactory());

        // 查询LocalBean
        displayContainsLocalBean(beanFactory, "user");
        displayContainsLocalBean(parentBeanFactory, "user");

        // 通过递归查询Bean
        displayContainsBean(beanFactory, "user");
        displayContainsBean(parentBeanFactory, "user");

        // 启动应用上下文
        applicationContext.refresh();

        // 关闭应用上下文
        applicationContext.close();

    }

    private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name : %s] : %s\n", beanFactory, beanName,
                containsBean(beanFactory, beanName));
    }

    /**
     * 双亲委派方式,优先查找父类的Bean
     */
    private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
            if (containsBean(parentHierarchicalBeanFactory, beanName)) {
                return true;
            }
        }
        return beanFactory.containsLocalBean(beanName);
    }

    private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean[name : %s] : %s\n", beanFactory, beanName,
                beanFactory.containsLocalBean(beanName));
    }

    private static ConfigurableListableBeanFactory createParentBeanFactory() {
        // 创建 BeanFactory 容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        // XML 配置文件 ClassPath 路径
        String location = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加载配置
        reader.loadBeanDefinitions(location);
        return beanFactory;
    }

}

使用BeanFactoryUtils查找Bean

  • 单一类型:BeanFactoryUtils#beanOfType
  • 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors
  • 根据 Java 注解查找名称列表:BeanFactoryUtils#beanNamesForTypeIncludingAncestors
// org.springframework.beans.factory.BeanFactoryUtils#beansOfTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>)
// 也是递归,从父容器查找到子容器
public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
		throws BeansException {

	Assert.notNull(lbf, "ListableBeanFactory must not be null");
	Map<String, T> result = new LinkedHashMap<>(4);
	result.putAll(lbf.getBeansOfType(type));
	if (lbf instanceof HierarchicalBeanFactory) {
		HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
		if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
			Map<String, T> parentResult = beansOfTypeIncludingAncestors(
					(ListableBeanFactory) hbf.getParentBeanFactory(), type);
			parentResult.forEach((beanName, beanInstance) -> {
				if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) { // 有相同的bean优先使用父容器的
					result.put(beanName, beanInstance);
				}
			});
		}
	}
	return result;
}

4、延迟依赖查找(接口 - ObjectFactory)

延迟查找并非是Bean的延迟加载,跟@Lazy是两码事,延迟指的就是查找的时候并没有查找到想要查找的那个bean而是查找到了,objectFactory或者objectProvider。并且Provider还比Factory多了能查找出多个的功能。ObjectProvider#getxxx 方法 底层还是通过BeanFactory来进行依赖查找的,但是在进行依赖查找前,可以制定以下规则,比如Bean找到后,再设置额外的属性,完成一些用户的自定义需求;Bean没有找到,该如何处理。

理解什么是延迟依赖查找

延迟查找,我个人认为主要是给架构开发者使用的。非常典型的一个使用场景,就是SpringBoot里的自动配置,可以看看LettuceConnectionConfiguration这个类,这个类用于创建RedisClient。显然,RedisClient的创建是要根据用户的需求来创建的。

有些属性是必须的,可以在配置文件里配置,比如ip,port这种。Lettuce在创建RedisClient的时候,会从配置文件里读取这些数据来创建RedisClient。

但有些属性是非必须的,而且不能在配置文件里配置,比如开启读写分离,RedisClient在Lettuce内部,是通过一个Builder来创建的,如果要开启读写分离,这需要你在这个Builder在执行build的过程中,额外加一行:clientConfigurationBuilder.readFrom(ReadFrom.REPLICA);

问题就在这里,怎么让业务开发人员把这行代码加入到其内部的build流程中?这个问题,一种比较常见的思路,是使用模板方法,写一个抽象方法,调用它。具体的实现交给开发人员。

所以Lettuce设计了一个LettuceClientConfigurationBuilderCustomizer的类,他有一个customize方法,并且把上面提到的Builder作为这个方法的参数传递进来。开发人员,如果能去配置LettuceClientConfigurationBuilderCustomizer这样一个类,就能达到上述的目的。

但问题是,如果是开发人员去配置这样一个类,说明LettuceClientConfigurationBuilderCustomizer这个类还没有被实例化。但根据模板模式,流程中必须调用LettuceClientConfigurationBuilderCustomizer这个类的抽象方法,才能达到目的。

这个时候延迟加载,ObjectProvider的作用就体现出来了。他可以规定,他产生的是一个LettuceClientConfigurationBuilderCustomizer的对象,并且指定这个对象产生以后,做什么事情。比如调用customize方法。

如果用户配置了LettuceClientConfigurationBuilderCustomizer对象。那么在创建RedisClient的流程中,ObjectProvider就能拿到该对象,然后按照预先指定的动作执行,比如执行customize方法。

如果用户没配置,那么拿不到Bean对象,就什么都不做。

所以这个场景,我认为是延迟查找的一个典型实现

// 根本在于函数式接口的使用   就是下面的第四行
private void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
	this.builderCustomizers.orderedStream()
		.forEach((customizer) -> customizer.customize(builder));
}

熟悉ObjectFactory的子接口-ObjectProvider接口

public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {

	// 返回对象一个实例,args为创建相应实例时使用的参数
	T getObject(Object... args) throws BeansException;

	// 返回bean实例,如果不可用则为空
	@Nullable
	T getIfAvailable() throws BeansException;

	// 返回bean实例,如果不可用则使用默认的Supplier
	default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
		T dependency = getIfAvailable();
		return (dependency != null ? dependency : defaultSupplier.get());
	}

	// 如果可用,调用消费者
	default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
		T dependency = getIfAvailable();
		if (dependency != null) {
			dependencyConsumer.accept(dependency);
		}
	}

	// 如果不可用或不唯一,则为空
	@Nullable
	T getIfUnique() throws BeansException;

	// 如果不可用或不唯一,则使用Supplier
	default T getIfUnique(Supplier<T> defaultSupplier) throws BeansException {
		T dependency = getIfUnique();
		return (dependency != null ? dependency : defaultSupplier.get());
	}

	// 如果可用唯一,调用消费者Consumer
	default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
		T dependency = getIfUnique();
		if (dependency != null) {
			dependencyConsumer.accept(dependency);
		}
	}

	// 可遍历,支持stream
	@Override
	default Iterator<T> iterator() {
		return stream().iterator();
	}

	
	default Stream<T> stream() {
		throw new UnsupportedOperationException("Multi element access not supported");
	}

	// 获取排序后的数据流
	default Stream<T> orderedStream() {
		throw new UnsupportedOperationException("Ordered element access not supported");
	}

}

延迟加载ObjectProvider使用示例

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

/**
 * 通过 {@link ObjectProvider} 进行依赖查找
 */
public class ObjectProviderDemo { // 这里@Configuration 是非必须注解

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
        applicationContext.register(ObjectProviderDemo.class);
        // 启动应用上下文
        applicationContext.refresh();
        // 依赖查找集合对象
        lookupByObjectProvider(applicationContext);
        lookupIfAvailable(applicationContext);
        lookupByStreamOps(applicationContext);

        // 关闭应用上下文
        applicationContext.close();

    }

    private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
//        Iterable<String> stringIterable = objectProvider;
//        for (String string : stringIterable) {
//            System.out.println(string);
//        }
        // Stream -> Method reference
        objectProvider.stream().forEach(System.out::println);
    }

    private static void lookupIfAvailable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        User user = userObjectProvider.getIfAvailable(User::createUser); // 如果没有,就创建对象
        System.out.println("当前 User 对象:" + user);
    }

    @Bean
    @Primary
    public String helloWorld() { // 方法名就是 Bean 名称 = "helloWorld"
        return "Hello,World";
    }

    @Bean
    public String message() {
        return "Message";
    }

    private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        System.out.println(objectProvider.getObject());
    }
}

5、依赖查找的异常安全性

依赖查找类型代表实现—是否安全
单一类型查找BeanFactory#getBean
ObjectFactory#getObject
ObjectProvider#getIfAvailable
集合类型查找ListableBeanFactory#getBeansOfType
ObjectProvider#stream

注意:层次性依赖查找的安全性取决于其扩展的单一或集合类型的 BeanFactory 接口

安全依赖查找代码实例


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 类型安全 依赖查找示例
 */
public class TypeSafetyDependencyLookupDemo {

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类 TypeSafetyDependencyLookupDemo 作为配置类(Configuration Class)
        applicationContext.register(TypeSafetyDependencyLookupDemo.class); // 这里面是没有User的
        // 启动应用上下文
        applicationContext.refresh();

        // 演示 BeanFactory#getBean 方法的安全性
        displayBeanFactoryGetBean(applicationContext);
        // 演示 ObjectFactory#getObject 方法的安全性
        displayObjectFactoryGetObject(applicationContext);
        // 演示 ObjectProvider#getIfAvaiable 方法的安全性
        displayObjectProviderIfAvailable(applicationContext);

        // 演示 ListableBeanFactory#getBeansOfType 方法的安全性
        displayListableBeanFactoryGetBeansOfType(applicationContext);
        // 演示 ObjectProvider Stream 操作的安全性
        displayObjectProviderStreamOps(applicationContext);

        // 关闭应用上下文
        applicationContext.close();
    }

    private static void displayObjectProviderStreamOps(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        /**
         * userObjectProvider.forEach是安全的
         */
        printBeansException("displayObjectProviderStreamOps", () -> userObjectProvider.forEach(System.out::println));
    }

    private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
        /**
         * beanFactory.getBeansOfType(User.class) 是安全的
         * 找不到bean不会抛异常,会返回空(并不是null)
         */
        printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
    }

    private static void displayObjectProviderIfAvailable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        /**
         * objectProvider.getIfAvailable() 是安全的
         * 找不到bean会返回null
         */
        printBeansException("displayObjectProviderIfAvailable", () -> userObjectProvider.getIfAvailable());
    }

    private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
        // ObjectProvider 等价于 ObjectFactory
        ObjectFactory<User> userObjectFactory = applicationContext.getBeanProvider(User.class);
        /**
         * ObjectFactory.getObject()不安全!
         * 没有该Bean会抛异常
         */
        printBeansException("displayObjectFactoryGetObject", () -> userObjectFactory.getObject());
    }

    public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
        /**
         * beanFactory.getBean(User.class) 不安全!
         * beanFactory.getBean(class); 有三种情况会抛异常
         * NoSuchBeanDefinitionException -如果没有找到给定类型的bean
         * NoUniqueBeanDefinitionException——如果找到了多个给定类型的bean
         * BeansException -如果无法创建bean
         */
        printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
    }

    private static void printBeansException(String source, Runnable runnable) {
        System.err.println("==========================================");
        System.err.println("Source from :" + source);
        try {
            runnable.run();
        } catch (BeansException exception) {
            exception.printStackTrace();
        }
    }
}

由依赖查找的异常安全性做出的总结

日常开发中能用异常安全的方式获取Bean就用安全的方式获取Bean,这里推荐使用ObjectProvider的方式获取Bean,既可以单一类型查找,又可以集合类型查找,使用方便还安全。

6、Spring内建可查找的依赖

AbstractApplicationContext 内建可查找的依赖

Bean 名称Bean 实例使用场景
environmentEnvironment 对象外部化配置(-D启动参数)以及 Profiles
systemPropertiesjava.util.Properties 对象Java 系统属性
systemEnvironmentjava.util.Map 对象操作系统环境变量(当前用户)
messageSourceMessageSource 对象国际化文案
lifecycleProcessorLifecycleProcessor 对象Lifecycle Bean 处理器
applicationEventMulticasterApplicationEventMulticaster 对象Spring 事件广播器

注解驱动 Spring 应用上下文内建可查找的依赖

Bean 名称Bean 实例使用场景
org.springframework.context.annotation.internalConfigurationAnnotationProcessorConfigurationClassPostProcessor 对象处理 Spring 配置类
org.springframework.context.annotation.internalAutowiredAnnotationProcessorAutowiredAnnotationBeanPostProcessor 对象处理 @Autowired 以及 @Value注解
org.springframework.context.annotation.internalCommonAnnotationProcessorCommonAnnotationBeanPostProcessor 对象(条件激活)处理 JSR-250 注解,如 @PostConstruct 等
org.springframework.context.event.internalEventListenerProcessorEventListenerMethodProcessor对象处理标注 @EventListener 的Spring 事件监听方法
org.springframework.context.event.internalEventListenerFactoryDefaultEventListenerFactory 对象@EventListener 事件监听方法适配为 ApplicationListener
org.springframework.context.annotation.internalPersistenceAnnotationProcessorPersistenceAnnotationBeanPostProcessor 对象(条件激活)处理 JPA 注解场景

1、ConfigurationClassPostProcessor
标注了@Configuration 的类,底层是由ConfigurationClass处理的。
而ConfigurationClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor,用于处理BeanFactory的生命周期回调。

2、AutowiredAnnotationBeanPostProcessor

从其构造器也可以看出,它是处理@Autowired和@Value的。

public AutowiredAnnotationBeanPostProcessor() {
	this.autowiredAnnotationTypes.add(Autowired.class);
	this.autowiredAnnotationTypes.add(Value.class);
	try {
		this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
				ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
		logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

7、依赖查找中的经典异常

BeansException 子类型

异常类型触发条件(举例)场景举例
NoSuchBeanDefinitionException当查找 Bean 不存在于 IoC 容器时BeanFactory#getBean、 ObjectFactory#getObject
NoUniqueBeanDefinitionException类型依赖查找时,IoC 容器存在多个 Bean 实例BeanFactory#getBean(Class)
BeanInstantiationException当 Bean 所对应的类型非具体类时BeanFactory#getBean
BeanCreationException当 Bean 初始化过程中Bean 初始化方法执行异常时
BeanDefinitionStoreException当 BeanDefinition 配置元信息非法时XML 配置资源无法打开时

附1:各个BeanFactory的继承关系

在这里插入图片描述

附2:ApplicationContext继承关系

在这里插入图片描述

二、依赖注入

依赖注入类型:

依赖注入类型配置元数据举例
Setter 方法<proeprty name=”user” ref=”userBean” />
构造器<constructor-arg name=“user” ref=“userBean” />
字段@Autowired User user;
方法@Autowired public void user(User user) { … }
接口回调class MyBean implements BeanFactoryAware { … }

1、自动绑定(Autowiring-区别于@Autowired)

其实自动绑定的方式更多的是使用xml的方式,实际自动绑定有很多限制和不足,日常开发过程中较少使用。

模式说明
no默认值,未激活 Autowiring,需要手动指定依赖注入对象
byName根据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该属性
byType根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性
constructor特殊 byType 类型,用于构造器参数
<!--使用ref的方式,可以将addressBean命名的bean绑定到address字段-->
<!--使用parent的方式,指定父类-->
<bean id="superUser" class="com.demo.domain.SuperUser" parent="user">
    <property name="address" ref="addressBean"/>
</bean>

<!--autowire自动绑定有多种模式,上面已经介绍过 -->
<bean id="user" class="com.demo.domain.SuperUser" autowire="byName">
</bean>

每种自动绑定的模式都或多或少有一些坑:

  • no:需要自己手动指定注入对象,value或者ref。
  • byName:属性名与被注入的bean名必须相同,如果有修改需要同步修改。
  • byType:如果有多个类型的bean,需要指定@Primary。不能绑定如String类型这种原生类型。

Autowire是一个枚举,指定的自动绑定的模式:

// 我们可以看到,枚举类型并没有构造器绑定,其实构造器绑定就是一种byType绑定
// 具体详情可以查看AutowireCapableBeanFactory
public enum Autowire {

	/**
	 * Constant that indicates no autowiring at all.
	 */
	NO(AutowireCapableBeanFactory.AUTOWIRE_NO),

	/**
	 * Constant that indicates autowiring bean properties by name.
	 */
	BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),

	/**
	 * Constant that indicates autowiring bean properties by type.
	 */
	BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);

	public boolean isAutowire() {
		return (this == BY_NAME || this == BY_TYPE);
	}

}

2、Setter 方法注入

自动Setter注入

自动Setter注入有byName和byType的方式,具体参考上面介绍的自动绑定。

手动Setter注入

1、XML资源配置元信息

<!-- 使用name+value的方式,可以实现手动Setter注入 -->
<bean class="com.demo.User">
    <property name="name" value="张三" />
</bean>

2、Java 注解配置元信息

// User在容器中已经注册过了,只需要作为方法参数传过来,就会自动获取到
@Bean
public UserHolder userHolder(User user) {
    UserHolder userHolder = new UserHolder();
    userHolder.setUser(user);
    return userHolder;
}

3、API 配置元信息

// 生成 UserHolder 的 BeanDefinition
BeanDefinition userHolderBeanDefinition = createUserHolderBeanDefinition();
// 注册 UserHolder 的 BeanDefinition
applicationContext.registerBeanDefinition("userHolder", userHolderBeanDefinition);

// 生成UserHolder 的 BeanDefinition
private static BeanDefinition createUserHolderBeanDefinition() {
    BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
    definitionBuilder.addPropertyReference("user", "superUser"); // 设置属性关联,将superUser命名的bean,赋值到user属性
    return definitionBuilder.getBeanDefinition();
}

3、构造器注入(官方推荐)

自动构造器注入

详情请看文章上面,自动绑定。

<bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder"
      autowire="constructor">
    <!--        <property name="user" ref="superUser" /> 替换成 autowiring 模式 -->
</bean>

手动构造器注入

1、xml方式

<!--自定义构造器参数,ref代表bean的名称 -->
<bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
    <constructor-arg name="user" ref="superUser" />
</bean>
public class UserHolder {

    private User user;

    public UserHolder(User user) {
        this.user = user;
    }

2、注解方式

@Bean
public UserHolder userHolder(User user) {
    return new UserHolder(user);
}

3、API方式

private static BeanDefinition createUserHolderBeanDefinition() {
    BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
    definitionBuilder.addConstructorArgReference("superUser"); // 自动找到名称为superUser的bean,注入到构造器
    return definitionBuilder.getBeanDefinition();
}

4、字段注入

字段注入一般使用Java 注解配置元信息:

  • @Autowired
  • @Resource
  • @Inject(可选)
/**
使用@Autowired 注入,默认根据类型注入,然后根据name注入
注意!@Autowired会忽略static类型的字段,具体源码后续再讲
*/
@Autowired
private UserHolder userHolder;

/**
使用@Resource 注入,默认根据类型注入,然后根据name注入
*/
@Resource
private UserHolder userHolder2;

/**
使用@Inject 注入,需要依赖JSR-330的jar包,这里略
*/

5、方法注入

方法注入一般使用Java 注解配置元信息:

  • @Autowired
  • @Resource
  • @Inject(可选)
  • @Bean

方法注入会自动匹配bean为方法的参数,将其注入到方法的参数中。

方法注入不走 factorybean, Setter 注入是通过 Java Beans 来实现的,而方法注入则是直接通过 Java 反射来做的。当然底层都是 Java 反射~

private UserHolder userHolder;

private UserHolder userHolder2;

@Autowired
public void init1(UserHolder userHolder) {
    this.userHolder = userHolder;
}

@Resource
public void init2(UserHolder userHolder2) {
    this.userHolder2 = userHolder2;
}

@Bean
public UserHolder userHolder(User user) {
    return new UserHolder(user);
}

6、接口回调注入

Aware 系列接口回调。

內建接口说明
BeanFactoryAware获取当前 IoC 容器 - BeanFactory
ApplicationContextAware获取 Spring 应用上下文 - ApplicationContext 对象
EnvironmentAware获取 Environment 对象
ResourceLoaderAware获取资源加载器 对象 - ResourceLoader
BeanClassLoaderAware获取加载当前 Bean Class 的 ClassLoader
BeanNameAware获取当前 Bean 的名称
MessageSourceAware获取 MessageSource 对象,用于 Spring 国际化
ApplicationEventPublisherAware获取 ApplicationEventPublishAware 对象,用于 Spring 事件
EmbeddedValueResolverAware获取 StringValueResolver 对象,用于占位符处理
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 基于 {@link Aware} 接口回调的依赖注入示例
 */
public class AwareInterfaceDependencyInjectionDemo implements BeanFactoryAware, ApplicationContextAware {

    private static BeanFactory beanFactory;

    private static ApplicationContext applicationContext;


    public static void main(String[] args) {

        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类) -> Spring Bean
        context.register(AwareInterfaceDependencyInjectionDemo.class);

        // 启动 Spring 应用上下文
        context.refresh();

        System.out.println(beanFactory == context.getBeanFactory());
        System.out.println(applicationContext == context);

        // 显示地关闭 Spring 应用上下文
        context.close();
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        AwareInterfaceDependencyInjectionDemo.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        AwareInterfaceDependencyInjectionDemo.applicationContext = applicationContext;
    }
}

7、各种依赖注入比较

注入方式优点缺点
构造器注入通用性强,官方推荐无法解决循环依赖问题;参数过多的话可读性差
Setter 方法注入解决循环依赖问题不能将对象设置为final
字段注入使用便捷不能将字段设为final、static;无法解决循环依赖问题
方法注入一般与@Bean配合使用,@Autowire、@Resource用的较少

8、基础类型的注入

  • 原生类型(Primitive):boolean、byte、char、short、int、float、long、double
  • 标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID
  • 常规类型(General):Object、String、TimeZone、Calendar、Optional 等
  • Spring 类型:Resource、InputSource、Formatter 等

使用xml配置的方式,一些基础类型的配置涉及自动类型转换:


public class User {
    private Long id;
    private String name;
    private City city; // 枚举
    private Resource configFileLocation; // 资源

public enum City {
    BEIJING,
    HANGZHOU,
    SHANGHAI
}

<bean id="user" class="com.demo.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="张三"/>
    <property name="city" value="HANGZHOU"/>
    <property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
</bean>

9、集合类型注入

  • 数组类型(Array):原生类型、标量类型、常规类型、Spring 类型
  • 集合类型(Collection)
    • Collection:List、Set(SortedSet、NavigableSet、EnumSet)
    • Map:Properties
public class User {
    private City[] workCities; // 数组
    private List<City> lifeCities; // list

public enum City {
    BEIJING,
    HANGZHOU,
    SHANGHAI
}

<bean id="user" class="com.demo.domain.User">
    <property name="workCities" value="BEIJING,HANGZHOU"/> <!-- list也可以用这种方式写 -->
    <property name="lifeCities">
        <list>
            <value>BEIJING</value>
            <value>SHANGHAI</value>
        </list>
    </property>
</bean>

10、限定注入

使用注解 @Qualifier 通过Bean 名称限定

有多个同类型的Bean时,使用@Primary指定默认的Bean之后,再使用@Autowired会自动注入使用@Primary指定的Bean,否则会报错。

使用@Qualifier(“user”),可以注入指定名称的bean。

@Autowired
private User user; 

@Autowired
@Qualifier("user") // 指定 Bean 名称或 ID
private User namedUser;

@Bean
public User user() {
    return createUser(5L);
}

@Bean
@Primary // 指定默认
public User users() {
    return createUser(6L);
}

使用注解 @Qualifier 通过分组限定

当使用@Bean定义一个bean时,同时使用@Qualifier标注,此bean就会被分组。
使用@Autowired注入时,同时使用@Qualifier标注,会取出分组过的bean。

@Autowired // 注入所有User类型的Bean
private Collection<User> allUsers; 

@Autowired
@Qualifier // 取出所有使用@Qualifier标注的bean
private Collection<User> qualifiedUsers; 

@Bean
@Qualifier // 进行逻辑分组
public User user1() {
    return createUser(7L);
}

@Bean
@Qualifier // 进行逻辑分组
public  User user2() {
    return createUser(8L);

}

基于注解 @Qualifier 扩展限定

(自定义注解 - 实际应用如 Spring Cloud @LoadBalanced)

我们可以使用@Qualifier扩展分组,扩展后的用法与@Qualifier相同。

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier
public @interface UserGroup {
}

@Autowired
@UserGroup
private Collection<User> groupedUsers;


@Bean
@UserGroup
public  User user3() {
    return createUser(9L);
}

@Bean
@UserGroup
public  User user4() {
    return createUser(10L);
}

案例

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier
public @interface UserGroup {
}

import org.geekbang.thinking.in.spring.ioc.dependency.injection.annotation.UserGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.Collection;

@Configuration
public class QualifierAnnotationDependencyInjectionDemo {

    @Autowired // 注入使用 @Primary标注的bean
    private User user;

    @Autowired
    @Qualifier("user") // 指定 Bean 名称或 ID
    private User namedUser;
    
    @Autowired // 注入所有User类型的bean
    private Collection<User> allUsers; 

    @Autowired
    @Qualifier // 注入使用@Qualifier 分组的bean
    private Collection<User> qualifiedUsers;

    @Autowired
    @UserGroup // 注入使用@UserGroup、@Qualifier分组的bean(因为@UserGroup用@Qualifier标注了)
    private Collection<User> groupedUsers;


    @Bean
    public User user() {
        return createUser(5L);
    }

    @Bean
    @Primary // 指定默认
    public User users() {
        return createUser(6L);
    }

    @Bean
    @Qualifier // 进行逻辑分组
    public User user1() {
        return createUser(7L);
    }

    @Bean
    @Qualifier // 进行逻辑分组
    public User user2() {
        return createUser(8L);

    }

    @Bean
    @UserGroup
    public User user3() {
        return createUser(9L);
    }

    @Bean
    @UserGroup
    public User user4() {
        return createUser(10L);
    }

    private static User createUser(Long id) {
        User user = new User();
        user.setId(id);
        return user;
    }

    public static void main(String[] args) {

        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类) -> Spring Bean
        applicationContext.register(QualifierAnnotationDependencyInjectionDemo.class);

        // 启动 Spring 应用上下文
        applicationContext.refresh();

        // 依赖查找 QualifierAnnotationDependencyInjectionDemo Bean
        QualifierAnnotationDependencyInjectionDemo demo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo.class);

        // 期待输出 superUser Bean
        System.out.println("demo.user = " + demo.user);
        // 期待输出 user Bean
        System.out.println("demo.namedUser = " + demo.namedUser);
        // 期待输出 superUser user user1 user2
        System.out.println("demo.allUsers = " + demo.allUsers);
        // 期待输出 user1 user2
        System.out.println("demo.qualifiedUsers = " + demo.qualifiedUsers);
        // 期待输出 user3 user4
        System.out.println("demo.groupedUsers = " + demo.groupedUsers);


        // 显示地关闭 Spring 应用上下文
        applicationContext.close();
    }
}

// 输出内容
demo.user = User{id=6}
demo.namedUser = User{id=5}
demo.allUsers = [User{id=5}, User{id=6}, User{id=7}, User{id=8}, User{id=9}, User{id=10}]
demo.qualifiedUsers = [User{id=7}, User{id=8}, User{id=9}, User{id=10}]
demo.groupedUsers = [User{id=9}, User{id=10}]

11、延迟依赖注入

延迟依赖注入请参考【延迟依赖查找】。

通常的 @Autowired 会及时依赖相关的 Spring Bean,不过此时 Bean 的状态并未完全准备好,所以 ObjectProvider 可以在需要时获取 Spring Bean,更好的状态准备,达到延迟的获取效果。(更多请查阅【Spring如何解决循环依赖的】)

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collection;
import java.util.Set;

/**
 * {@link ObjectProvider} 实现延迟依赖注入
 */
@Configuration
public class LazyAnnotationDependencyInjectionDemo {

    @Autowired
    @Qualifier("user")
    private User user; // 实时注入

    @Autowired
    private ObjectProvider<User> userObjectProvider; // 使用 API ObjectFactory 延迟注入

    @Autowired
    private ObjectFactory<Set<User>> usersObjectFactory; // 使用 API ObjectProvider 延迟注入(推荐)

    @Bean
    public User user() {
        return createUser(10L);
    }

    private static User createUser(Long id) {
        User user = new User();
        user.setId(id);
        return user;
    }

    public static void main(String[] args) {

        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类) -> Spring Bean
        applicationContext.register(LazyAnnotationDependencyInjectionDemo.class);

        // 启动 Spring 应用上下文
        applicationContext.refresh();

        // 依赖查找 QualifierAnnotationDependencyInjectionDemo Bean
        LazyAnnotationDependencyInjectionDemo demo = applicationContext.getBean(LazyAnnotationDependencyInjectionDemo.class);

        // 期待输出 superUser Bean
        System.out.println("demo.user = " + demo.user);
        // 期待输出 superUser Bean
        System.out.println("demo.userObjectProvider = " + demo.userObjectProvider.getObject()); // 继承 ObjectFactory
        // 期待输出 superUser user Beans
        System.out.println("demo.usersObjectFactory = " + demo.usersObjectFactory.getObject());

        demo.userObjectProvider.forEach(System.out::println);

        // 显示地关闭 Spring 应用上下文
        applicationContext.close();
    }
}

12、Optional类型注入

java8开始支持Optional类型,直接定义Optional的泛型,就会根据定义的泛型进行注入。

@Autowired
private Optional<User> userOptional;

13、依赖注入处理的过程源码分析

入口 - DefaultListableBeanFactory#resolveDependency

// org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

	descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
	if (Optional.class == descriptor.getDependencyType()) { // java8开始支持Optional
		return createOptionalDependency(descriptor, requestingBeanName);
	}
	else if (ObjectFactory.class == descriptor.getDependencyType() ||
			ObjectProvider.class == descriptor.getDependencyType()) { // 支持ObjectProvider
		return new DependencyObjectProvider(descriptor, requestingBeanName);
	}
	else if (javaxInjectProviderClass == descriptor.getDependencyType()) { // Java 中jsr330注入方式
		return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
	}
	else { // 默认操作
		Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( // 是否懒加载
				descriptor, requestingBeanName);
		if (result == null) {
			result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
		}
		return result;
	}
}
// org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

	InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
	try {
		Object shortcut = descriptor.resolveShortcut(this);
		if (shortcut != null) {
			return shortcut;
		}

		Class<?> type = descriptor.getDependencyType();
		Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
		if (value != null) {
			if (value instanceof String) {
				String strVal = resolveEmbeddedValue((String) value);
				BeanDefinition bd = (beanName != null && containsBean(beanName) ?
						getMergedBeanDefinition(beanName) : null);
				value = evaluateBeanDefinitionString(strVal, bd);
			}
			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
			try {
				return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
			}
			catch (UnsupportedOperationException ex) {
				// A custom TypeConverter which does not support TypeDescriptor resolution...
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}
		}

		Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
		if (multipleBeans != null) {
			return multipleBeans;
		}

		// 会返回多个bean
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
		if (matchingBeans.isEmpty()) {
			if (isRequired(descriptor)) {
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
			}
			return null;
		}

		String autowiredBeanName;
		Object instanceCandidate;

		if (matchingBeans.size() > 1) { // 多个bean
			autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
			if (autowiredBeanName == null) {
				if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
					return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
				}
				else {
					// In case of an optional Collection/Map, silently ignore a non-unique case:
					// possibly it was meant to be an empty collection of multiple regular beans
					// (before 4.3 in particular when we didn't even look for collection beans).
					return null;
				}
			}
			instanceCandidate = matchingBeans.get(autowiredBeanName);
		}
		else {
			// We have exactly one match.
			Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
			autowiredBeanName = entry.getKey();
			instanceCandidate = entry.getValue();
		}

		if (autowiredBeanNames != null) {
			autowiredBeanNames.add(autowiredBeanName);
		}
		if (instanceCandidate instanceof Class) {
			instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
		}
		Object result = instanceCandidate;
		if (result instanceof NullBean) {
			if (isRequired(descriptor)) {
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
			}
			result = null;
		}
		if (!ClassUtils.isAssignableValue(type, result)) {
			throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
		}
		return result;
	}
	finally {
		ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
	}
}
// org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
protected Map<String, Object> findAutowireCandidates(
		@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

	String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this, requiredType, true, descriptor.isEager());
	Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
	for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
		Class<?> autowiringType = classObjectEntry.getKey();
		if (autowiringType.isAssignableFrom(requiredType)) {
			Object autowiringValue = classObjectEntry.getValue();
			autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
			if (requiredType.isInstance(autowiringValue)) {
				result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
				break;
			}
		}
	}
	for (String candidate : candidateNames) {
		if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
			addCandidateEntry(result, candidate, descriptor, requiredType);
		}
	}
	if (result.isEmpty()) {
		boolean multiple = indicatesMultipleBeans(requiredType);
		// Consider fallback matches if the first pass failed to find anything...
		DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
		for (String candidate : candidateNames) {
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
					(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
		if (result.isEmpty() && !multiple) {
			// Consider self references as a final pass...
			// but in the case of a dependency collection, not the very same bean itself.
			for (String candidate : candidateNames) {
				if (isSelfReference(beanName, candidate) &&
						(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
						isAutowireCandidate(candidate, fallbackDescriptor)) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
		}
	}
	return result;
}
// 获取bean集合
// org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveMultipleBeans
@Nullable
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {

	Class<?> type = descriptor.getDependencyType();

	if (descriptor instanceof StreamDependencyDescriptor) {
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		Stream<Object> stream = matchingBeans.keySet().stream()
				.map(name -> descriptor.resolveCandidate(name, type, this))
				.filter(bean -> !(bean instanceof NullBean));
		if (((StreamDependencyDescriptor) descriptor).isOrdered()) {
			stream = stream.sorted(adaptOrderComparator(matchingBeans));
		}
		return stream;
	}
	else if (type.isArray()) {
		Class<?> componentType = type.getComponentType();
		ResolvableType resolvableType = descriptor.getResolvableType();
		Class<?> resolvedArrayType = resolvableType.resolve(type);
		if (resolvedArrayType != type) {
			componentType = resolvableType.getComponentType().resolve();
		}
		if (componentType == null) {
			return null;
		}
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
				new MultiElementDescriptor(descriptor));
		if (matchingBeans.isEmpty()) {
			return null;
		}
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
		Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);
		if (result instanceof Object[]) {
			Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
			if (comparator != null) {
				Arrays.sort((Object[]) result, comparator);
			}
		}
		return result;
	}
	else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
		Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
		if (elementType == null) {
			return null;
		}
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
				new MultiElementDescriptor(descriptor));
		if (matchingBeans.isEmpty()) {
			return null;
		}
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
		Object result = converter.convertIfNecessary(matchingBeans.values(), type);
		if (result instanceof List) {
			if (((List<?>) result).size() > 1) {
				Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
				if (comparator != null) {
					((List<?>) result).sort(comparator);
				}
			}
		}
		return result;
	}
	else if (Map.class == type) {
		ResolvableType mapType = descriptor.getResolvableType().asMap();
		Class<?> keyType = mapType.resolveGeneric(0);
		if (String.class != keyType) {
			return null;
		}
		Class<?> valueType = mapType.resolveGeneric(1);
		if (valueType == null) {
			return null;
		}
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
				new MultiElementDescriptor(descriptor));
		if (matchingBeans.isEmpty()) {
			return null;
		}
		if (autowiredBeanNames != null) {
			autowiredBeanNames.addAll(matchingBeans.keySet());
		}
		return matchingBeans;
	}
	else {
		return null;
	}
}

14、详解@Autowired 注入

重要类:AutowiredAnnotationBeanPostProcessor。

@Autowired注入过程(所有方法都在AutowiredAnnotationBeanPostProcessor#类里)
(1)调用postProcessProperties()方法(spring 5.1之后才是这个方法名,5.1之前是postProcessPropertyValues)
该步骤信息点:
a. postProcessProperties方法会比bean的setXX()方法先调用
b.findAutowiringMetadata()方法会找出一个bean加了@Autowired注解的字段(包括父类的),并且该方法做了缓存
c.xml配置的bean与bean之间是可以有继承关系的,有另一个周期(不是autowired的流程)是把配置super bean的属性合并到当前bean,之后会调用后置方法postProcessMergedBeanDefinition,该方法也会调用一次findAutowiringMetadata
d.经测试,postProcessMergedBeanDefinition会比postProcessProperties先执行,因此调用postProcessProperties时都是直接拿缓存
(2)—>inject方法(获得对应的bean,然后通过反射注入到类的字段上)
(3)inject方法会调用resolveDependency方法,这方法会根据@Autowired字段信息来匹配出符合条件的bean

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Field field = (Field) this.member;
	Object value;
	if (this.cached) {
		try {
			value = resolvedCachedArgument(beanName, this.cachedFieldValue);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Unexpected removal of target bean for cached argument -> re-resolve
			value = resolveFieldValue(field, bean, beanName);
		}
	}
	else {
		value = resolveFieldValue(field, bean, beanName);
	}
	if (value != null) { // 通过反射注入字段中
		ReflectionUtils.makeAccessible(field);
		field.set(bean, value);
	}
}

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
	DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
	desc.setContainingClass(bean.getClass());
	Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
	Assert.state(beanFactory != null, "No BeanFactory available");
	TypeConverter typeConverter = beanFactory.getTypeConverter();
	Object value;
	try {
		// 依赖查找的过程
		value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
	}
	catch (BeansException ex) {
		throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
	}
	synchronized (this) {
		if (!this.cached) {
			Object cachedFieldValue = null;
			if (value != null || this.required) {
				cachedFieldValue = desc;
				registerDependentBeans(beanName, autowiredBeanNames);
				if (autowiredBeanNames.size() == 1) {
					String autowiredBeanName = autowiredBeanNames.iterator().next();
					if (beanFactory.containsBean(autowiredBeanName) &&
							beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
						cachedFieldValue = new ShortcutDependencyDescriptor(
								desc, autowiredBeanName, field.getType());
					}
				}
			}
			this.cachedFieldValue = cachedFieldValue;
			this.cached = true;
		}
	}
	return value;
}

15、JSR-330 @Inject 注入

要用@Inject,首先要引入jar包:

<!-- JSR-330 API -->
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

通过添加JSR-330的依赖来让AutowiredAnnotationBeanPostProcessor支持@Inject的annotationType。

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#AutowiredAnnotationBeanPostProcessor
// 可以处理@Autowired、@Value、以及判断是否有@Inject 选择是否支持
public AutowiredAnnotationBeanPostProcessor() {
	this.autowiredAnnotationTypes.add(Autowired.class);
	this.autowiredAnnotationTypes.add(Value.class);
	try {
		this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
				ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
		logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation
// 一个字段同时标注三个注解时,注解处理有序,@Autowired > @Value > @Inject
@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
	MergedAnnotations annotations = MergedAnnotations.from(ao);
	for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
		MergedAnnotation<?> annotation = annotations.get(type);
		if (annotation.isPresent()) {
			return annotation;
		}
	}
	return null;
}

@Inject 的使用和@Autowired 并没有区别:

@Inject
private User injectedUser;

16、Java通用注解注入原理

主处理类:CommonAnnotationBeanPostProcessor(与AutowiredAnnotationBeanPostProcessor非常类似)

注入注解:

  • javax.xml.ws.WebServiceRef
  • javax.ejb.EJB
  • javax.annotation.Resource

生命周期注解:

  • javax.annotation.PostConstruct
  • javax.annotation.PreDestroy
// 支持以上注解:WebServiceRef、EJB、Resource、PostConstruct、PreDestroy
static {
	resourceAnnotationTypes.add(Resource.class);

	webServiceRefClass = loadAnnotationType("javax.xml.ws.WebServiceRef");
	if (webServiceRefClass != null) {
		resourceAnnotationTypes.add(webServiceRefClass);
	}

	ejbClass = loadAnnotationType("javax.ejb.EJB");
	if (ejbClass != null) {
		resourceAnnotationTypes.add(ejbClass);
	}
}

public CommonAnnotationBeanPostProcessor() {
	setOrder(Ordered.LOWEST_PRECEDENCE - 3);// 注意!优先级顺序
	setInitAnnotationType(PostConstruct.class);
	setDestroyAnnotationType(PreDestroy.class);
	ignoreResourceType("javax.xml.ws.WebServiceContext");

	// java.naming module present on JDK 9+?
	if (jndiPresent) {
		this.jndiFactory = new SimpleJndiBeanFactory();
	}
}
// 生命周期回调:org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
	if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
		return this.emptyLifecycleMetadata;
	}

	List<LifecycleElement> initMethods = new ArrayList<>(); // 只允许绑定方法,LifecycleElement中有invoke方法
	List<LifecycleElement> destroyMethods = new ArrayList<>(); // 只允许绑定方法,LifecycleElement中有invoke方法
	Class<?> targetClass = clazz;

	do {
		final List<LifecycleElement> currInitMethods = new ArrayList<>();
		final List<LifecycleElement> currDestroyMethods = new ArrayList<>();

		// 构建原信息
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
				LifecycleElement element = new LifecycleElement(method);
				currInitMethods.add(element);
				if (logger.isTraceEnabled()) {
					logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
				}
			}
			if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
				currDestroyMethods.add(new LifecycleElement(method));
				if (logger.isTraceEnabled()) {
					logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
				}
			}
		});

		initMethods.addAll(0, currInitMethods);
		destroyMethods.addAll(currDestroyMethods);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
			new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement
private static class LifecycleElement {

	private final Method method;

	private final String identifier;

	public LifecycleElement(Method method) {
		if (method.getParameterCount() != 0) {
			throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
		}
		this.method = method;
		this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
				ClassUtils.getQualifiedMethodName(method) : method.getName());
	}

	public Method getMethod() {
		return this.method;
	}

	public String getIdentifier() {
		return this.identifier;
	}

	// 方法回调执行
	public void invoke(Object target) throws Throwable {
		ReflectionUtils.makeAccessible(this.method);
		this.method.invoke(target, (Object[]) null);
	}

	@Override
	public boolean equals(@Nullable Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof LifecycleElement)) {
			return false;
		}
		LifecycleElement otherElement = (LifecycleElement) other;
		return (this.identifier.equals(otherElement.identifier));
	}

	@Override
	public int hashCode() {
		return this.identifier.hashCode();
	}
}

17、自定义依赖注入注解

基于@Autowired元注解创建自定义注解

// 可以自行扩展
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Autowired
public @interface MyAutowired {

    /**
     * Declares whether the annotated dependency is required.
     * <p>Defaults to {@code true}.
     */
    boolean required() default true;
}
// 与@Autowired用法一致,可以自行扩展
@MyAutowired
private Optional<User> userOptional;

基于 AutowiredAnnotationBeanPostProcessor 实现

1、使用方式一:

/**
 * 自定义依赖注入注解
 */
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD}) // 指定标注的位置
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InjectedUser {
}

// Bean非常想要提前初始化 或者提前注册的话,可以将方法定义为static的
@Bean(name = AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)
public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() {
    AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
    // @Autowired + @Inject +  新注解 @InjectedUser
    Set<Class<? extends Annotation>> autowiredAnnotationTypes =
            new LinkedHashSet<>(Arrays.asList(Autowired.class, Inject.class, InjectedUser.class));
    beanPostProcessor.setAutowiredAnnotationTypes(autowiredAnnotationTypes);
    return beanPostProcessor;
}

// 使用新注解
@InjectedUser
private User myInjectedUser;

这种方式是完全将之前的AutowiredAnnotationBeanPostProcessor 替换了,但是@Inject有可能并没有引包,所以这里有一些小缺陷。

2、使用方式二:

先注册系统的AutowiredAnnotationBeanPostProcessor,再注册自定义的AutowiredAnnotationBeanPostProcessor,可以让两个同时存在。

@Bean
// 正常的@Autowired注解优先级为Ordered.LOWEST_PRECEDENCE - 2,这里比系统的AutowiredAnnotationBeanPostProcessor优先级低,会同时存在
@Order(Ordered.LOWEST_PRECEDENCE - 3)
public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() {
    AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
    beanPostProcessor.setAutowiredAnnotationType(InjectedUser.class);
    return beanPostProcessor;
}

3、原理
在org.springframework.context.annotation.AnnotationConfigUtils 定义了一个bean:

public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
		"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";

// Bean不存在才自动注入,如果存在会优先使用用户自定义的
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
	RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
	def.setSource(source);
	beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

自定义实现(略)

生命周期处理

  • InstantiationAwareBeanPostProcessor
  • MergedBeanDefinitionPostProcessor

元数据

  • InjectedElement
  • InjectionMetadata

19、附:关于加static的bean会提前注册问题

因为当 @Bean 方法定义是非 static 的话,那么它的初始化依赖于所属类 Bean 的初始化,而处理Demo属性的注入@Autowired属于Demo这个Bean初始化的一个环节,所以当 @Autowired 字段发生时,BeanPostProcessor后置处理器并没有创建好,所以@Autowired注解会失效。
采用static,将创建BeanPostProcessor后置处理器提前到了初始化Demo这个Bean之前,所以能够处理@Autowired注解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/76499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Metal每日分享,颜色转换滤镜效果

本案例的目的是理解如何用Metal实现像素颜色转换滤镜&#xff0c;通过对像素颜色的不同读取方式获取到相应像素颜色&#xff0c;灰度图移除场景中除了黑白灰以外所有的颜色&#xff0c;让整个图像灰度化&#xff1b; Demo HarbethDemo地址 实操代码 // 转成灰度图滤镜 let f…

js深拷贝浅拷贝与lodash

title: js深拷贝浅拷贝与lodash date: 2022/9/27 14:46:25 categories: lodashjs入门 深拷贝和浅拷贝 ref&#xff1a;JS传递参数时的内部存储逻辑 JS 變數傳遞探討&#xff1a;pass by value 、 pass by reference 還是 pass by sharing&#xff1f; 在这个问题前&#xff…

LeetCode HOT 100 —— 169.多数元素

题目 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 思路 方法一&#xff1a;哈希表 遍历整个数组&#xff0c;记录每个数值出…

JAVA IO详解

目录 一、流的概念与作用 二、Java IO的用途和特征 三、流的使用详解 一、流的概念与作用 流(Stream)&#xff1a; 在Java IO中&#xff0c;流是一个核心的概念。流从概念上来说是一个连续的数据传输过程。人们根据数据传输特性将流抽象为各种类&#xff0c;方便更直观的进…

【服务器数据恢复】服务器raid5崩溃导致上层应用不可用的数据恢复案例

服务器数据故障&#xff1a; 某公司服务器8块硬盘组成raid5磁盘阵列&#xff0c;其中有2块硬盘故障指示灯报警&#xff0c;其他硬盘指示灯正常&#xff0c;上层应用不可用。 服务器数据恢复过程&#xff1a; 1、服务器数据恢复工程师拿到故障服务器所有硬盘后对出现物理故障的…

ADI Blackfin DSP处理器-BF533的开发详解18:用触摸屏的例程来理解中断(含源码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 硬件实现原理 ADSP-EDU-BF533 开发板上的中断资源连接到了 CPLD&#xff0c;并通过 CPLD 将中断信号连接到 PF0 触发&#xff0c;通过 CPLD映射的…

ubuntu交叉编译(armv7_32位)onnx源码_cpu版本

1 下载onnx git clone https://github.com/microsoft/onnxruntime cd onnxruntime git submodule update --init --recursive2 编译 由于是交叉编译&#xff0c;所以需要设置一下编译工具&#xff0c;在网上搜索看到了这个 chineseocr_lite/build-onnxruntime-android.sh a…

Jmeter(一):jmeter概述与工作原理,安装与基本配置介绍

Jmeter(1)&#xff1a;jmeter概述与工作原理 jmeter概述与工作原理 JMeter 是 Apache 基金会 Jakarta 上的一个纯 Java 开源项目&#xff0c;起初用于基 于 Web 的压力测试&#xff08;pressure test&#xff09;&#xff0c;后来其应用范围逐渐扩展到对文件传输 FTP, 大型数据…

基于JavaWeb+JSP的校园二手交易平台(源码+数据库+说明文档)

目录 一、前后端功能模块 1.用户web前端页面功能模块 2.后台信息管理模块 二、开发环境 三、开发技术 四、页面设计 1.登录注册界面 2.网页主页界面 3.商品列表界面 4.商品详情界面 5.支付页面 6.支付成功后页面 7.我的订单页面 ​8.个人已发布与待处理订单界面…

google外链重要性高吗?谷歌外链作用大不大

google外链重要性高吗&#xff1f; 答案是&#xff1a;非常重要&#xff0c;而且要注重建设付费GPB外链。 要相信有价值的外链一般都比较难获取&#xff0c;那种高流量的外链一般要靠自己去outreach, 但是成功率比较低&#xff0c;我们需要用金钱和优质外链资源去交换 做高质…

程序员的浪费,Python一对一还原《点燃我,温暖你》里面比较火的那个爱心代码 | 附源码

前言 包子们&#xff0c;上午好 最近有个剧挺火的 就是那个程序员的剧&#xff0c;叫《点燃我&#xff0c;温暖你》 最近听说很火呀&#xff0c;那作为程序员&#xff0c;Python中的战斗机的小编&#xff0c;能不给大家安排一波&#xff01; 怎么说呢&#xff0c;用这个表白也…

我凭借这 1000 道 java 真题,顺利拿下京东、饿了么、阿里大厂 offer

今天这篇文章也算是一次面试总结了吧&#xff01; 毕竟金九银十过去了&#xff0c;总得给大家来点东西交代交代&#xff01; 所以今天&#xff0c;这篇文章就应运而生了&#xff0c;给大家来点正正经经的干货教学&#xff0c;让大家体验一下干货的魅力&#xff01; 小编今天这里…

【C语言数据结构(基础篇)】第一站:时间复杂度与空间复杂度

目录 一、什么是时间复杂度和空间复杂度 1.算法效率 2.时间复杂度的概念 3.空间复杂度的概念 二、如何计算常见的时间复杂度 1.大O的渐进表示法 2.一些时间复杂度的例子 &#xff08;1&#xff09;例1 &#xff08;2&#xff09;例2 (3)例3 (4)例4 &#xff08;5&a…

【计算机视觉+自动驾驶】二、多任务深度学习网络并联式、级联式构建详细讲解(图像解释 超详细必看)

觉得有帮助麻烦点赞关注收藏~~~ 一、多任务网络的主要分类 目前建立的多任务网络可以分为两种方法&#xff0c;一种为并联多任务网络结构&#xff0c;另一种为级联多任务网络结构&#xff0c;两种网络构建方式分别如下图所示 并联式 级联式 并联网络结构大多为共享基础网络而…

ADI Blackfin DSP处理器-BF533的开发详解14:LED跑马灯(含源代码)

接口讲完了&#xff0c;下面写点应用程序&#xff0c;GPIO最典型的应用&#xff0c;LED跑马灯。 硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 *硬件实现原理 ADSP-EDU-BF533开发板上共设计了…

2005-2020年全国31省劳动力市场分割指数

2005-2020年全国31省劳动力市场分割指数 1、时间&#xff1a;2005-2020年 2、范围&#xff1a;包括全国31省&#xff0c; 3、数据内容&#xff1a;数据存在缺失&#xff0c;下载链接界面有数据预览&#xff0c;具体缺失情况参看链接内数据预览&#xff0c; 内含原始数据、A…

把废旧监控改无人机遥控车红外远程摄像头

像我们这些精打细算的业余玩家&#xff0c;淘个新宝贝都要掂量掂量。很羡慕能买到专用红外摄像头配无人机。可是手头不宽裕&#xff0c;只有一些旧零件。这都是废物再利用&#xff0c;所以说不要太追求性能了&#xff0c;自然让他工作就好&#xff0c;测试这条路线的可行性。 …

blneder 蜡笔

文章目录简介.打开蜡笔.基本操作.自由线.图形工具.图层.遮罩.画布.画布原点.![在这里插入图片描述](https://img-blog.csdnimg.cn/46cb7019e8ff41e6b391e056c616ce32.png)画布旋转.辅助.圆形.径向.平行.栅格.等距.编辑模式.顶部工具栏.选择.曲线编辑.左侧工具栏.快捷键.画笔深度…

值得一看的Linux内核—中断下半部之软中断

软中断 软中断&#xff08;softirq&#xff09;是中断处理程序在开启中断的情况下执行的部分&#xff0c;可以被硬中断抢占。 内核定义了一张软中断向量表&#xff0c;每种软中断有一个唯一的编号&#xff0c;对应一个softirq_action实例&#xff0c;softirq_action实例的成员…

b站黑马JavaScript的Node.js案例代码——考试成绩整理案例

目录 目标效果&#xff1a; 重点原理&#xff1a; 1.js中split方法——转换字符串为数组 2.js中forEach方法——遍历数组中每个对象 3.js数组操作中push方法——添加1/多个元素去数组的末尾 4.js数组操作中replace方法——在字符串中用一些字符替换另一些字符 5.js数组操…