黑马spring高级49讲学习笔记
第一章 容器与Bean
第一讲 容器接口
-
BeanFactory 接口,典型功能有:
- getBean
-
ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:
- 国际化
- 通配符方式获取一组 Resource 资源
- 整合 Environment 环境(能通过它获取各种来源的配置信息)
- 事件发布与监听,实现组件之间的解耦
BeanFactory 与 ApplicationContext 的区别
测试代码:
@SpringBootApplication
public class A01Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
System.out.println(context);
}
}
1、到底什么是BeanFactory
-
它是
ApplicationContext的父接口查看
ConfigurableApplicationContext类图,可见BeanFactory是ApplicationContext的一个父接口。 -
它才是 Spring 的核心容器, 主要的
ApplicationContext实现都【组合】了它的功能,【组合】是指ApplicationContext的一个重要成员变量就是BeanFactory通过打印
context,发现返回的是ConfigurableApplicationContext类的具体实现AnnotationConfigServletWebServerApplicationContext,通过类图,找到该类间接继承的类GenericApplicationContext,可以发现beanFactory为该类的成员变量。
BeanFactory功能
BeanFactory接口定义:
通过debug,可以发现ConfigurableApplicationContext的getBeanFactory方法返回的是DefaultListableBeanFactory,打印该类的类图如下:
DefaultListableBeanFactory类的具体实现后面再分析,跳转到父类DefaultSingletonBeanRegistry,可以发现私有变量singletonObjects,它是一个Map结构,key为bean的名称,value是bean的实例对象。
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
通过反射输出该map的元素
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
.forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));
总结:
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
ApplicationContext功能
继续查看ConfigurableApplicationContext类图,可以看到ApplicationContext继承了EnvironmentCapable,MessageSource,ApplicationEventPublisher,ResourcePatternResolver四个接口,它的扩展功能主要体现在这四个接口上。
- MessageSource: 国际化功能,支持多种语言
- ResourcePatternResolver: 通配符匹配资源
- ApplicationEventPublisher: 发布事件对象
- EnvironmentCapable: 读取系统环境变量,比如
*.properties、*.application.yml等配置文件中的环境变量
1、MessageSource
演示:
在resources目录下创建四个文件messages.propertes、messages_en.properties、messages_ja.properties、messages_zh.properties,在messages.propertes文件写通用的翻译(可不写),在messages_en.properties、messages_ja.properties、messages_zh.properties文件分别填入hi=hello,hi=こんにちは,hi=你好。
执行代码:
System.out.println(context.getMessage("hi", null, Locale.CHINESE));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
打印结果:
你好
hello
こんにちは
2、ResourcePatternResolver
演示:
获取类路径下的资源文件
Resource[] resources = context.getResources("classpath:application.properties");
for (Resource resource : resources) {
System.out.println(resource);
}
打印结果:
class path resource [application.properties]
3、EnvironmentCapable
演示:
读取环境变量
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
打印结果:
D:\developer\java
8080
4、ApplicationEventPublisher
演示:
定义事件
public class UserRegisteredEvent extends ApplicationEvent {
public UserRegisteredEvent(Object source) {
super(source);
}
}
发布事件
@Autowired
private ApplicationEventPublisher context;
public void register() {
log.debug("用户注册");
context.publishEvent(new UserRegisteredEvent(this));
}
定义监听器
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
log.debug("发送短信");
}
第二讲 容器实现
BeanFactory实现
DefaultListableBeanFactory是BeanFactory重要的实现类,以该类为例进行学习
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class, scope, 初始化, 销毁)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
// 给 BeanFactory 添加一些常用的后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// BeanFactory 后处理器主要功能,补充一些 bean 定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(
beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
//Bean 后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource ...
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println(">>>>>>" + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
beanFactory.preInstantiateSingletons(); // 预加载所有单例
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// System.out.println(beanFactory.getBean(Bean1.class).getBean2());
System.out.println(beanFactory.getBean(Bean1.class).getInter());
/*
学到了什么:
a. beanFactory 不会做的事
1. 不会主动调用 BeanFactory 后处理器
2. 不会主动添加 Bean 后处理器
3. 不会主动初始化单例
4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
b. bean 后处理器会有排序的逻辑
*/
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public Bean3 bean3() {
return new Bean3();
}
@Bean
public Bean4 bean4() {
return new Bean4();
}
}
interface Inter {
}
static class Bean3 implements Inter {
}
static class Bean4 implements Inter {
}
static class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
@Autowired
@Resource(name = "bean4")
private Inter bean3;
public Inter getInter() {
return bean3;
}
}
static class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("构造 Bean2()");
}
}
}
原始的BeanFactory并没有解析@Configuration、@Bean等注解的功能,需要添加BeanFactory后处理器并运行postProcessBeanFactory方法来实现扩展功能,以及添加Bean后处理器,来针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource...
总结:
a. beanFactory 不会做的事
1. 不会主动调用 BeanFactory 后处理器
2. 不会主动添加 Bean 后处理器
3. 不会主动初始化单例
4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
b. bean 后处理器会有排序的逻辑
order越小,处理器优先级越高
ApplicationContext实现
1、ClassPathXmlApplicationContext,基于 classpath 下 xml 格式的配置文件来创建;
// 较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
2、FileSystemXmlApplicationContext,基于磁盘路径下 xml 格式的配置文件来创建;
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext(
"src\main\resources\a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
3、AnnotationConfigApplicationContext,基于 java 配置类来创建
// 较为经典的容器, 基于 java 配置类来创建
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());
}
4、AnnotationConfigServletWebServerApplicationContext,基于 java 配置类来创建, 用于 web 环境;
// 较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
配置类案例:
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
第三讲 Bean的生命周期
Spring bean 生命周期各个阶段
测试类代码:
@Component
public class LifeCycleBean {
private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);
public LifeCycleBean() {
log.debug("构造");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home){
log.debug("依赖注入:{}",home);
}
@PostConstruct
public void init(){
log.debug("初始化");
}
@PreDestroy
public void destory(){
log.debug("销毁");
}
}
运行SpringBoot启动类,从打印日志可以得到Spring bean的生命周期:
- 实例化
- 依赖注入
- 初始化
- 销毁
前面讲到Bean后处理器是针对bean的生命周期的各个阶段提供扩展功能;
测试代码:
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
// return false;
}
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
return pvs;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
return bean;
}
}
打印结果:
模板方法
Bean后处理器的功能是通过模板方法模式来对bean的生命周期进行扩展。
这里通过一个简单样例来演示Bean后处理器对依赖注入阶段的扩展:
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
beanFactory.getBean();
}
// 模板方法 Template Method Pattern
static class MyBeanFactory {
public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean); // @Autowired, @Resource
for (BeanPostProcessor processor : processors) {
processor.inject(bean);
}
System.out.println("初始化 " + bean);
return bean;
}
private List<BeanPostProcessor> processors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor processor) {
processors.add(processor);
}
}
static interface BeanPostProcessor { public void inject(Object bean); // 对依赖注入阶段的扩展
}
第四讲 Bean后处理器
- Bean后处理器的作用:为Bean生命周期各个阶段提供扩展
常见bean后处理器
测试类代码
public static void main(String[] args) {
// GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
// 用原始方法注册三个 bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
// 初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
// 销毁容器
context.close();
}
Bean1:
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
private Bean2 bean2;
@Autowired
public void setBean2(Bean2 bean2) {
log.debug("@Autowired 生效: {}", bean2);
this.bean2 = bean2;
}
// @Autowired
private Bean3 bean3;
@Resource
public void setBean3(Bean3 bean3) {
log.debug("@Resource 生效: {}", bean3);
this.bean3 = bean3;
}
private String home;
@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.debug("@Value 生效: {}", home);
this.home = home;
}
@PostConstruct
public void init() {
log.debug("@PostConstruct 生效");
}
@PreDestroy
public void destroy() {
log.debug("@PreDestroy 生效");
}
@Override
public String toString() {
return "Bean1{" +
"bean2=" + bean2 +
", bean3=" + bean3 +
", home='" + home + ''' +
'}';
}
}
Bean2和Bean3没有添加任何属性和方法;
启动main方法,控制台没有打印Bean1的日志信息,说明注解没有生效;
1、注册AutowiredAnnotationBeanPostProcessor后处理器
AutowiredAnnotationBeanPostProcessor后处理器用于解析@Autowired、@Value注解;
直接启动后会报错,因为默认解析器不能解析值注入,需要设置为ContextAnnotationAutowireCandidateResolver;
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
2、注册CommonAnnotationBeanPostProcessor后处理器
CommonAnnotationBeanPostProcessor后处理器用于解析@Resource、@PostConstruct、@PreDestroy注解
context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy
3、注册ConfigurationPropertiesBindingPostProcessor后处理器
注册ConfigurationPropertiesBindingPostProcessor后处理器用于解析@ConfigurationProperties注解;
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
@ConfigurationProperties(prefix = "java")
public class Bean4 {
private String home;
private String version;
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return "Bean4{" +
"home='" + home + ''' +
", version='" + version + ''' +
'}';
}
}
AutowiredAnnotationBeanPostProcessor运行分析
测试代码:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器
// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Bean1 bean1 = new Bean1();
System.out.println(bean1);
processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
System.out.println(bean1);
上述代码使用AutowiredAnnotationBeanPostProcessor后处理器对Bean1中的注解进行解析,完成依赖注入;
postProcessProperties方法:
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
通过解读源码发现,在postProcessProperties方法中,先执行了findAutowiringMetadata私有方法,该方法用于获取当前bean内所有加了@Value和@Autowired的成员变量以及方法参数信息,返回InjectionMetadata对象。然后调用该类的inject方法来进行依赖注入。
可以通过反射调用查看依赖注入的执行情况:
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
System.out.println(metadata);
// 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);
在inject方法内部,是如何按照类型查找对应的值?
下述代码演示了在执行inject方法时,内部根据属性类型和方法参数类型如何获取对应值的方式:
// 3. 如何按类型查找值
Field bean3 = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o);
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 =
new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o1);
Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o2);
第五讲 常见工厂后处理器
演示代码:未注册工厂后处理器
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
Config类:
@Configuration
@ComponentScan("com.itheima.a05.component")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
1、ConfigurationClassPostProcessor工厂后处理器
该处理器用于解析@ComponentScan、@Bean、@Import、@ImportResource注解
演示代码:
context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
2、MapperScannerConfigurer工厂后处理器
该处理器用于Spring整合Mybatis时,解析@MapperScanner注解
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
});
下面将分析上述两个Bean工厂后处理器是如何解析注解,生成BeanDefinition,以及注册到BeanFactory的;
1、模拟实现-组件扫描
第一步:根据@ComponentScan扫描路径,查找类文件,找到哪些类加了@Component或派生的注解;
第二步:对注解了@Comonent或其派生注解的类,创建BeanDefinition,并注册到BeanFactory;
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
String path = "classpath*:" + p.replace(".", "/") + "/**/*";
System.out.println(path);
//根据classpath路径获取资源文件
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
// System.out.println(resource);
//分析class文件元数据
MetadataReader reader = factory.getMetadataReader(resource);
System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
System.out.println("是否加了@Component注解:" + annotationMetadata.hasAnnotation(Component.class.getName()));
System.out.println("是否加了@Component派生注解:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
//找到注解了Component或者派生注解的类,创建BeanDefinition
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
//生成bean name
if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
String beanName = generator.generateBeanName(beanDefinition, beanFactory);
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、模拟实现—@Bean
第一步:获取类的元数据信息,找到指定类中注解了@Bean的方法;
第二步:使用BeanDefinitionBuilder生成BeanDefinition,设置工厂方法(factoryMethod)和工厂Bean(factoryBean)、自动装载模式;
public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
//获取类的元数据信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
//获取方法信息
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
System.out.println(method);
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、模拟实现—@Mapper
第一步:根据指定资源路径,获取所有Mapper接口;
第二步:遍历Mappper接口,获取接口元数据信息,生成MapperFactoryBean的BeanDefinition,并注册到bean容器;
注:FactoryBean是一种特殊的bean,它是个工厂bean,通过实现getObject方法可以创建自定义的bean实例,而Mapper接口生成的bean需要通过MapperFactoryBean工厂bean去创建;
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/itheima/a05/mapper/**/*.class");
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
if (classMetadata.isInterface()) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
AbstractBeanDefinition beanDefinition2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName())
.getBeanDefinition();
String beanName = generator.generateBeanName(beanDefinition2, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
第六讲 Aware接口
Aware接口和InitializationBean接口
Aware接口的作用:
用于注入一些与容器相关的信息,例如
BeanNameAware注入bean的名字BeanFactoryAware注入BeanFactory容器ApplicationContextAware注入ApplicationContext容器EmbeddedValueResolverAware${}
InitializationBean接口的作用:
提供接口方法afterPropertiesSet,在bean初始化时执行;
演示代码:
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(MyBean.class);
@Override
public void setBeanName(String name) {
log.debug("当前bean " + this + " 名字叫:" + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean " + this + " 容器是:" + applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("当前bean " + this + " 初始化");
}
@Autowired
public void aaa(ApplicationContext applicationContext) {
log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);
}
@PostConstruct
public void init() {
log.debug("当前bean " + this + " 使用@PostConstruct 初始化");
}
}
问题:为什么可以使用@Autowired注解和@PostContruct注解,还要使用Aware接口?
a. @Autowired注解的解析需要用到bean后处理器, 属于扩展功能
b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别
某些情况下, 扩展功能会失效, 而内置功能不会失效
@Autowire失效分析
在Java配置类在添加了bean工厂后处理器后,你会发现用传统接口方式的注入和初始化仍然成功, 而@Autowired和@PostConstruct的注入和初始化失败;
演示代码:
@Configuration
public class MyConfig1 {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.debug("注入 ApplicationContext");
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@Bean // beanFactory 后处理器
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.debug("执行 processor1");
};
}
}
运行后可以看到,@Autowired和@PostConstruct注解的方法没有执行;
原因分析:
通过阅读分析ApplicationContext的refresh方法,其主要流程为:
sequenceDiagram
participant ac as ApplicationContext
participant bfpp as BeanFactoryPostProcessor
participant bpp as BeanPostProcessor
participant config as Java配置类
ac ->> bfpp : 1. 执行 BeanFactoryPostProcessor
ac ->> bpp : 2. 注册 BeanPostProcessor
ac ->> +config : 3. 创建和初始化
bpp ->> config : 3.1 依赖注入扩展(如 @Value 和 @Autowired)
bpp ->> config : 3.2 初始化扩展(如 @PostConstruct)
ac ->> config : 3.3 执行 Aware 及 InitializingBean
config -->> -ac : 3.4 创建成功
当Java配置类包含BeanFactoryPostProcessor的时候,要创建其中的BeanFactoryPostProcessor必须提前创建Java配置类,而此时的BeanPostProcessor还未准备好,导致@Autowired等注解失效;
sequenceDiagram
participant ac as ApplicationContext
participant bfpp as BeanFactoryPostProcessor
participant bpp as BeanPostProcessor
participant config as Java配置类
ac ->> +config : 3. 创建和初始化
ac ->> config : 3.1 执行 Aware 及 InitializingBean
config -->> -ac : 3.2 创建成功
ac ->> bfpp : 1. 执行 BeanFactoryPostProcessor
ac ->> bpp : 2. 注册 BeanPostProcessor
解决方法:
- 用内置依赖注入和初始化取代扩展依赖注入和初始化
- 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建
总结:
a. Aware接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory,ApplicationContext
b. InitializingBean 接口提供了一种【内置】的初始化手段
c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们
第七讲 初始化与销毁
Spring框架提供了多种初始化和销毁手段
初始化方式:(执行顺序1>2>3)
- 使用
@PostConstruct注解方法; - 继承
InitializingBean接口,实现afterPropertiesSet方法; - 使用
@Bean(initMethod)指定的初始化方法
销毁方式:(执行顺序1>2>3)
- 使用
@PreDestroy注解方法; - 继承
DisposableBean接口,实现destory方法; @Bean(destroyMethod)指定的销毁方法;
第八讲 Scope
在当前版本的 Spring 和 Spring Boot 程序中,支持五种Scope
- singleton,容器启动时创建(未设置延迟),容器关闭时销毁
- prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
- request,每次请求用到此 bean 时创建,请求结束时销毁
- session,每个会话用到此 bean 时创建,会话结束时销毁
- application,web 容器用到此 bean 时创建,容器停止时销毁
Scope失效解决 —— singleton注入其它scope失效
问题演示:singleton单例bean注入prototype原型bean
单例bean:E
@Component
public class E {
@Autowired
private F1 f1;
public F1 getF1() {
return f1;
}
}
原型bean:F1
@Scope("prototype")
@Component
public class F1 {
}
测试:
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(A08_1.class);
E e = context.getBean(E.class);
log.debug("{}", e.getF1().getClass());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
输出:
- class com.itheima.a08.sub.F1
- com.itheima.a08.sub.F1@2898ac89
- com.itheima.a08.sub.F1@2898ac89
通过打印的f1对象发现,返回的是同一个对象,@Scope("prototype")没有生效;
原因分析:
对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F
graph LR
e1(e 创建)
e2(e set 注入 f)
f1(f 创建)
e1-->f1-->e2
解决方式:
1、使用@Lazy注解生成代理
@Lazy
@Autowired
private F1 f1;
输出日志:
- class com.itheima.a08.sub.F1$$EnhancerBySpringCGLIB$$21b324fd
- com.itheima.a08.sub.F1@7748410a
- com.itheima.a08.sub.F1@37f1104d
从输出日志可以看到,使用@Lazy后生成的是代理对象,代理对象虽然还是同一个,但当每次使用代理对象的任意方法时(如本例隐含调用了toString方法),才会从容器中查找 f 对象
graph LR
e1(e 创建)
e2(e set 注入 f代理)
f1(f 创建)
f2(f 创建)
f3(f 创建)
e1-->e2
e2--使用f方法-->f1
e2--使用f方法-->f2
e2--使用f方法-->f3
2、使用@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
输出日志:
- class com.itheima.a08.sub.F2$$EnhancerBySpringCGLIB$$a7e65ca8
- com.itheima.a08.sub.F2@574b560f
- com.itheima.a08.sub.F2@ba54932
由输出结果可知,同样是使用代理方式解决
3、使用ObjectFactory
@Autowired
private ObjectFactory<F3> f3;
4、使用ApplicationContext
@Autowired
private ApplicationContext context;
public F4 getF4() {
return context.getBean(F4.class);
}