一建延期最新消息2022/seo运营做什么
1、ApplicationContext与BeanFactory
BeanFactory是ApplicationContext的父级接口:(citl+alt+u查看类关系图)
在springboot的启动类中,我们通过SpringApplication.run方法拿到的是继承了ApplicationContext的ConfigurableApplicationContext接口:
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringPlusApplication.class, args);
通过.getClass() 方法获取 Spring 应用上下文的类类型,得知在运行时,在本类中ApplicationContext 实现类的具体类型是AnnotationConfigServletWebServerApplicationContext
System.out.println(applicationContext.getClass());
IOC,DI,管理SpringBean的生命周期,都是由beanFactory的实现类完成:
//例:通过反射得到存放bean的数组Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");singletonObjects.setAccessible(true);//得到BeanFactoryConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();//解析beanFactory中有多少单例的beanMap<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);map.forEach((k,v)->{System.out.println(k+":"+v);});
注册两个自定义的bean:
@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);@Autowiredprivate ApplicationEventPublisher publisher;public void register(){log.debug("用户注册");publisher.publishEvent(new UserRegisterEvent(this));}}
@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);@EventListenerpublic void sendMessage(UserRegisterEvent event){log.debug(event.toString());log.info("发送短信");}
}
//例:通过反射得到存放bean的数组Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");singletonObjects.setAccessible(true);//得到BeanFactoryConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();//解析beanFactory中有多少单例的beanMap<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);map.entrySet().stream().filter(k->k.getKey().startsWith("component")).forEach(e->{System.out.println(e.getKey()+":"+e.getValue());});
在上面的案例中,还可以观察到一种现象,即自定义的两个类,Component1和Component2的类名首字母都是大写,而在解析beanFactory时,.startsWith("component") 方法的component却是小写,其原因在于:
Spring中默认情况下,Bean的名称是小写的,Bean的名称是在定义Bean时指定的,如果你没有显式地为Bean指定名称,Spring会根据一定的命名规则自动生成Bean的名称。在自动生成名称时,通常会将类名的首字母小写作为Bean的名称。
同时ApplicationContext也对BeanFactory通过聚合的方式进行了功能的扩展
- MessageSource:国际化翻译
- ResourcePatternResolver:根据通配符获取资源
- EnvironmentCapable:获取配置信息
- ApplicationEventPublisher:发送事件
1.1、MessageSource
在resource目录下准备四个文件:
messages_en.properties:
hi=Hello
messages_ja.properties:
hi=こんにちは
messages_zh.properties:
hi=你好
System.out.println(applicationContext.getMessage("hi", null, Locale.CHINESE));
System.out.println(applicationContext.getMessage("hi", null, Locale.ENGLISH));
System.out.println(applicationContext.getMessage("hi", null, Locale.JAPANESE));
1.2、ResourcePatternResolver
Resource resource = applicationContext.getResource("classpath*:META-INF/spring.factories");
System.out.println(resource);
1.3、 EnvironmentCapable
System.out.println(applicationContext.getEnvironment().getProperty("java_home")); System.out.println(applicationContext.getEnvironment().getProperty("server.port"));
1.4、 ApplicationEventPublisher
发布事件通知,类似于消息中间件的功能,可以实现解耦:
首先创建一个中间类:
public class UserRegisterEvent extends ApplicationEvent {public UserRegisterEvent(Object source) {super(source);}
}
消息生产者,调用publisher.publishEvent():
@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);@Autowiredprivate ApplicationEventPublisher publisher;public void register(){log.debug("用户注册");publisher.publishEvent(new UserRegisterEvent(this));}}
消息消费者,将UserRegisterEvent作为参数传入,同时方法上需要加上@EventListener:
@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);@EventListenerpublic void sendMessage(UserRegisterEvent event) {log.debug(event.toString());log.info("发送短信");}
}
主类:
Component1 component1 = applicationContext.getBean("component1", Component1.class);
component1.register();
ApplicationEventPublisher的局限性:
不支持分布式事件发布。在典型的 Spring 应用程序中,ApplicationEventPublisher是用于在单个应用程序上下文中发布事件的,而不是用于跨应用程序或分布式系统的事件传播。所以在分布式系统中,推荐使用更加成熟的消息队列、事件总线或者分布式事件处理系统。
1.5、小结:
ApplicationContext和BeanFactory的区别和联系:
区别:
- ApplicationContext:ApplicationContext是 BeanFactory的子接口之一。它提供了更多的企业级功能,如国际化、事件传播、资源加载等。ApplicationContext在启动时就会加载所有的单例 bean,并在初始化过程中完成依赖注入和各种后处理器的应用。ApplicationContext通常在实际开发中更为常用,因为它提供了更多的特性和功能。
- BeanFactory:BeanFactory是 Spring 框架的核心接口之一,它提供了 bean 的配置、创建、管理和查找功能。BeanFactory的实现类负责加载 bean 的定义信息,并且在需要时才实例化 bean。与 ApplicationContext相比,BeanFactory更轻量级,因为它只在需要时才初始化 bean,节省了资源。但是它不提供像 ApplicationContext那样的扩展功能。
联系:
- 接口关系:ApplicationContext是 BeanFactory的子接口,因此 ApplicationContext包含了 BeanFactory的所有功能,并且在此基础上提供了更多的功能。
- 功能:BeanFactory是 Spring 框架中负责管理 bean 的核心接口,而 ApplicationContext在 BeanFactory的基础上提供了更多的企业级功能和扩展。因此,ApplicationContext 在实际开发中更常用,但如果对资源有较高要求,可以考虑使用轻量级的 BeanFactory。
2、容器实现
列举一些BeanFactory常见的实现:
-
XmlBeanFactory: XmlBeanFactory是 Spring 最基本的容器实现之一,它从 XML 文件中加载 bean 的定义信息,并在需要时实例化和管理 bean。这是 Spring 早期版本中最常用的容器实现之一,但现在已经不推荐使用,因为它在初始化时会加载所有的 bean,可能会造成性能问题。
-
ClassPathXmlApplicationContext: ClassPathXmlApplicationContext是基于 XML 配置文件的应用程序上下文实现。它从类路径中加载 XML 文件,并使用其中的 bean 定义来初始化应用程序上下文。这是使用 Spring 框架时最常见的容器实现之一。
-
FileSystemXmlApplicationContext: FileSystemXmlApplicationContext也是基于 XML 配置文件的应用程序上下文实现,但它从文件系统中加载 XML 文件。与 ClassPathXmlApplicationContext不同,它需要指定 XML 文件的绝对路径或相对路径。
-
AnnotationConfigApplicationContext: AnnotationConfigApplicationContext是基于注解的应用程序上下文实现。它会扫描指定的包路径,查找带有特定注解的类(如@Configuration、@Component等),并根据这些类中的配置来初始化 bean。
-
GenericApplicationContext: GenericApplicationContext是一个泛型的应用程序上下文实现,它可以通过编程方式动态地注册和管理 bean。与基于 XML 或注解的上下文实现不同,GenericApplicationContext允许在运行时动态地添加、修改和删除 bean。(是不带后处理器,较为干净的实现,后续演示后处理器使用该实现完成)
-
DefaultListableBeanFactory:是 BeanFactory 最重要的实现,像控制反转和依赖注入功能,都是它来实现
2.1、DefaultListableBeanFactory
为了演示功能,首先创建几个静态内部类:
- Config类的作用是注册自定义bean,类上加入@Configuration 注解。
- bean3和bean4同时实现了Inter接口。
/*** 注册bean*/@Configurationstatic class config {@Beanpublic Bean1 createBean1() {return new Bean1();}@Beanpublic Bean2 createBean2() {return new Bean2();}@Beanpublic Bean3 createBean3() {return new Bean3();}@Beanpublic Bean4 createBean4() {return new Bean4();}}interface Inter{}static class Bean1 {private static final Logger log = LoggerFactory.getLogger(Bean1.class);@AutowiredBean2 bean2;//那么如果同时加了 @Autowired 和 @Resource 呢?答案是以 @Autowired为准。因为在后处理器中它的位置靠前
// @Autowired//如果同一个接口有多个实现类,需要注入,必须要使用@Qualifier 或者 和bean的名字同名@Resource(name = "createBean4")//如果使用resource并且标注了name,按照name中的bean装配private Inter createBean3;public Bean1() {log.info("Bean1 created");}public Bean2 getBean2() {return bean2;}public Inter getInter() {return createBean3;}}static class Bean2 {private static final Logger log = LoggerFactory.getLogger(Bean2.class);public Bean2() {log.info("Bean2 created");}}static class Bean3 implements Inter{private static final Logger log = LoggerFactory.getLogger(Bean3.class);public Bean3(){log.info("Bean3 created");}}static class Bean4 implements Inter{private static final Logger log = LoggerFactory.getLogger(Bean4.class);public Bean4(){log.info("Bean4 created");}}
主类:
//DefaultListableBeanFactory是beanFactory最重要的实现DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//定义bean 包括范围,初始化,销毁 这里定义的是config bean
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(config.class).setScope("singleton").getBeanDefinition();
//注册config bean
beanFactory.registerBeanDefinition("config", beanDefinition);
我们还需要给beanFactory添加一些常用的后处理器:(如果不添加后处理器,beanFactory不会主动解析@Configuration ,@Bean 等注解)
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
可以遍历查看后处理器:
for (String name : beanFactory.getBeanDefinitionNames()) {//org.springframework.context.annotation.internalConfigurationAnnotationProcessor 解析@Bean//org.springframework.context.annotation.internalAutowiredAnnotationProcessor 解析@AutoWired @Value//org.springframework.context.annotation.internalCommonAnnotationProcessor 解析@Resource//org.springframework.context.event.internalEventListenerProcessor//org.springframework.context.event.internalEventListenerFactorySystem.out.println(name);}
此时只是bean工厂中有了这些后处理器中的bean,但是要生效还需要:
其中beanFactory.getBeansOfType():根据类型获取多个bean
processor.postProcessBeanFactory():执行bean工厂后处理器
Collection<BeanFactoryPostProcessor> values = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values();
for (BeanFactoryPostProcessor processor : values) {processor.postProcessBeanFactory(beanFactory);
}//再次查看beanFactory中的bean
for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);
}
此时beanFactory已经有了解析@Configuration ,@Bean 等注解的能力:
因为此时bean1中有一个使用@AutoWired 注入的bean2,此时尝试一下获取bean2:
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
可以发现bean2没有被初始化:
需要添加bean的相关后处理器,解析@AutoWired @Value @Resource 等注解信息:
- internalConfigurationAnnotationProcessor:解析@Bean
- internalAutowiredAnnotationProcessor :解析@AutoWired @Value
- internalCommonAnnotationProcessor: 解析@Resource
Collection<BeanPostProcessor> postProcessors = beanFactory.getBeansOfType(BeanPostProcessor.class).values();
for (BeanPostProcessor postProcessor : postProcessors) {System.out.println("<<<<<"+postProcessor);beanFactory.addBeanPostProcessor(postProcessor);
}for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);
}
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
至此所有的bean都加载完毕。但是有一个问题:bean3和bean4都实现了Inter接口,我在bean1中对其注入时,如果类型是bean3和bean4的父类,那么使用@AutoWired注解将注入哪一个bean?
如果使用@AutoWired注解,需要配合@Qualifier 注解确定具体注入哪一个实现类。
如果使用@Resource(name = "createBean4")注解,按照name属性的bean进行装配:
属性名虽然叫createBean3,但是实际注入的是@Resource 的name所在的bean:
那如果同时标上了@AutoWired 和@Resource 呢?答案是以 @Autowired为准。因为在后处理器中它的位置靠前:
CommonAnnotationBeanPostProcessor的order排序:(解析@Resource)
AutowiredAnnotationProcessor 的order排序:(解析@AutoWired @Value)
order排序的数字越小,优先级越高。所以同时标注@AutoWired 和@Resource 时,以@AutoWired 为准。
2.2、常见 ApplicationContext 实现
首先自定义两个bean:
static class Bean1{}static class Bean2{private Bean1 bean1;public void setBean1(Bean1 bean1) {this.bean1 = bean1;}public Bean1 getBean1() {return bean1;}}
使用xml的方式进行bean管理:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义bean--><bean id="bean1" class="com.itbaima.a01.ApplicaionContextDemo.Bean1"/><bean id="bean2" class="com.itbaima.a01.ApplicaionContextDemo.Bean2"><property name="bean1" ref="bean1"/></bean>
</beans>
2.2.1、基于classPath 读取xml文件
public static void testClassPathXmlApplicationContext(){ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("test1.xml");for (String name : applicationContext.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(applicationContext.getBean(Bean2.class).getBean1());}
2.2.2、基于磁盘路径读取xml文件
public static void testFileSystemXmlApplicationContext(){FileSystemXmlApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\Idea_workspace\\2024\\spring-plus\\src\\main\\resources");for (String name : applicationContext.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(applicationContext.getBean(Bean2.class).getBean1());}
2.2.3、基于java配置类实现
会默认加上后处理器:
@Configurationstatic class Config{@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2(Bean1 bean1) {Bean2 bean2 = new Bean2();bean2.setBean1(bean1);return bean2;}}
public static void testAnnotationConfigApplicationContext(){AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);for (String name : applicationContext.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(applicationContext.getBean(Bean2.class).getBean1());}
2.2.4、基于java配置类实现 web相关
public static void testAnnotationConfigServletWebServerApplicationContext(){AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);}
@Configurationstatic class WebConfig{/*** 将内嵌tomcat注册成bean* @return*/@Beanpublic ServletWebServerFactory servletWebServerFactory() {return new TomcatServletWebServerFactory();}@Beanpublic DispatcherServlet dispatcherServlet(){return new DispatcherServlet();}@Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){return new DispatcherServletRegistrationBean(dispatcherServlet, "/");}@Bean("/hello")public Controller controller(){return ((httpServletRequest, httpServletResponse) -> {httpServletResponse.getWriter().write("hello");return null;});}}
2.3、小结
beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象
我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中
bean definition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
beanFactory 需要手动调用 beanFactory 后处理器对它做增强
例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition
beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强
例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的
bean 后处理的添加顺序会对解析结果有影响,见视频中同时加 @Autowired,@Resource 的例子
beanFactory 需要手动调用方法来初始化单例
beanFactory 需要额外设置才能解析 ${} 与 #{}
3、Bean的生命周期
Spring 中的 bean 生命周期通常包括以下几个阶段:
-
实例化(Instantiation):容器根据配置信息(XML、注解等)创建 bean 的实例。这通常涉及到实例化目标 bean 类,并根据配置注入依赖。
-
属性设置(Population):容器通过依赖注入(Dependency Injection)将配置的属性值或引用设置到 bean 实例中。这包括基本属性、引用类型、集合类型等。
-
初始化(Initialization):
- Initialization callbacks:在 bean 实例化完成并且所有属性设置完毕后,容器会调用 bean 的InitializingBean接口的afterPropertiesSet()方法,或者调用在配置中声明的初始化方法。
- BeanPostProcessors:在调用初始化方法之前和之后,容器会调用所有注册的 BeanPostProcessor实现类的postProcessBeforeInitialization
()
和postProcessAfterInitialization()方法,允许对 bean 进行自定义的初始化处理。
-
使用(In Use):在初始化完成后,bean 可以被容器或其他 bean 使用。
-
销毁(Destruction):
- DisposableBean:如果 bean 实现了 DisposableBean接口,容器在销毁 bean 之前会调用其destroy() 方法。
- 自定义销毁方法:在配置中可以声明自定义的销毁方法,在容器关闭时调用。
-
销毁回调(Destruction callbacks):与初始化类似,容器在销毁 bean 之前会调用所有注册的 BeanPostProcessor实现类的 postProcessBeforeDestruction
()
方法,允许对 bean 进行自定义的销毁处理。
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");}}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
// return false;}return true;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");}return pvs;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");}return bean;}
}
4、常见的Bean后处理器
首先创建几个bean:
public class Bean2 {
}
public class Bean3 {
}
public class Bean1 {private static final Logger log = LoggerFactory.getLogger(Bean1.class);Bean3 bean3;Bean2 bean2;@Autowiredpublic void setBean1(Bean3 bean3) {log.debug("setBean3");this.bean3 = bean3;}@Resourcepublic void setBean2(Bean2 bean2) {log.debug("setBean2");this.bean2 = bean2;}@Autowiredpublic void autowiredMethod(@Value("${JAVA_HOME}")String name){log.debug("注入:"+name);}@PostConstructpublic void initMethod(){log.debug("初始化");}@PreDestroypublic void destroyMethod(){log.debug("销毁");}
}
@ConfigurationProperties(prefix = "java")
public class Bean4 {String home;String version;public Bean4() {}public Bean4(String home, String version) {this.home = home;this.version = version;}/*** 获取* @return home*/public String getHome() {return home;}/*** 设置* @param home*/public void setHome(String home) {this.home = home;}/*** 获取* @return version*/public String getVersion() {return version;}/*** 设置* @param version*/public void setVersion(String version) {this.version = version;}public String toString() {return "Bean4{home = " + home + ", version = " + version + "}";}
}
在主类中,我们使用ApplicationContext的GenericApplicationContext实现(在容器实现中提到过,GenericApplicationContext是不带后处理器,较为干净的实现)
GenericApplicationContext context = new GenericApplicationContext();
//注册bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
context.registerBean("bean4", Bean4.class);//初始化容器
context.refresh();//销毁容器
context.close();
此时并没有打印出关于bean中定义的任何信息。
4.1、解析@AutoWired @Value
如果需要解析bean1中的@AutoWired @Value ,则主类中需要增加:
//这行代码用于设置默认的 Bean 工厂的自动装配候选项解析器(AutowireCandidateResolver)。
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
//AutowiredAnnotationBeanPostProcessor 是 Spring 框架中用于处理 @Autowired 注解的后置处理器,它会在 bean 实例化后,对标记了 @Autowired 注解的字段进行自动注入。
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
4.2、解析@Resource @PostConstruct @PreDestroy
context.registerBean(CommonAnnotationBeanPostProcessor.class);
4.3、解析@ConfigurationProperties
//将 ConfigurationPropertiesBindingPostProcessor 注册到 Spring 容器中的默认 Bean 工厂中。
//ConfigurationPropertiesBindingPostProcessor 是 Spring Boot 中用于处理 @ConfigurationProperties 注解的后置处理器。
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());System.out.println(context.getBean("bean4"));
4.4、@Autowired bean 后处理器运行分析
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerSingleton("bean2", new Bean2());beanFactory.registerSingleton("bean3", new Bean3());//解析@value不出异常beanFactory.setAutowireCandidateResolver(newContextAnnotationAutowireCandidateResolver());//解析${}需要加上beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);
通过反射,演示一下AutowiredAnnotationBeanPostProcessor后处理器运行的过程:
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();//设置后处理器要处理的beanFactory,因为bean2,bean3都在beanFactory中
processor.setBeanFactory(beanFactory);
AutowiredAnnotationBeanPostProcessor执行依赖注入,实际是调用了:
processor.postProcessProperties(null, bean1, "bean1");
在该方法内部,再次调用了
因为findAutowiringMetadata() 是私有方法,所以通过反射的方式进行调用处理:
//通过反射获取findAutowiringMetadata方法
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);//执行findAutowiringMetadata方法 //参数一:方法所在的对象实例 参数二:方法的参数
//1.收集类中所有加了@AutoWired @Value注解的方法/成员变量
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor,"bean1",Bean1.class,null);
这样就拿到了findAutowiringMetadata() 方法的返回,利用方法的返回调用inject()方法:
//2.进行依赖注入
metadata.inject(bean1,"bean1",null);
System.out.println(bean1);
由此可知:postProcessProperties方法内部做了两件事:
1.收集类中所有加了@AutoWired @Value注解的方法/成员变量
2.进行依赖注入
而inject() 方法内部如何根据类型找到值?
//1.得到成员变量/方法的信息Field bean3 = Bean1.class.getDeclaredField("bean3");//2.封装成DependencyDescriptorDependencyDescriptor dependencyDescriptor = new DependencyDescriptor(bean3, false);//3.调用beanFactory的doResolveDependencyObject o = beanFactory.doResolveDependency(dependencyDescriptor, null, null, null);System.out.println(o);
//方法注入时,以某个方法的参数为单位去容器中找
Method method = Bean1.class.getDeclaredMethod("setBean1", Bean3.class);
DependencyDescriptor descriptor = new DependencyDescriptor(new MethodParameter(method, 0), false);
Object o1 = beanFactory.doResolveDependency(descriptor, null, null, null);
System.out.println(o1);//方法的参数值注入,以某个方法的参数为单位去容器中找
Method method1 = Bean1.class.getDeclaredMethod("autowiredMethod", String.class);
DependencyDescriptor dependencyDescriptor1 = new DependencyDescriptor(new MethodParameter(method1, 0), false);
Object o2 = beanFactory.doResolveDependency(dependencyDescriptor1, null, null, null);
System.out.println(o2);
inject内部如何根据类型找到值?
1.得到成员变量/方法的信息
2.封装成DependencyDescriptor
3.调用beanFactory的doResolveDependency
5、BeanFactory 后处理器
如果需要解析@Bean @Mapper 等注解,则需要使用到BeanFactory的后处理器:
//需要添加对@Bean @ComponentScan @Import 的处理器
context.registerBean(ConfigurationClassPostProcessor.class);//需要添加对@MapperScan的后处理器
context.registerBean(MapperScannerConfigurer.class, bd -> {bd.getPropertyValues().add("basePackage","com.itbaima.a04.mapper");
});
-
ConfigurationClassPostProcessor 可以解析:
-
@ComponentScan
-
@Bean
-
@Import
-
@ImportResource
-
-
MapperScannerConfigurer 可以解析:
-
@Mapper
-
下面会模拟源码中@ComponentScan @Bean @Mapper 后处理器的实现,在进行演示之前,首先介绍一下一些会用到的API和类:
- CachingMetadataReaderFactory:是 Spring 框架中用于加载和缓存类元数据的工厂类,它提供了一种高效的方式来获取类的元数据信息,并且通过缓存机制提高了性能。当需要获取某个类的元数据时,它会通过底层的 MetadataReader实现类来读取类的信息,并将其封装为MetadataReader对象。
- AnnotationBeanNameGenerator:是 Spring 框架中用于生成 bean 名称的默认策略类之一。当你不显式地为 bean 指定名称时,Spring 会自动使用它来生成 bean 的名称。这样,你就可以通过在类上添加相应的注解来控制 bean 的名称,而不需要额外进行配置。
- BeanDefinitionBuilder:是 Spring 框架中用于构建 BeanDefinition对象的工具类。在 Spring 中,BeanDefinition是用于描述和定义 bean 的元数据信息的类,包括了 bean 的类名、作用域、构造函数参数、属性值、初始化方法、销毁方法等等。
其中BeanDefinitionBuilder的一些常用方法:
static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass)
: 创建一个通用的 bean 定义构建器,指定 bean 的类名。BeanDefinitionBuilder setScope(String scope)
: 设置 bean 的作用域。BeanDefinitionBuilder addPropertyValue(String name, Object value)
: 添加一个属性值到 bean 的属性列表中。BeanDefinitionBuilder addConstructorArgValue(Object value)
: 添加一个构造函数参数值到 bean 的构造函数参数列表中。BeanDefinitionBuilder setInitMethodName(String methodName)
: 设置 bean 的初始化方法名。BeanDefinitionBuilder setDestroyMethodName(String methodName)
: 设置 bean 的销毁方法名。BeanDefinitionBuilder setLazyInit(boolean lazyInit)
: 设置是否启用延迟初始化。BeanDefinitionBuilder setAutowireMode(int autowireMode)
: 设置自动装配模式。
5.1、模拟@ComponentScan 的实现
找到类中的@ComponentScan 注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (!ObjectUtils.isEmpty(componentScan)) {//得到注解的值for (String base : componentScan.basePackages()) {//拼装成路径信息String path = "classpath*:" + base.replace(".","/") + "/**/*.class";//用于读取类的元数据 在运行时加载类的元数据,如类名、注解信息等CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();//用于根据类的注解生成 Bean 的名称AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();//context的扩展//得到路径下的所有资源是ApplicationContext对于BeanFactory的扩展GenericApplicationContext context = new GenericApplicationContext();Resource[] resource = context.getResources(path);for (Resource r : resource) {//从元数据读取工厂中获取一个元数据读取器readerMetadataReader reader = factory.getMetadataReader(r);// System.out.println("类名"+reader.getClassMetadata().getClassName());// System.out.println("是否加了Component"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));// System.out.println("是否加了Component 相关"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));//从元数据读取器reader中获取注解元数据AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();//如果类上加了Component及相关注解if (annotationMetadata.hasMetaAnnotation(Component.class.getName())|| annotationMetadata.hasAnnotation(Component.class.getName())) {AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;//生成一个唯一的bean名称。String name = generator.generateBeanName(beanDefinition, defaultListableBeanFactory);//通过BeanDefinitionBuilder创建的bean定义(即beanDefinition)注册到Spring容器中。// registerBeanDefinition方法将生成的bean名称(name)与对应的bean定义关联起来,使得该bean可以被Spring容器管理和使用。defaultListableBeanFactory.registerBeanDefinition(name, beanDefinition);}}}}
5.2、模拟解析 @Bean
//读取@Bean注解CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();//1.读取指定类路径下的信息MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itbaima/a04/Config.class"));//2.获取指定类中所有标注了@Bean方法的信息Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());//遍历这些方法for (MethodMetadata method : methods) {//使用反射获取注解信息String initMothod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();//用于创建一个通用的 Bean 定义。BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();//设置工厂方法来创建 Bean。使用config中的方法作为工厂方法创建beandefinitionBuilder.setFactoryMethodOnBean(method.getMethodName(), "config");//处理标注了@Bean的有参方法definitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);if (initMothod.length()>0){//用于设置 Bean 的初始化方法名。初始化方法是在 Bean 实例化后立即调用的方法。definitionBuilder.setInitMethodName(initMothod);}//用于获取最终的 Bean 定义对象。AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;//将之前创建的 Bean 定义注册到 Spring 的 Bean 工厂中。 参数一:bean的名称 参数二:用于获取最终的 Bean 定义对象。defaultListableBeanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);}
5.3、模拟解析@Mapper
关于Mapper工厂,需要在参数中指定SqlSessionFactory,以便Spring 容器将指定的依赖注入到方法的参数中。
@Beanpublic MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}
模拟实现:
与@Bean类似,被Spring bean管理的类型都是工厂,但是在处理bean名称的时候,需要额外进行,保证bean的名称是每个Mapper接口的名称,而不是工厂的名称。
try {//获取指定路径下的资源PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();Resource[] resources = patternResolver.getResources("classpath:com/itbaima/a04/mapper/**/*.class");CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {MetadataReader reader = factory.getMetadataReader(resource);ClassMetadata classMetadata = reader.getClassMetadata();//判断路径下的某个资源是类还是接口if (classMetadata.isInterface()) {//生成接口对应的MapperFactoryBean//被spring管理的bean的类型 是 MapperFactoryBeanAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).addConstructorArgValue(classMetadata.getClassName())//给构造设置参数值.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)//按照类型装配,对应方法上的参数.getBeanDefinition();//创建bean名称 此处是根据bean定义去生成名称的,我们此时定义的是bean工厂//所以要在定义一个beanDefinition,名字是Mapper1 Mapper2(具体的接口,非工厂)AbstractBeanDefinition beanDefinitionInter = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();String name = generator.generateBeanName(beanDefinitionInter, registry);registry.registerBeanDefinition(name, beanDefinition);}}} catch (IOException e) {throw new RuntimeException(e);}
相关文章:

spring高级篇(一)
1、ApplicationContext与BeanFactory BeanFactory是ApplicationContext的父级接口:(citlaltu查看类关系图) 在springboot的启动类中,我们通过SpringApplication.run方法拿到的是继承了ApplicationContext的ConfigurableApplicatio…...

免费的GPT-3.5 API服务aurora
什么是 aurora ? aurora 是利用免登录 ChatGPT Web 提供的无限制免费 GPT-3.5-Turbo API 的服务,支持使用 3.5 的 access 调用。 【注意】:仅 IP 属地支持免登录使用 ChatGPT的才可以使用(也可以自定义 Baseurl 来绕过限制&#x…...

突破编程_C++_网络编程(Windows 套接字(处理 TCP 粘包问题))
1 TCP 协议与粘包问题概述 1.1 TCP 粘包的产生原因 TCP粘包问题的产生原因涉及多个方面,主要的原因如下: 首先,发送方在发送数据时,由于TCP协议为提高传输效率而采用的Nagle算法,可能会将多个小数据包合并成一个大数…...

【训练营】DateWhale——动手学大模型应用开发(更新中)
文章目录 写在前面大模型简介LLM简介RAG简介LangChain开发框架开发LLM应用的整体流程 写在前面 大模型时代从GPT爆发开始到现在已有一年多了,深度学习发展之快无法想象,一味感叹技术发展速度超越个人学习速度是没用的,倒不如花点时间参加一些…...

【学习笔记十九】EWM Yard Management概述及后台配置
一、EWM Yard堆场管理业务概述 1.Yard Management基本概念 YARD管理针对的是库房以外的区域,可以理解为入大门开始到库门之前的这部分的区域 堆场结构 像在仓库中一样,将相应仓位映射为堆场仓位,可将其分组到堆场分区。场地中可能具有以下结…...

【环境搭建】(五)Ubuntu22.04安装cuda_11.8.0+cudnn_8.6.0
一个愿意伫立在巨人肩膀上的农民...... 设备配置: 一、安装GCC 安装cuda之前,首先应该安装GCC,安装cuda需要用到GCC,否则报错。可以先使用下方指令在终端查看是否已经安装GCC。 gcc --version 如果终端打印如下则说明已经安装…...

【UE5.1】使用MySQL and MariaDB Integration插件——(3)表格形式显示数据
在上一篇(【UE5.1】使用MySQL and MariaDB Integration插件——(2)查询)基础上继续实现以表格形式显示查询到的数据的功能 效果 步骤 1. 在“WBP_Query”中将多行文本框替换未网格面板控件,该控件可以用表格形式布局…...

JVM复习
冯诺依曼模型与计算机处理数据过程相关联: 冯诺依曼模型: 输入/输出设备存储器输出设备运算器控制器处理过程: 提取阶段:输入设备传入原始数据,存储到存储器解码阶段:由CPU的指令集架构ISA将数值解…...

63、ARM/STM32中IIC相关学习20240417
完成温湿度传感器数据采集实验。 【思路:1.通过IIC通信原理,理解其通信过程,通过调用封装的IIC函数达成主机和从机之间:起始信号、终止信号、读、写数据的操作; 2.了解温湿度传感器控制芯片SI7006的工作原理&#…...

离岸人民币与人民币国际化
参考 什么是离岸人民币?它有什么用? - 知乎 “人民币就是人民币,为什么要在它前面加上离岸二字?” “既然有离岸人民币,是否有在岸人民币?” 今天我们就简单了解一下什么是离岸人民币。 离岸/在岸人民币…...

Linux平台上部署和运行Ollama的全面指南
Ollama的安装与配置 Ollama提供了一种简单的安装方法,只需一行命令即可完成安装,但是对于想要更深入了解和自定义安装的用户,我们也提供了手动安装的步骤。 快速安装 Ollama的安装极为简单,只需在终端中执行以下命令࿱…...

Web---robots协议详解
在Web中,robots协议(也称为robots.txt)是一种文本文件,用于向搜索引擎机器人(通常称为爬虫)提供指导,以指示它们哪些页面可以抓取,哪些页面应该忽略。robots.txt文件位于网站的根目录…...

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第四套
华为海思校园招聘-芯片-数字 IC 方向 题目分享——第四套 (共9套,有答案和解析,答案非官方,仅供参考)(共九套,每套四十个选择题) 部分题目分享,完整版获取(WX:didadida…...

clipper一些数据结构(入门初识(一))
clipper一些数据结构(一) Clipper库是一个用于执行多边形裁剪(clipping)和偏移(offsetting)操作的开源C库。在Clipper库中,点和多边形(polygon)是基本的数据结构。Clipp…...

读《SQL基础教程 第二版 上》的一些总结
1. 数据库语言 DDL: Data Definition Language,数据定义语言(库、表的操作) DML: Data Manipulation Language, 数据操控语言(对表中数据的增删改) DQL: Data Query Language,数据库查询语言…...

EDI是什么:EDI系统功能介绍
EDI全称Electronic Data Interchange,中文名称是电子数据交换,也被称为“无纸化贸易”。EDI实现企业间(B2B)自动化通信,帮助贸易伙伴和组织完成更多的工作、加快物流时间并消除人为错误。 目前国内企业实现EDI通信大多…...

64B/66B GT Transceiver 配置
一、前言 前一篇文章已经讲述了64B/66B的编码原理,此篇文章来配置一下7系列GT的64B/66B编码。并讲述所对应的例子工程的架构,以及部分代码的含义。 二、IP核配置 1、打开7 Series FPGAs Transceiver Wizards,选择将共享逻辑放置在example …...

ES6: promise对象与回调地狱
ES6: promise对象与回调地狱 一、回调地狱二、Promise概述三、Promise的组成四、用函数封装Promise读取文件操作 一、回调地狱 在js中大量使用回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,所以希望一段程序按我们制定的顺序执…...

Qt事件处理机制2-事件函数的传播
所有继承自QObject的类都有event函数,该函数用来处理自身的事件,函数定义如下: virtual bool QObject::event(QEvent *e);Qt帮助文档: This virtual function receives events to an object and should return true i…...

【PDF.js】PDF文件预览
【PDF.js】PDF文件预览 一、PDF.js二、PDF.js 下载1、下载PDF.js2、在项目中引入3、屏蔽跨域错误 三、项目中使用四、说明五、实现效果 使用PDFJS实现pdf文件的预览,支持预览指定页、关键词搜索、缩略图、页面尺寸调整等等。 一、PDF.js 官方地址 文档地址 二、PD…...

从建表语句带你学习doris_表索引
1、doris建表概述 1.1、doris建表模板 CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [DATABASE.]table_name (column_definition1[,column_deinition2,......][,index_definition1,[,index_definition2,]] ) [ENGINE [olap|mysql|broker|hive]] [key_desc] [COMMENT "tabl…...

Linux CentOS 安装 MySQL 服务教程
Linux CentOS 安装 MySQL 服务教程 1. 查看系统和GNU C库(glibc)版本信息 1.1 查询机器 glibc 版本信息 glibc,全名GNU C Library,是大多数Linux发行版中使用的C库,为系统和应用程序提供核心的API接口。在Linux系统中,特别是在…...

MSSQL 命令行操作说明 sql server 2022 命令行下进行配置管理
说明:本文的内容是因为我在导入Access2019的 *.accdb 格式的数据时,总是出错的背景下,不得已搜索和整理了一下,如何用命令行进行sql server 数据库和用户管理的方法,作为从Access2019 直接导出数据到sql server 数据库…...

【系统分析师】系统安全分析与设计
文章目录 1、安全基础技术1.1 密码相关1.1.1对称加密1.1.2非对称加密1.1.3信息摘要1.1.4数字签名1.1.5数字信封 1.2 PKI公钥体系 2、信息系统安全2.1 保障层次2.2 网络安全2.2.1WIFI2.2.2 网络威胁与攻击2.2.3 安全保护等级 2.3计算机病毒与木马2.4安全防范体系 1、安全基础技术…...

ActiveMQ 07 集群配置
Active MQ 07 集群配置 官方文档 http://activemq.apache.org/clustering 主备集群 http://activemq.apache.org/masterslave.html Master Slave TypeRequirementsProsConsShared File System Master SlaveA shared file system such as a SANRun as many slaves as requ…...

Redis(哨兵模式)
什么是哨兵机制 问题: redis 主从复制模式下, 一旦主节点由于故障不能提供服务, 需要人工进行主从切换, 同时大量客户端需要被通知切换到新的主节点上, 对于有一定规模的应用来说, 对于人力的资源消耗会很大.解决: 通过哨兵对主从结构进行监控, 一旦出现主节点挂了的情况, 自动…...

一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制
简介 在嵌入式开发中,经常有需要用到RingBuffer的概念,在RingBuffer中经常遇到一个Buffer满和Buffer空的判断的问题,一般的做法是留一个单位的buffer不用,这样做最省事,但是当RingBuffer单位是一个结构体时࿰…...

【Java开发指南 | 第十一篇】Java运算符
读者可订阅专栏:Java开发指南 |【CSDN秋说】 文章目录 算术运算符关系运算符位运算符逻辑运算符赋值运算符条件运算符(?:)instanceof 运算符Java运算符优先级 Java运算符包括:算术运算符、关系运算符、位运算符、逻辑运算符、赋值…...

【IC前端虚拟项目】验证环境方案思路和文档组织
【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 对于mvu的验证环境,从功能角度就可以分析出需要搭建哪些部分,再看一下mvu的周围环境哈: 很明显验证环境必然要包括几个部分: 1.模拟idu发送指令; 2.模拟ram/ddr读写数据; 3.rm模拟mvu的行为; …...

程序设计|C语言教学——C语言基础1:C语言的引入和入门
一、程序的执行 1.定义 解释:借助一个程序,那个程序能够试图理解你的程序,然后按照你的要求执行。下次执行的时候还需要从零开始解释。 编译:借助一个程序,能够像翻译官一样,把你的程序翻译成机器语言&a…...