上海浦东做网站的公司,免费网站商城模板,2023年最建议买的手机,微信公众号接口开发BeanFactory和ApplicationContext
什么是BeanFactory
它是ApplicationContext的父接口它才是Spring的核心容器#xff0c;主要的ApplicationContext实现都组合了它的功能 BeanFactory能做什么?
表面上看BeanFactory的主要方法只有getBean()#xff0c;实际上控制反转、基…BeanFactory和ApplicationContext
什么是BeanFactory
它是ApplicationContext的父接口它才是Spring的核心容器主要的ApplicationContext实现都组合了它的功能 BeanFactory能做什么?
表面上看BeanFactory的主要方法只有getBean()实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能都由他的实现类提供
例如通过反射查看它的成员变量singletonObjectssingletonObjects内部包含了所有单例bean
实现这个例子前引出一个类DefaultListableBeanFactory主要功能是作为一个可配置的、可列表化的 Bean 工厂用于管理和维护应用程序中的 Bean 定义和实例
DefaultListableBeanFactory继承了DefaultSingletonBeanRegistry类而DefaultSingletonBeanRegistry类就是专门用来管理单例Bean的 DefaultSingletonBeanRegistry其中有个成员变量也就是singletonObjects 这样我们就找到了单例Bean存储的地方通过反射调用即可 SpringBoot启动会有很多单例Bean注入map中包含了所有的单例Bean我自定义了Component1类注入到其中 SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {//返回容器对象ConfigurableApplicationContext context SpringApplication.run(Application.class, args);//反射获取singletonObjects属性Field singletonObjects DefaultSingletonBeanRegistry.class.getDeclaredField(singletonObjects);//开启可操作singletonObjects否则无法对他进行操作因为他是private final的singletonObjects.setAccessible(true);//BeanFactory是ConfigurableListableBeanFactory实现类的成员变量ConfigurableListableBeanFactory beanFactory context.getBeanFactory();//Field.get(Object obj)获取指定对象obj上此field表示的字段的值MapString,Object map (MapString, Object) singletonObjects.get(beanFactory);map.entrySet().stream().filter(e - e.getKey().startsWith(component)).forEach(e - {System.out.println(e.getKey() e.getValue());});}
}上面代码最难理解的是singletonObjects.get(beanFactory)刚开始没有理解到反射重新翻了反射的代码发现他的作用是填充ConfigurableListableBeanFactory中的singletonObjects属性
Java源码中并没有这个属性反复查找后发现DefaultListableBeanFactory是ConfigurableListableBeanFactory的实现类而DefaultListableBeanFactory继承了DefaultSingletonBeanRegistry所以自然有singletonObjects属性
ApplicationContext能做什么
前面提到过ApplicationContext是在BeanFactory基础上做了扩展
MessageSource拓展国际化功能ApplicationEventPublisher事件发布与监听实现组件之间的解耦ResourcePatternResolver通过通配符方式获取一组Resource资源EnvironmentCapable整合 Environment 环境能通过它获取各种来源的配置信息 国际化
所谓国际化就是可以通过准备好的配置文件将语言翻译成各种国家的语言
#准备以下2个配置文件
#messages_en.properties 翻译成英文
hihello#messages_zh.properties 翻译成中文 \u4F60\u597D对应着你好
hi\u4F60\u597D通过getMessage()转化
SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context SpringApplication.run(Application.class, args);//国际化System.out.println(context.getMessage(hi, null, Locale.US)); //helloSystem.out.println(context.getMessage(hi, null, Locale.CHINA)); //你好}
}获取Resource资源
SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context SpringApplication.run(Application.class, args);//通配符方式获取一组 Resource 资源Resource[] resources context.getResources(classpath:application.properties);for (Resource resource : resources) {System.out.println(resource); //class path resource [application.properties]}}
}整合 Environment 环境
可以读取系统环境变量也可以读取SpringBoot配置文件
SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context SpringApplication.run(Application.class, args);//整合 Environment 环境System.out.println(context.getEnvironment().getProperty(JAVA_HOME));System.out.println(context.getEnvironment().getProperty(server.port));}
}事件发布与监听
事件发布与监听指的是一条线程发布特定事件监听该事件的方法收到消息后处理该方法这也是异步的一种思想
创建事件
事件需要继承ApplicationEvent
public class UserRegisteredEvent extends ApplicationEvent {public UserRegisteredEvent(Object source) {super(source);}
}监听者
EventListener 可以在 Spring 应用中监听和响应特定类型的事件。当事件发生时标记有 EventListener 注解的方法将被自动调用。
Component
public class Component1 {private static final Logger logger LoggerFactory.getLogger(Component1.class);EventListenerpublic void aaa(UserRegisteredEvent event) {logger.info({},event);}
}事件发布
SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context SpringApplication.run(Application.class, args);//发布事件context.publishEvent(new UserRegisteredEvent(context));}
}BeanFactory实现
代码准备
我们最终想要实现的是像Spring一样向自定义的BeanFactory注册Config对象
Configuration
static class Config{Beanpublic Bean1 bean1(){return new Bean1();}Beanpublic Bean2 bean2(){return new Bean2();}
}static class Bean1{private static final Logger logger LoggerFactory.getLogger(Bean1.class);Autowiredprivate Bean2 bean2;public Bean1() {logger.info(构造 Bean1());}public Bean2 getBean2(){return bean2;}
}static class Bean2{private static final Logger logger LoggerFactory.getLogger(Bean2.class);public Bean2() {logger.info(构造 Bean2());}
}具体实现
BeanFactory可以通过registerBeanDefinition注册一个BeanDefinition 对象
public static void main(String[] args) {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();//定义一个bean (classscope初始化销毁) scope:单例还是多例AbstractBeanDefinition beanDefinition BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope(singleton).getBeanDefinition();beanFactory.registerBeanDefinition(config,beanDefinition);//查看BeanFactory中的beanfor (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}
}执行以上代码控制台输出config证明Config已经注册到BeanFactory中
但是发现Bean1Bean2并没有注册进去因为BeanFactory并不会主动调用BeanFactory的后置处理器
现在我们需要为BeanFactory添加一些常用的后置处理器注意这里仅仅是添加
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);通过观察控制台输出发现多了一些BeanFactory中多了些Bean 简单介绍一下目前需要了解几个关键的处理器
internalConfigurationAnnotationProcessor用于处理Configuration注解它会解析Configuration注解并处理其中的Bean注解将被注解的方法返回的对象注册为Bean。internalAutowiredAnnotationProcessor用于处理Autowired注解它会解析Autowired注解并自动装配依赖对象。它会在Bean初始化之前进行处理确保依赖对象正确注入。internalCommonAnnotationProcessor用于处理通用注解如ResourcePostConstructPreDestroy等。它会在Bean初始化之前进行处理执行相应的初始化和销毁方法。
接下来需要让BeanFactory处理器生效其中internalConfigurationAnnotationProcessor属于BeanFactory后置处理器internalAutowiredAnnotationProcessorinternalCommonAnnotationProcessor属于Bean的后置处理器生效方式不一样
//BeanFactory后置处理器的主要功能补充了一些bean的定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor - {beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});//bean后置处理器针对bean的生命周期的各个阶段提供扩展例如Autowird,Resource
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor - {beanFactory.addBeanPostProcessor(beanPostProcessor);
});观察控制台发现Bean1Bean2也被出册到了BeanFactory中并且可以实例化 目前到这看似我们的目标已经完成了实际上会发现Bean的构造方法是在用到的时候调用的属于懒汉式对于单例Bean来说希望他在注册的时候就已经实例化
//在容器初始化完成后提前实例化所有的单例Bean
beanFactory.preInstantiateSingletons();观察控制台发现构造方法在容器初始化的时候就已经执行了 总结
BeanFactory不会主动调用BeanFactory后置处理器BeanFactory不会主动调用Bean后置处理器BeanFactory不会主动初始化单例
补充说明
Bean的后置处理器会有排序的逻辑
举个例子当一个接口有两个实现类时这个接口通过依赖注入的形式注入同时标注了Autowired以及Resource哪个注解会生效取决于Bean的哪个后置处理器先添加到BeanFactory中
ApplicationContext实现
这里我们主要了解几个常用的ApplicationContext实现
ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContextAnnotationConfigServletWebServerApplicationContext
基于XML配置文件注册Bean
目前无论是Spring还是SpringBoot都很少用XML的方式更推荐使用配置类来注册Bean所以这里了解即可
前置代码
需要注册的Bean
static class Bean1{
}static class Bean2{private Bean1 bean1;public Bean1 getBean1() {return bean1;}public void setBean1(Bean1 bean1) {this.bean1 bean1;}
}读取的XML配置文件
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean idbean1 classcom.yellowstar.spring.demo1.TestApplicationContext$Bean1/bean idbean2 classcom.yellowstar.spring.demo1.TestApplicationContext$Bean2property namebean1 refbean1//bean
/beansClassPathXmlApplicationContext
ClassPathXmlApplicationContext的作用是基于classpath下 xml 格式的配置文件来注册Bean
private static void testClassPathXmlApplicationContext(){ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(b01.xml);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(context.getBean(Bean2.class).getBean1());
}FileSystemXmlApplicationContext
FileSystemXmlApplicationContext的作用是基于磁盘路径下 xml 格式的配置文件来创建 private static void testFileSystemXmlApplicationContext(){//可以选择绝对路径也可以是相对路径FileSystemXmlApplicationContext context new FileSystemXmlApplicationContext(src/main/resources/b01.xml);}实现ClassPathXmlApplicationContext
XmlBeanDefinitionReader用于从XML配置文件中读取和解析Bean的定义信息 DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();//设置配置文件读取到beanFactoryXmlBeanDefinitionReader xmlBeanDefinitionReader new XmlBeanDefinitionReader(beanFactory);xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource(b01.xml));基于Java配置类注册Bean
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext的作用是基于Java配置类来注册Bean
配置类
Configuration
static class Config{Beanpublic Bean1 bean1(){return new Bean1();}Beanpublic Bean2 bean2(Bean1 bean1){Bean2 bean2 new Bean2();bean2.setBean1(bean1);return bean2;}
}实现
private static void testAnnotationConfigApplicationContext(){AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(Config.class);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println( context.getBean(Bean2.class).getBean1());
}这里可以注意到常用的一些后置处理器是自动帮我们加载到容器中了而基于XML模式的方式还需要在XML另外定义才行 AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext也是是基于Java配置类来注册Bean区别不同的在于他适用于web环境
这块内容比较复杂先贴代码在详细解释 private static void testAnnotationConfigServletWebServerApplicationContext(){AnnotationConfigServletWebServerApplicationContext context new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);}Configurationstatic class WebConfig{Beanpublic ServletWebServerFactory servletWebServerFactory(){return new TomcatServletWebServerFactory();}Beanpublic DispatcherServlet dispatcherServlet(){return new DispatcherServlet();}Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){// / 代表匹配所有请求return new DispatcherServletRegistrationBean(dispatcherServlet,/);}Bean(/)public Controller controller1(){return (request,response) - {response.getWriter().print(hello);return null;};}}上述代码的功能就像是启动了Spring服务一样可以通过访问localhost:8080来进入controller1 其中最重要的是WebConfig必须拥有三大组件
ServletWebServer用于支持 Servlet 容器的接口。它定义了与 Servlet 容器相关的通用操作和属性允许 Spring 应用程序与不同的 Servlet 容器如Tomcat、Jetty等进行交互。DispatcherServlet用于处理web请求的关键组件之一所有请求都会经过他DispatcherServletRegistrationBean用于注册和配置 DispatcherServlet 的 Bean。它允许开发者以编程方式配置 DispatcherServlet 的各种属性以及将其与特定的 URL 映射关联起来。
Bean的生命周期
通常情况下Bean的生命周期分为四个阶段
构造方法阶段在这个阶段Spring容器实例化Bean对象调用其构造方法来创建Bean的实例依赖注入阶段在Bean对象被实例化完成后Spring容器会对Bean的属性进行依赖注入将依赖的其他 Bean或值注入到当前 Bean中。初始化阶段在依赖注入完成后Spring容器会调用Bean的初始化方法例如使用PostConstruct注解销毁阶段当容器关闭或销毁时Spring容器会调用Bean的销毁方法例如使用PreDestory注解
这样讲讲方法太枯燥了我一开始也记不住通过下面例子可以加深印象
SpringBootApplication
public class A03Application {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(A03Application.class, args);context.close();}
}Component
public class LifeCycleBean {private static final Logger logger LoggerFactory.getLogger(LifeCycleBean.class);public LifeCycleBean() {logger.debug(构造);}Autowiredpublic void autowire(Value(${JAVA_HOME}) String home){logger.debug(依赖注入:{},home);}PostConstructpublic void init(){logger.debug(初始化);}PreDestroypublic void destory(){logger.debug(销毁);}
}执行代码可以观察到顺序依次为构造-依赖注入-初始化-销毁 Bean扩展
Spring的扩展程度是非常高的可以在Bean的各个生命周期中实现自己想要的结果
Component
public class MyBeanPostProcessor implements DestructionAwareBeanPostProcessor, InstantiationAwareBeanPostProcessor {private static final Logger log LoggerFactory.getLogger(MyBeanPostProcessor.class);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 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;}Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean)) {log.debug( 销毁之前执行, 如 PreDestroy);}}
}模版方法
模板方法是一种常用的设计模式在之前对BeanFactory添加后处理器时比较好奇他这个处理器是如何加载进去的现在使用模板方法可以试着自己动手实现
先来看一下基础代码MyBeanFactory是自定义的一个BeanFactory其中getBean()可以获取Bean
我们的想法是在getBean()时添加后处理器来解析Autowired或者Resource public static void main(String[] args) {MyBeanFactory beanFactory new MyBeanFactory();beanFactory.getBean();}static class MyBeanFactory{public Object getBean(){Object bean new Object();//依赖注入解析return bean;}}方案一
比较简单的方法就是直接在getBean()中实现但是这样并不利于扩展假如需要后续解析其他注解那么就得修改getBean()方法
方案二
参照之前DefaultListableBeanFactory的做法我们并没有修改内部方法而是调用DefaultListableBeanFactory其他方法来添加后处理器
通过一个集合来加载所有后处理器这样在需要添加后处理器的时候就可以在外部直接调用addBeanPostProcessor()方法不需要动到MyBeanFactory这个最底层的类 public static void main(String[] args) {MyBeanFactory beanFactory new MyBeanFactory();beanFactory.addBeanPostProcessor(() - System.out.println(解析 Autowired));beanFactory.addBeanPostProcessor(() - System.out.println(解析 Resource));beanFactory.getBean();}static class MyBeanFactory{public Object getBean(){Object bean new Object();//执行后置处理器for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {beanPostProcessor.inject();}return bean;}private static ListBeanPostProcessor beanPostProcessorList new ArrayList();public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){beanPostProcessorList.add(beanPostProcessor);}}interface BeanPostProcessor{/*** 依赖注入阶段扩展*/void inject();}