spring高级源码50讲-1-8(spring容器与bean)
文章目录
- 容器与 bean
- 1) 容器接口
- 演示1 - BeanFactory 与 ApplicationContext 的区别
- 关键代码参考
- 收获💡
- 演示2 - 国际化
- 2) 容器实现
- 演示1 - DefaultListableBeanFactory
- 代码参考
- 收获💡
- 演示2 - 常见 ApplicationContext 实现
- 代码参考
- 收获💡
- 3) Bean 的生命周期
- 演示1 - bean 生命周期
- 代码参考
- 收获💡
- 演示2 - 模板方法设计模式
- 关键代码
- 演示3 - bean 后处理器排序
- 代码参考
- 收获💡
- 4) Bean 后处理器
- 演示1 - 后处理器作用
- 代码参考
- 收获💡
- 演示2 - @Autowired bean 后处理器运行分析
- 代码参考
- 收获💡
- 5) BeanFactory 后处理器
- 演示1 - BeanFactory 后处理器的作用
- 代码参考
- 收获💡
- 演示2 - 模拟解析 @ComponentScan
- 代码参考
- 收获💡
- 演示3 - 模拟解析 @Bean
- 代码参考
- 收获💡
- 演示4 - 模拟解析 Mapper 接口
- 代码参考
- 收获💡
- 6) Aware 接口
- 演示 - Aware 接口及 InitializingBean 接口
- 代码参考
- 收获💡
- 配置类 @Autowired 失效分析
- 7) 初始化与销毁
- 演示 - 初始化销毁顺序
- 代码参考
- 收获💡
- 8) Scope
- 演示1 - request, session, application 作用域
- 代码参考
- 收获💡
- 分析 - singleton 注入其它 scope 失效
- 演示2 - 4种解决方法
- 代码参考
- 收获💡
容器与 bean
1) 容器接口
-
BeanFactory 接口,典型功能有:
- getBean
-
ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:
- 国际化
- 通配符方式获取一组 Resource 资源
- 整合 Environment 环境(能通过它获取各种来源的配置信息)
- 事件发布与监听,实现组件之间的解耦
可以看到,我们课上讲的,都是 BeanFactory 提供的基本功能,ApplicationContext 中的扩展功能都没有用到。
演示1 - BeanFactory 与 ApplicationContext 的区别
关键代码参考
package com.itheima.a01;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Map;/*BeanFactory 与 ApplicationContext 的区别*/
@SpringBootApplication
public class A01 {private static final Logger log = LoggerFactory.getLogger(A01.class);public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);/*1. 到底什么是 BeanFactory- 它是 ApplicationContext 的父接口- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能*/System.out.println(context);/*2. BeanFactory 能干点啥- 表面上只有 getBean- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供*/Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");singletonObjects.setAccessible(true);ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();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());});/*3. ApplicationContext 比 BeanFactory 多点啥*/System.out.println(context.getMessage("hi", null, Locale.CHINA));System.out.println(context.getMessage("hi", null, Locale.ENGLISH));System.out.println(context.getMessage("hi", null, Locale.JAPANESE));Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");for (Resource resource : resources) {System.out.println(resource);}System.out.println(context.getEnvironment().getProperty("java_home"));System.out.println(context.getEnvironment().getProperty("server.port"));// context.publishEvent(new UserRegisteredEvent(context));context.getBean(Component1.class).register();/*4. 学到了什么a. BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能b. 又新学一种代码之间解耦途径练习:完成用户注册与发送短信之间的解耦, 用事件方式、和 AOP 方式分别实现*/}
}
package com.itheima.a01;import org.springframework.context.ApplicationEvent;public class UserRegisteredEvent extends ApplicationEvent {public UserRegisteredEvent(Object source) {super(source);}
}
package com.itheima.a01;import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);@Autowiredprivate ApplicationEventPublisher context;public void register() {log.debug("用户注册");context.publishEvent(new UserRegisteredEvent(this));}}
package com.itheima.a01;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);@EventListenerpublic void aaa(UserRegisteredEvent event) {log.debug("{}", event);log.debug("发送短信");}
}
收获💡
通过这个示例结合 debug 查看 ApplicationContext 对象的内部结构,学到:
-
到底什么是 BeanFactory
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
-
BeanFactory 能干点啥
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
- 例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
-
ApplicationContext 比 BeanFactory 多点啥
- ApplicationContext 组合并扩展了 BeanFactory 的功能
- 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
- 新学一种代码之间解耦途径,事件解耦
建议练习:完成用户注册与发送短信之间的解耦,用事件方式、和 AOP 方式分别实现
注意
- 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED,这是因为这些版本的 jdk 默认不允许跨 module 反射
- 事件发布还可以异步,这个视频中没有展示,请自行查阅 @EnableAsync,@Async 的用法
演示2 - 国际化
public class TestMessageSource {public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("messageSource", MessageSource.class, () -> {ResourceBundleMessageSource ms = new ResourceBundleMessageSource();ms.setDefaultEncoding("utf-8");ms.setBasename("messages");return ms;});context.refresh();System.out.println(context.getMessage("hi", null, Locale.ENGLISH));System.out.println(context.getMessage("hi", null, Locale.CHINESE));System.out.println(context.getMessage("hi", null, Locale.JAPANESE));}
}
国际化文件均在 src/resources 目录下
messages.properties(空)
messages_en.properties
hi=Hello
messages_ja.properties
hi=こんにちは
messages_zh.properties
hi=你好
注意
- ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
- 使用 SpringBoot 时,国际化文件名固定为 messages
- 空的 messages.properties 也必须存在
2) 容器实现
Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考
- DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转和依赖注入功能,都是它来实现
- ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
- FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
- XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
- AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
- AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
- AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
- AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)
另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来
演示1 - DefaultListableBeanFactory
代码参考
package com.itheima.a02;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;import javax.annotation.Resource;public class TestBeanFactory {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// bean 的定义(class, scope, 初始化, 销毁)AbstractBeanDefinition beanDefinition =BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();beanFactory.registerBeanDefinition("config", beanDefinition);// 给 BeanFactory 添加一些常用的后处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// BeanFactory 后处理器主要功能,补充了一些 bean 定义beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().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 后处理器会有排序的逻辑*/System.out.println("Common:" + (Ordered.LOWEST_PRECEDENCE - 3));System.out.println("Autowired:" + (Ordered.LOWEST_PRECEDENCE - 2));}@Configurationstatic class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2() {return new Bean2();}@Beanpublic Bean3 bean3() {return new Bean3();}@Beanpublic 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()");}@Autowiredprivate 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 可以通过 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 需要额外设置才能解析 ${} 与 #{}
演示2 - 常见 ApplicationContext 实现
代码参考
package com.itheima.a02;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;/*常见 ApplicationContext 实现*/
public class A02 {private static final Logger log = LoggerFactory.getLogger(A02.class);public static void main(String[] args) {testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
// testAnnotationConfigApplicationContext();
// testAnnotationConfigServletWebServerApplicationContext();/*DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();System.out.println("读取之前...");for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}System.out.println("读取之后...");XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\a02.xml"));for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}*//*学到了什么a. 常见的 ApplicationContext 容器实现b. 内嵌容器、DispatcherServlet 的创建方法、作用*/}// ⬇️较为经典的容器, 基于 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());}// ⬇️基于磁盘路径下 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());}// ⬇️较为经典的容器, 基于 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());}// ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境private static void testAnnotationConfigServletWebServerApplicationContext() {AnnotationConfigServletWebServerApplicationContext context =new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}}@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("/hello")public Controller controller1() {return (request, response) -> {response.getWriter().print("hello");return null;};}}@Configurationstatic class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2(Bean1 bean1) {Bean2 bean2 = new Bean2();bean2.setBean1(bean1);return bean2;}}static class Bean1 {}static class Bean2 {private Bean1 bean1;public void setBean1(Bean1 bean1) {this.bean1 = bean1;}public Bean1 getBean1() {return bean1;}}
}
收获💡
- 常见的 ApplicationContext 容器实现
- 内嵌容器、DispatcherServlet 的创建方法、作用
3) Bean 的生命周期
一个受 Spring 管理的 bean,生命周期主要阶段有
- 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
- 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
- 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
- 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
- prototype 对象也能够销毁,不过需要容器这边主动调用
一些资料会提到,生命周期中还有一类 bean 后处理器:BeanPostProcessor,会在 bean 的初始化的前后,提供一些扩展逻辑。但这种说法是不完整的,见下面的演示1
演示1 - bean 生命周期
代码参考
package com.itheima.a03;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;/*bean 的生命周期, 以及 bean 后处理器学到了什么a. Spring bean 生命周期各个阶段b. 模板设计模式, 大流程已经固定好了, 通过接口回调(bean 后处理器)扩展*/
@SpringBootApplication
public class A03 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);context.close();}
}
package com.itheima.a03;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@Component
public class LifeCycleBean {private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);public LifeCycleBean() {log.debug("构造");}@Autowiredpublic void autowire(@Value("${JAVA_HOME}") String home) {log.debug("依赖注入: {}", home);}@PostConstructpublic void init() {log.debug("初始化");}@PreDestroypublic void destroy() {log.debug("销毁");}
}
package com.itheima.a03;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;@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;}
}
创建前后的增强
- postProcessBeforeInstantiation
- 这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程
- postProcessAfterInstantiation
- 这里如果返回 false 会跳过依赖注入阶段
依赖注入前的增强
- postProcessProperties
- 如 @Autowired、@Value、@Resource
初始化前后的增强
- postProcessBeforeInitialization
- 这里返回的对象会替换掉原本的 bean
- 如 @PostConstruct、@ConfigurationProperties
- postProcessAfterInitialization
- 这里返回的对象会替换掉原本的 bean
- 如代理增强
销毁之前的增强
- postProcessBeforeDestruction
- 如 @PreDestroy
收获💡
- Spring bean 生命周期各个阶段
- 模板设计模式, 指大流程已经固定好了, 通过接口回调(bean 后处理器)在一些关键点前后提供扩展
演示2 - 模板方法设计模式
关键代码
public class TestMethodTemplate {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 Patternstatic class MyBeanFactory {public Object getBean() {Object bean = new Object();System.out.println("构造 " + bean);System.out.println("依赖注入 " + bean); // @Autowired, @Resourcefor (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); // 对依赖注入阶段的扩展}
}
演示3 - bean 后处理器排序
代码参考
package com.itheima.a03;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;import java.util.ArrayList;
import java.util.List;/*bean 后处理的的排序*/
public class TestProcessOrder {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);List<BeanPostProcessor> list = new ArrayList<>(List.of(new P1(), new P2(), new P3(), new P4(), new P5()));list.sort(beanFactory.getDependencyComparator());list.forEach(processor->{processor.postProcessBeforeInitialization(new Object(), "");});/*学到了什么1. 实现了 PriorityOrdered 接口的优先级最高2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序3. 其它的排在最后*/}@Order(1)static class P1 implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(P1.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization @Order(1)");return bean;}}@Order(2)static class P2 implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(P2.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization @Order(2)");return bean;}}static class P3 implements BeanPostProcessor, PriorityOrdered {private static final Logger log = LoggerFactory.getLogger(P3.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization PriorityOrdered");return bean;}@Overridepublic int getOrder() {return 100;}}static class P4 implements BeanPostProcessor, Ordered {private static final Logger log = LoggerFactory.getLogger(P4.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization Ordered");return bean;}@Overridepublic int getOrder() {return 0;}}static class P5 implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(P5.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization");return bean;}}
}
收获💡
- 实现了 PriorityOrdered 接口的优先级最高
- 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
- 其它的排在最后
4) Bean 后处理器
演示1 - 后处理器作用
代码参考
package com.itheima.a04;import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;/*bean 后处理器的作用*/
public class A04 {public static void main(String[] args) {// ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context = new GenericApplicationContext();// ⬇️用原始方法注册三个 beancontext.registerBean("bean1", Bean1.class);context.registerBean("bean2", Bean2.class);context.registerBean("bean3", Bean3.class);context.registerBean("bean4", Bean4.class);context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());//@Value解析器context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Valuecontext.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroyConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());//@ConfigurationProperties// ⬇️初始化容器context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例System.out.println(context.getBean(Bean1.class));// ⬇️销毁容器context.close();/*学到了什么a. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能b. 这些扩展功能由 bean 后处理器来完成*/}
}
package com.itheima.a04;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;public class Bean1 {private static final Logger log = LoggerFactory.getLogger(Bean1.class);private Bean2 bean2;@Autowiredpublic void setBean2(Bean2 bean2) {log.debug("@Autowired 生效: {}", bean2);this.bean2 = bean2;}@Autowiredprivate Bean3 bean3;@Resourcepublic void setBean3(Bean3 bean3) {log.debug("@Resource 生效: {}", bean3);this.bean3 = bean3;}private String home;@Autowiredpublic void setHome(@Value("${JAVA_HOME}") String home) {log.debug("@Value 生效: {}", home);this.home = home;}@PostConstructpublic void init() {log.debug("@PostConstruct 生效");}@PreDestroypublic void destroy() {log.debug("@PreDestroy 生效");}@Overridepublic String toString() {return "Bean1{" +"bean2=" + bean2 +", bean3=" + bean3 +", home='" + home + '\'' +'}';}
}
package com.itheima.a04;public class Bean2 {
}
package com.itheima.a04;public class Bean3 {
}
package com.itheima.a04;import org.springframework.boot.context.properties.ConfigurationProperties;/*java.home=java.version=*/
@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;}@Overridepublic String toString() {return "Bean4{" +"home='" + home + '\'' +", version='" + version + '\'' +'}';}
}
收获💡
- @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成
- 每个后处理器各自增强什么功能
- AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @Value
- CommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroy
- ConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties
- 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等
演示2 - @Autowired bean 后处理器运行分析
代码参考
package com.itheima.a04;import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.StandardEnvironment;import java.lang.reflect.Field;
import java.lang.reflect.Method;// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {public static void main(String[] args) throws Throwable {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化beanFactory.registerSingleton("bean3", new Bean3());beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @ValuebeanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadataAutowiredAnnotationBeanPostProcessor 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);// 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);// 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);}
}
收获💡
- AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionMetadata
- InjectionMetadata 可以完成依赖注入
- InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
- 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找
5) BeanFactory 后处理器
演示1 - BeanFactory 后处理器的作用
代码参考
package com.itheima.a05;import com.itheima.a05.mapper.Mapper1;
import com.itheima.a05.mapper.Mapper2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.GenericApplicationContext;import java.io.IOException;/*BeanFactory 后处理器的作用*/
public class A05 {private static final Logger log = LoggerFactory.getLogger(A05.class);public static void main(String[] args) throws IOException {// ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);
// context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
// context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
// bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
// });// context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScancontext.registerBean(AtBeanPostProcessor.class); // 解析 @Beancontext.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口// ⬇️初始化容器context.refresh();for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}Mapper1 mapper1 = context.getBean(Mapper1.class);Mapper2 mapper2 = context.getBean(Mapper2.class);// ⬇️销毁容器context.close();/*学到了什么a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义*/}
}
package com.itheima.a05;import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;@Configuration
@ComponentScan("com.itheima.a05.component")
public class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic 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/test");dataSource.setUsername("root");dataSource.setPassword("root");return dataSource;}/*@Beanpublic MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}@Beanpublic MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}*/
}
package com.itheima.a05;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Bean1 {private static final Logger log = LoggerFactory.getLogger(Bean1.class);public Bean1() {log.debug("我被 Spring 管理啦");}
}
- ConfigurationClassPostProcessor 可以解析
- @ComponentScan
- @Bean
- @Import
- @ImportResource
- MapperScannerConfigurer 可以解析
- Mapper 接口
收获💡
- @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
- 这些扩展功能由不同的 BeanFactory 后处理器来完成,其实主要就是补充了一些 bean 定义
演示2 - 模拟解析 @ComponentScan
代码参考
package com.itheima.a05;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;import java.io.IOException;public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {@Override // context.refreshpublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if (componentScan != null) {for (String p : componentScan.basePackages()) {System.out.println(p);// com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.classString path = "classpath*:" + p.replace(".", "/") + "/**/*.class";System.out.println(path);CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);AnnotationBeanNam eGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {// System.out.println(resource);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()));if (annotationMetadata.hasAnnotation(Component.class.getName())|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();String name = generator.generateBeanName(bd, beanFactory);beanFactory.registerBeanDefinition(name, bd);}}}}} catch (IOException e) {e.printStackTrace();}}
}
收获💡
- Spring 操作元数据的工具类 CachingMetadataReaderFactory
- 通过注解元数据(AnnotationMetadata)获取直接或间接标注的注解信息
- 通过类元数据(ClassMetadata)获取类名,AnnotationBeanNameGenerator 生成 bean 名
- 解析元数据是基于 ASM 技术
演示3 - 模拟解析 @Bean
代码参考
package com.itheima.a05;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;import java.io.IOException;
import java.util.Set;public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) 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 bd = builder.getBeanDefinition();beanFactory.registerBeanDefinition(method.getMethodName(), bd);}} catch (IOException e) {e.printStackTrace();}}
}
收获💡
- 进一步熟悉注解元数据(AnnotationMetadata)获取方法上注解信息
演示4 - 模拟解析 Mapper 接口
代码参考
package com.itheima.a05;import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;import java.io.IOException;public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource[] resources = resolver.getResources("classpath:com/itheima/a05/mapper/**/*.class");AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();for (Resource resource : resources) {MetadataReader reader = factory.getMetadataReader(resource);ClassMetadata classMetadata = reader.getClassMetadata();if (classMetadata.isInterface()) {AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).addConstructorArgValue(classMetadata.getClassName()).setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();String name = generator.generateBeanName(bd2, beanFactory);beanFactory.registerBeanDefinition(name, bd);}}} catch (IOException e) {e.printStackTrace();}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}
收获💡
- Mapper 接口被 Spring 管理的本质:实际是被作为 MapperFactoryBean 注册到容器中
- Spring 的诡异做法,根据接口生成的 BeanDefinition 仅为根据接口名生成 bean 名
6) Aware 接口
演示 - Aware 接口及 InitializingBean 接口
代码参考
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;/*Aware 接口及 InitializingBean 接口*/
public class A06 {private static final Logger log = LoggerFactory.getLogger(A06.class);public static void main(String[] args) {/*1. Aware 接口用于注入一些与容器相关信息, 例如a. BeanNameAware 注入 bean 的名字b. BeanFactoryAware 注入 BeanFactory 容器c. ApplicationContextAware 注入 ApplicationContext 容器d. EmbeddedValueResolverAware ${}*/GenericApplicationContext context = new GenericApplicationContext();
// context.registerBean("myBean", MyBean.class);
// context.registerBean("myConfig1", MyConfig1.class);//1、@Autowired失效context.registerBean("myConfig2", MyConfig2.class);//2、Aware未失效context.registerBean(AutowiredAnnotationBeanPostProcessor.class);context.registerBean(CommonAnnotationBeanPostProcessor.class);context.registerBean(ConfigurationClassPostProcessor.class);/*2. 有同学说: b、c、d 的功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢简单地说:a. @Autowired 的解析需要用到 bean 后处理器, 属于扩展功能b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别某些情况下, 扩展功能会失效, 而内置功能不会失效例1: 你会发现用 Aware 注入 ApplicationContext 成功, 而 @Autowired 注入 ApplicationContext 失败*//*例2: Java 配置类在添加了 bean 工厂后处理器后,你会发现用传统接口方式的注入和初始化仍然成功, 而 @Autowired 和 @PostConstruct 的注入和初始化失败*/context.refresh(); // 1. beanFactory 后处理器, 2. 添加 bean 后处理器, 3. 初始化单例context.close();/*学到了什么a. Aware 接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory, ApplicationContextb. InitializingBean 接口提供了一种【内置】的初始化手段c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们*/}
}
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;import javax.annotation.PostConstruct;public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {private static final Logger log = LoggerFactory.getLogger(MyBean.class);@Overridepublic void setBeanName(String name) {log.debug("当前bean " + this + " 名字叫:" + name);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.debug("当前bean " + this + " 容器是:" + applicationContext);}@Overridepublic void afterPropertiesSet() throws Exception {log.debug("当前bean " + this + " 初始化");}@Autowiredpublic void aaa(ApplicationContext applicationContext) {log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);}@PostConstructpublic void init() {log.debug("当前bean " + this + " 使用@PostConstruct 初始化");}
}
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;@Configuration
public class MyConfig1 {private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.debug("注入 ApplicationContext");}@PostConstructpublic void init() {log.debug("初始化");}@Bean // beanFactory 后处理器public BeanFactoryPostProcessor processor1() {return beanFactory -> {log.debug("执行 processor1");};}}
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {private static final Logger log = LoggerFactory.getLogger(MyConfig2.class);@Overridepublic void afterPropertiesSet() throws Exception {log.debug("初始化");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.debug("注入 ApplicationContext");}@Bean // beanFactory 后处理器public BeanFactoryPostProcessor processor2() {return beanFactory -> {log.debug("执行 processor2");};}
}
收获💡
- Aware 接口提供了一种【内置】 的注入手段,例如
- BeanNameAware 注入 bean 的名字
- BeanFactoryAware 注入 BeanFactory 容器
- ApplicationContextAware 注入 ApplicationContext 容器
- EmbeddedValueResolverAware 注入 ${} 解析器
- InitializingBean 接口提供了一种【内置】的初始化手段
- 对比
- 内置的注入和初始化不受扩展功能的影响,总会被执行
- 而扩展功能受某些情况影响可能会失效
- 因此 Spring 框架内部的类常用内置注入和初始化
配置类 @Autowired 失效分析
Java 配置类不包含 BeanFactoryPostProcessor 的情况
Java 配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效
对应代码
@Configuration
public class MyConfig1 {private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.debug("注入 ApplicationContext");}@PostConstructpublic void init() {log.debug("初始化");}@Bean // ⬅️ 注释或添加 beanFactory 后处理器对应上方两种情况public BeanFactoryPostProcessor processor1() {return beanFactory -> {log.debug("执行 processor1");};}}
注意
解决方法:
- 用内置依赖注入和初始化取代扩展依赖注入和初始化
- 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建
7) 初始化与销毁
演示 - 初始化销毁顺序
代码参考
package com.itheima.a07;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;/*初始化和销毁的执行顺序*/
@SpringBootApplication
public class A07_1 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(A07_1.class, args);context.close();/*学到了什么a. Spring 提供了多种初始化和销毁手段b. Spring 的面试有多么地卷*/}@Bean(initMethod = "init3")public Bean1 bean1() {return new Bean1();}@Bean(destroyMethod = "destroy3")public Bean2 bean2() {return new Bean2();}
}
package com.itheima.a07;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;public class Bean1 implements InitializingBean {private static final Logger log = LoggerFactory.getLogger(Bean1.class);@PostConstructpublic void init1() {log.debug("初始化1");}@Overridepublic void afterPropertiesSet() throws Exception {log.debug("初始化2");}public void init3() {log.debug("初始化3");}
}
package com.itheima.a07;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;import javax.annotation.PreDestroy;public class Bean2 implements DisposableBean {private static final Logger log = LoggerFactory.getLogger(Bean2.class);@PreDestroypublic void destroy1() {log.debug("销毁1");}@Overridepublic void destroy() throws Exception {log.debug("销毁2");}public void destroy3() {log.debug("销毁3");}
}
package com.itheima.a07;import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;public class A07_2 {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerBeanDefinition("myBean",BeanDefinitionBuilder.genericBeanDefinition(MyBean.class).setDestroyMethodName("destroy").getBeanDefinition());System.out.println(beanFactory.getBean(MyBean.class));beanFactory.destroySingletons(); // 销毁之后, 仍可创建新的单例System.out.println(beanFactory.getBean(MyBean.class));}static class MyBean {public MyBean() {System.out.println("MyBean()");}public void destroy() {System.out.println("destroy()");}}
}
收获💡
Spring 提供了多种初始化手段,除了课堂上讲的 @PostConstruct,@Bean(initMethod) 之外,还可以实现 InitializingBean 接口来进行初始化,如果同一个 bean 用了以上手段声明了 3 个初始化方法,那么它们的执行顺序是
- @PostConstruct 标注的初始化方法
- InitializingBean 接口的初始化方法
- @Bean(initMethod) 指定的初始化方法
与初始化类似,Spring 也提供了多种销毁手段,执行顺序为
- @PreDestroy 标注的销毁方法
- DisposableBean 接口的销毁方法
- @Bean(destroyMethod) 指定的销毁方法
8) Scope
在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope
- singleton,容器启动时创建(未设置延迟),容器关闭时销毁
- prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
- request,每次请求用到此 bean 时创建,请求结束时销毁
- session,每个会话用到此 bean 时创建,会话结束时销毁
- application,web 容器用到此 bean 时创建,容器停止时销毁
有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃
但要注意,如果在 singleton 注入其它 scope 都会有问题,解决方法有
- @Lazy
- @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
- ObjectFactory
- ApplicationContext.getBean
演示1 - request, session, application 作用域
代码参考
package com.itheima.a08;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*singleton, prototype, request, session, applicationjdk >= 9 如果反射调用 jdk 中方法jdk <= 8 不会有问题演示 request, session, application 作用域打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED*/
@SpringBootApplication
public class A08 {public static void main(String[] args) {SpringApplication.run(A08.class, args);/*学到了什么a. 有几种 scopeb. 在 singleton 中使用其它几种 scope 的方法c. 其它 scope 的销毁1. 可以将通过 server.servlet.session.timeout=10s 观察 session bean 的销毁2. ServletContextScope 销毁机制疑似实现有误*/}
}
package com.itheima.a08;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;@RestController
public class MyController {@Lazy@Autowiredprivate BeanForRequest beanForRequest;@Lazy@Autowiredprivate BeanForSession beanForSession;@Lazy@Autowiredprivate BeanForApplication beanForApplication;@GetMapping(value = "/test", produces = "text/html")public String test(HttpServletRequest request, HttpSession session) {ServletContext sc = request.getServletContext();String sb = "<ul>" +"<li>" + "request scope:" + beanForRequest + "</li>" +"<li>" + "session scope:" + beanForSession + "</li>" +"<li>" + "application scope:" + beanForApplication + "</li>" +"</ul>";return sb;}}
package com.itheima.a08;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;@Scope("request")
@Component
public class BeanForRequest {private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);@PreDestroypublic void destroy() {log.debug("destroy");}}
package com.itheima.a08;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;@Scope("session")
@Component
public class BeanForSession {private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}
package com.itheima.a08;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;@Scope("application")
@Component
public class BeanForApplication {private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}
- 打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
- 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
收获💡
- 有几种 scope
- 在 singleton 中使用其它几种 scope 的方法
- 其它 scope 的销毁时机
- 可以将通过 server.servlet.session.timeout=30s 观察 session bean 的销毁
- ServletContextScope 销毁机制疑似实现有误
分析 - singleton 注入其它 scope 失效
以单例注入多例为例
有一个单例对象 E
@Component
public class E {private static final Logger log = LoggerFactory.getLogger(E.class);private F f;public E() {log.info("E()");}@Autowiredpublic void setF(F f) {this.f = f;log.info("setF(F f) {}", f.getClass());}public F getF() {return f;}
}
要注入的对象 F 期望是多例
@Component
@Scope("prototype")
public class F {private static final Logger log = LoggerFactory.getLogger(F.class);public F() {log.info("F()");}
}
测试
E e = context.getBean(E.class);
F f1 = e.getF();
F f2 = e.getF();
System.out.println(f1);
System.out.println(f2);
输出
com.itheima.demo.cycle.F@6622fc65
com.itheima.demo.cycle.F@6622fc65
发现它们是同一个对象,而不是期望的多例对象
对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F
解决
- 仍然使用 @Lazy 生成代理
- 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的 f 对象
@Component
public class E {@Autowired@Lazypublic void setF(F f) {this.f = f;log.info("setF(F f) {}", f.getClass());}// ...
}
注意
- @Lazy 加在也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了
- @Autowired 加在 set 方法的目的类似
输出
E: setF(F f) class com.itheima.demo.cycle.F$$EnhancerBySpringCGLIB$$8b54f2bc
F: F()
com.itheima.demo.cycle.F@3a6f2de3
F: F()
com.itheima.demo.cycle.F@56303b57
从输出日志可以看到调用 setF 方法时,f 对象的类型是代理类型
演示2 - 4种解决方法
代码参考
package com.itheima.a08.sub;import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;@Component
public class E {@Lazy@Autowiredprivate F1 f1;@Autowiredprivate F2 f2;@Autowiredprivate ObjectFactory<F3> f3;@Autowiredprivate ApplicationContext context;public F1 getF1() {return f1;}public F2 getF2() {return f2;}public F3 getF3() {return f3.getObject();}public F4 getF4() {return context.getBean(F4.class);}
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;@Scope("prototype")
@Component
public class F1 {
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;@Scope("prototype")
@Component
public class F3 {
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;@Scope("prototype")
@Component
public class F4 {
}
- 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
收获💡
- 单例注入其它 scope 的四种解决方法
- @Lazy
- @Scope(value = “prototype”, proxyMode = ScopedProxyMode.TARGET_CLASS)
- ObjectFactory
- ApplicationContext
- 解决方法虽然不同,但理念上殊途同归: 都是推迟其它 scope bean 的获取
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
spring高级源码50讲-1-8(spring容器与bean)
文章目录 容器与 bean1) 容器接口演示1 - BeanFactory 与 ApplicationContext 的区别关键代码参考 收获💡演示2 - 国际化 2) 容器实现演示1 - DefaultListableBeanFactory代码参考 收获💡演示2 - 常见 ApplicationContext 实现代码参考 收获💡…...
![](https://img-blog.csdnimg.cn/129d53d3d4f248b0bb5889e2b647c689.png)
微服务06-Dockerfile自定义镜像+DockerCompose部署多个镜像
常见的镜像在DockerHub能找到,但是我们自己写项目得自己构造镜像 1 镜像结构 作用:提高复用性,当应用需要更新时,不再是整个系统重装进行更新 ,而是对需要更新的部分进行更新,其他地方不动——>这就是分…...
![](https://img-blog.csdnimg.cn/b224b395e6b74be5a826166d2e413982.png)
2023高教社杯 国赛数学建模A题思路 - 定日镜场的优化设计
1 赛题 A 题 定日镜场的优化设计 构建以新能源为主体的新型电力系统, 是我国实现“碳达峰”“碳中和”目标的一项重要 措施。塔式太阳能光热发电是一种低碳环保的新型清洁能源技术[1]。 定日镜是塔式太阳能光热发电站(以下简称塔式电站)收集太阳能的基本组件&…...
![](https://img-blog.csdnimg.cn/18d763edefe84d5e8f086515b5762b8b.png)
Qt +VTK+Cmake 编译和环境配置(第二篇,中级篇, 重新编译)
1.下载VTK和Cmake 这里不介绍了。我的VTK 8.2.0 cmake 3.27.4 就是不服这编译器了。重新来一次 打开Cmake,把VTK源文件路径和目标路径设置一下(目标路径自己设置,随意) 点击Configure:。 点击下一步 选择好 Qt的gcc…...
![](https://img-blog.csdnimg.cn/fc846ea34ac446aea64b5aa433760076.png)
图的学习,深度和广度遍历
一、什么是图 表示“多对多”的关系 包括: 一组顶点:通常用V(Vertex)表示顶点集合一组边:通常用E(Edge)表示边的集合 边是顶点对:(v, w)∈E,其中v,w∈V有向边<v, w&…...
![](https://www.ngui.cc/images/no-images.jpg)
ChatGPT驱动下,网站AI客服该如何进步和创新
在ChatGPT这个AI智能的驱动下,网站AI客服在进步和创新方面有很多潜力。由于GPT模型的强大语言处理能力和智能对话技巧,使得网站AI客服能够更准确和流畅地与用户交互。looklook今天总结了一些网站AI客服智能的进步和创新方向,以供大家参考。 网…...
![](https://img-blog.csdnimg.cn/img_convert/c32c69917ad2340bf1558b9ff3d80111.png)
Linux系统中实现便捷运维管理和远程访问的1Panel部署方法解析
文章目录 前言 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等下面我们介绍在Linux 本地安装1Panel 并结合cpolar 内网穿透工具实现远程访问1Panel 管理…...
![](https://www.ngui.cc/images/no-images.jpg)
数学建模黄河水沙监测数据分析
数学建模黄河水沙监测数据分析 问题: 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变化和人民生活的影响,以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾等方面都具有重要的理论指导意义。 解题思…...
![](https://img-blog.csdnimg.cn/f90b707765ba4d37a725bab7f4f7621e.png)
Unity ProBuilder(自己创建斜面、拐角)
目录 基础操作 下载 打开面板 新增对象 材质保存 1.斜面实例 2.拐角实例 3.切割实例 4.单独面赋值 基础操作 下载 打开面板 新增对象 选中想创建的块体后,在编辑器见面拉出块体 材质保存 打开材质编辑器后,将材质赋值,之后&am…...
![](https://img-blog.csdnimg.cn/9184677f1d8f45158177ada3eed07a76.gif)
以气象行业为例,浅谈在ToB/ToG行业中如何做好UI设计
商业气象公司是典型的TOB/TOG性质的公司,客户包括农业、能源、航空航天、交通运输、建筑工程等行业,它们需要准确的气象数据、预报和分析来支持业务决策和运营管理。商业气象公司通常会提供各种气象服务,如气象数据采集与分析、预报产品、风险…...
![](https://img-blog.csdnimg.cn/b8f0e4c0f52e4ef1bba95ac5fdf4f3eb.png)
shiny根据数据的长度设置多个色板
shiny根据数据的长度设置多个色板 library(shiny) library(colourpicker) ui <- fluidPage(# 添加一个选择颜色的下拉菜单uiOutput("color_dropdown") )server <- function(input, output) {# 数据长度data_length <- reactive({length(c("数据1"…...
![](https://www.ngui.cc/images/no-images.jpg)
2023高教社杯 国赛数学建模D题思路 - 圈养湖羊的空间利用率
1 赛题 D 题 圈养湖羊的空间利用率 规模化的圈养养殖场通常根据牲畜的性别和生长阶段分群饲养, 适应不同种类、不同阶段 的牲畜对空间的不同要求,以保障牲畜安全和健康;与此同时,也要尽量减少空间闲置所造成 的资源浪费。在实际…...
![](https://img-blog.csdnimg.cn/ad4015dba18347c99b6aa3c26212ea31.png)
网络是如何进行通信
网络是如何进行通信的 简介 在现代社会中,网络已经成为我们生活中不可或缺的一部分。从上网搜索信息、在线购物到远程工作和社交媒体,我们几乎无时无刻不与网络保持着联系。但是,网络究竟是个什么玩意,它是如何工作的呢…...
![](https://www.ngui.cc/images/no-images.jpg)
vue3 watch watchEffect
watch & watchEffect 函数都是监听器, 用于监视数据的变化; watch 有惰性,watchEffect 无惰性;watch 需要指定具体的监视属性,watchEffect 不需要指定具体的监视属性和配置参数,会自动感知代码依赖;watch 能获取到…...
![](https://www.ngui.cc/images/no-images.jpg)
lintcode 1410 · 矩阵注水【BFS 中等 vip】
题目链接,描述 https://www.lintcode.com/problem/1410 给一个二维矩阵,每个grid的值代表地势的高度。水流只会沿上下左右流动,且必须从地势高的地方流向地势低的地方。视为矩阵四面环水,现在从(R,C)处注水,问水能否…...
![](https://www.ngui.cc/images/no-images.jpg)
软件架构设计(十) 架构评估(复审)-方法论
我们上一节讲到了为什么么要进行架构的评估, 以及架构评估有哪些质量属性,本节正式来学习架构评估的一些方法论。 再讲到架构评估之前,还需要了解几个概念,也就是风险点,非风险点,敏感点,权衡点等。 风险点:系统架构风险是指架构设计中潜在的,存在问题的架构策略所带…...
![](https://img-blog.csdnimg.cn/a38f160f73f64146b584ee40fa25b9c2.png)
SQL注入案例
目录 一、简介 二、案例 1.发现注入点 2.寻找注入类型 3.寻找字段数 4.将传参值设为超出数据量的大值,联合查询找到回显位置 5.找到数据库 6.寻找库中的表 7.寻找表中列 8.查看表中数据 附:SQLMap注入 1.输入指令查数据库 2.输入指令查表 3…...
![](https://img-blog.csdnimg.cn/f0f4c1c5f06e40d28ac3084b4952a92f.png)
lv3 嵌入式开发-5 linux shell命令(进程管理、用户管理)
目录 1 进程处理相关命令 1.1 进程的概念 1.2 查看进程的命令 1.3 发送信号命令 2 用户管理相关命令 2.1 用户管理相关文件介绍 2.2 用户管理相关命令介绍 1 进程处理相关命令 1.1 进程的概念 进程的概念主要有两点: 进程是一个实体。每一个进程都有它自己…...
![](https://img-blog.csdnimg.cn/f89ca054ba0d4a319c9c74fb5dfd48d1.png)
学习Bootstrap 5的第六天
目录 信息警告框 警告框 实例 警告框链接 实例 关闭警告框 实例 警告框动画 实例 按钮 按钮样式 实例 按钮轮廓 实例 编辑按钮尺寸 实例 块级按钮 实例 实例 活动/禁用按钮 实例 加载器按钮 实例 扩展小知识 按钮组 按钮组 实例 实例 垂直按钮组…...
![](https://img-blog.csdnimg.cn/70d56d4d93574385b717bc17b9697f37.png)
攻防世界-WEB-NewsCenter
打开环境 有查询,猜测是sql注入 保存请求头到文件中 准备利用sqlmap 查找数据库 python sqlmap.py -r ./123.txt --dbs 查找表 python sqlmap.py -r ./123.txt --tables -D news 查找字段 python sqlmap.py -r ./123.txt --column -D news -T secret_table 显示字…...
![](https://www.ngui.cc/images/no-images.jpg)
vue router 路由跳转获取不到参数
问题: 路由传参一直不能获取到参数, 未出现报错 原因: 混淆 query 和 params 的使用方法, 在使用 params 传参时错误的使用了 path 代码: 正确写法1: 使用path要对应query ...this.$router.push({path: /Health,query: {title:…...
![](https://img-blog.csdnimg.cn/3c4301f3207542838d9d068e76999462.png)
将 Llama2 中文模型接入 FastGPT,再将 FastGPT 接入任意 GPT 套壳应用,真刺激!
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景! Llama2 是Facebook 母公司 Meta 发布的开源可商用大模型,国内的…...
![](https://img-blog.csdnimg.cn/cd1b54580b194293948e64f6f2c12bf4.png)
Ubuntu之apt-get系列--apt-get安装软件的方法/教程
原文网址:Ubuntu之apt-get系列--apt-get安装软件的方法/教程_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Ubuntu使用apt-get安装软件的方法。 安装软件 先更新列表 sudo apt-get update 安装软件 sudo apt-get install <package name>[<version>]…...
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgithub.com%2Fbailicangdu%2Fpxq%2Fblob%2Fmaster%2Fscreenshot%2Freact_props.png&pos_id=img-BN05To2a-1693987007447)
redux的理解
技术栈: react redux webpack react-router ES6/7/8 immutable 运行项目(nodejs 6.0) git clone https://github.com/bailicangdu/react-pxq.gitcd react-pxqnpm i 或者运行 yarn(推荐)npm startnpm run build (发布&…...
![](https://www.ngui.cc/images/no-images.jpg)
【Java】Java 多线程的应用场景
文章目录 前言多线程的常见应用场景多线程使用的主要目的在于业务需求程序设计代码示例运行结果总结 前言 Java多线程程序设计到的知识: 对同一个数量进行操作 对同一个对象进行操作 回调方法使用 线程同步,死锁问题 线程通信 … 多线程的常见应用…...
![](https://img-blog.csdnimg.cn/1de3829a320f41ff8eda68bc8fa699db.png)
Mysql--技术文档--索引-《索引为什么查找数据快?》-超底层详细说明索引
索引的概念 在MySQL中,索引是一种数据结构,它被用于快速查找、读取或插入数据。索引能够极大地提高数据库查询的速度。 索引的工作方式类似于图书的索引。如果你想在图书馆找到一本书,你可以按照书名进行查找。书名就像是一个索引…...
![](https://img-blog.csdnimg.cn/59f0a4f2a4cd418180955421582ee6a0.png)
jmeter 接口快速创建
通过cURL命令创建测试计划 从浏览器获取接口 从postman获取接口...
![](https://img-blog.csdnimg.cn/a744b42124a14662a0d5163ca1baa19f.bmp)
docker 笔记10:Docker轻量级可视化工具Portainer
1. 是什么 Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。 2.安装 https://docs.portainer.io/v/ce-2.9/start/install/server/docker/linux 步骤 docker命令安装 docker run -d …...
![](https://img-blog.csdnimg.cn/16526e886d79429587dabee8b74b3e9d.gif#pic_center)
028:vue上传解析excel文件,列表中输出内容
第028个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…...
![](https://img-blog.csdnimg.cn/img_convert/a59707aa2dec72e678abe59a379163cd.png)
在VR全景中嵌入3D模型有哪些优势?
现阶段,很多商企都会引入VR全景展示来宣传推广自己的产品、服务以及环境,但是环境展示凸显的沉浸式体验只是 VR全景一部分的价值所在,商企使用VR全景还有一个优势就是互动性,通过丰富多样的互动性,让用户同VR场景中的物…...
![](/images/no-images.jpg)
徐州手机网站开发公司电话/推广游戏赚钱的平台
当你在一个Xcode版本上编辑Storyboard并储存后(比如 8.1)在另一个版本上(比如8.2.1)打开想继续编辑的时候,有时候会无法打开Storyboard。所以两人合作编写一个 Xcode project 的时候,一定要使用相同的版本。…...
![](/images/no-images.jpg)
wordpress 调用特征图片/衡水网站seo
1, 人生就是投资和收益的人生。如果你有钱,就投入金钱;如果你没有金钱,而有经验和能力,就投入经验和能力;如果你既没有金钱,也没有经验和能力,但是你有热情、勤奋和努力,…...
![](https://images2018.cnblogs.com/blog/1346274/201805/1346274-20180502174144825-1717160008.png)
怎样做b2b网站/网站排名工具
【面向对象设计与构造】第二次博客作业 一、多线程协同和同步控制策略 1. 第五次作业 第五次作业设计了一套由3 部电梯组成的多电梯调度系统,通过采用线程机制, 在第三次作业所实现程序的基础上完成新的调度系统程序。本次作业为调度器,3部电…...
![](/images/no-images.jpg)
类似5173的网站怎么做/如何快速提升自己
一、数组:同一个类型数据的集合,其实他也是一个容器 1、数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些数据 2、数组的定义: 在Java中常见: 格式1: 类型 [] 数组名 new 类型[数组…...
![](https://img2018.cnblogs.com/blog/1430442/201809/1430442-20180904152644601-1502635852.png)
找别人做网站注意事项/国内永久免费建站
现象,之前本机上的 uiautomatorviewer 一直是好的,最近这段时间无故就不行了,报如标题错误,网上找了各种办法仍无法有效解决,静心细想上一次使用该工具时到目前对本机有做什么跟系统或者工具相关的配置。前段时间再调试…...
![](/images/no-images.jpg)
用电脑做网站服务器/怎样推广一个产品
介绍 看到有很多人找这个SEO计费系统的源码,就分享在这里了,我自己没有测试,想要的朋友可以自测。仅供学习交流,请勿用于商业用途。1.会员管理:系统共分三级会员流程。总站管理员, 代{过}{滤}理, 和会员(会员分三个等…...