//springboot的启动方法返回的就是applicationContext实现对象
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map map = (Map) singletonObjects.get(beanFactory);
map.entrySet().stream().forEach(e -> {
//打印spring容器中的单例Bean
System.out.println(e.getKey() + "=" + e.getValue());
});
1)国家化
如果需要配置请参考SpringBoot 国际化_naki_bb的博客-CSDN博客
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))
2)加载资源
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}
3)获取环境资源
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
4)事件发布,新解耦方式
//自定义事件
class MyEvent extends ApplicationEvent {
MyEvent(Object source) {
super(source);
}
}
//发布自定义事件
@Component
public class MyEventPublisher {
@Autowired
private ApplicationEventPublisher publisher;
public void publish() {
publisher.publishEvent(new MyEvent(this));
}
}
//监听自定义事件
@Component
public class MyEventListener {
@EventListener
public void aaa(MyEvent event){
System.out.println("aaa监控到了My event事件");
}
@EventListener
public void bbb(MyEvent event){
System.out.println("bbb监控到了My event事件");
}
}
//调用事件发布方法
MyEventPublisher myEventPublisher = context.getBean(MyEventPublisher.class);
myEventPublisher.publish();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class, scope, 初始化, 销毁)
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
//可以查看beanFactory中所有BeanDefinition的
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
//准备好所有单例
beanFactory.preInstantiateSingletons();
由上可以发现
a. beanFactory 不会做的事
1. 不会主动调用 BeanFactory 后处理器
2. 不会主动添加 Bean 后处理器
3. 不会主动初始化单例
4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
b. bean 后处理器会有排序的逻辑
经典的容器, 基于 classpath 下 xml 格式的配置文件来创建spring容器
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
基于磁盘路径下 xml 格式的配置文件来创建
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext(
"src\\main\\resources\\a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
经典的容器, 基于 java 注解配置类来创建
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
经典的容器, 基于 java 配置类来创建, 用于 web 环境
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;
};
}
}
GenericApplicationContext 是一个【干净】的容器
当需要让这个容器可以识别@Autowired,@Value时,需要添加如下后处理器
context.getDefaultListableBeanFactory()
.setAutowireCandidateResolver(
new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
当需要让这个容器可以识别@Resource @PostConstruct @PreDestroy
context.registerBean(CommonAnnotationBeanPostProcessor.class);
当需要让这个容器可以识别@ConfigurationProperties
ConfigurationPropertiesBindingPostProcessor
.register(context.getDefaultListableBeanFactory());
初始化容器,执行beanFactory后处理器, 执行bean后处理器,
context.refresh();
销毁容器
context.close();
配置类如下:
@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/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
@Bean解析注入容器
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 {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//获取Config的源数据
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
//获取所有使用@Bean的方法
Set methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
//解析包含@Bean包含initMethod属性值
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//根据容器中已经存在名为config的Bean的方法名实例化Bean
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
//在使用构造器方式注入
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
//根据builder生成BeanDefinition信息
AbstractBeanDefinition bd = builder.getBeanDefinition();
//实例化Bean根据BeanDefinition,spring的id为方法名
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@ComponentScan
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.refresh
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//获取Config实例中的ComponentScan注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
for (String p : componentScan.basePackages()) {
//配置的包信息为“.”,Resource则加载文件“/”分割
// com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//根据路径,获取所有的.class
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
//获取Resource的源数据
MetadataReader reader = factory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//因为@Service,@Controller等都是@Component的派生类,则需要两个都判断
// 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容器
context.registerBean(AtBeanPostProcessor.class); // 解析 @Bean
context.registerBean(ComponentScanPostProcessor.class); // 解析 @Component
Aware 接口提供了一种【内置】 的注入手段,例如
BeanNameAware 注入 bean 的名字
BeanFactoryAware 注入 BeanFactory 容器
ApplicationContextAware 注入 ApplicationContext 容器
EmbeddedValueResolverAware 注入 ${} 解析器
InitializingBean 接口提供了一种【内置】的初始化手段
对比
内置的注入和初始化不受扩展功能的影响,总会被执行
而扩展功能受某些情况影响可能会失效
因此 Spring 框架内部的类常用内置注入和初始化
Java 配置类不包含 BeanFactoryPostProcessor 的情况
Java 配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效
对应的代码:
@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");
};
}
}
因为MyConfig1配置类中包含实例化自定义BeanFactoryPostProcessor则会先实例化这个配置类,但是因为没有设置自带的BeanFactoryPostProcessor和BeanPostProcessor则,@Autowired,@PostConstruct都没有处理的后处理器,则会注入失败。
则可以使用Aware和InitializingBean接口的内置注入手段,使其一定会先被执行
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(MyConfig2.class);
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("注入 ApplicationContext");
}
@Bean // beanFactory 后处理器
public BeanFactoryPostProcessor processor2() {
return beanFactory -> {
log.debug("执行 processor2");
};
}
}
注意
解决方法:
用内置依赖注入和初始化取代扩展依赖注入和初始化
用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建
Spring 提供了多种初始化手段, @PostConstruct,@Bean(initMethod) 之外,还可以实现 InitializingBean 接口来进行初始化,如果同一个 bean 用了以上手段声明了 3 个初始化方法,那么它们的执行顺序是
@PostConstruct 标注的初始化方法
InitializingBean 接口的初始化方法
@Bean(initMethod) 指定的初始化方法
@Bean(initMethod = "init3")
public Bean1 bean1() {
return new Bean1();
}
public class Bean1 implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
@PostConstruct
public void init1() {
log.debug("初始化1");
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化2");
}
public void init3() {
log.debug("初始化3");
}
}
Spring 也提供了多种销毁手段,执行顺序为
@PreDestroy 标注的销毁方法
DisposableBean 接口的销毁方法
@Bean(destroyMethod) 指定的销毁方法
@Bean(destroyMethod = "destroy3")
public Bean2 bean2() {
return new Bean2();
}
public class Bean2 implements DisposableBean {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
@PreDestroy
public void destroy1() {
log.debug("销毁1");
}
@Override
public void destroy() throws Exception {
log.debug("销毁2");
}
public void destroy3() {
log.debug("销毁3");
}
}
在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope
singleton,容器启动时创建(未设置延迟),容器关闭时销毁
prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
request,每次请求用到此 bean 时创建,请求结束时销毁
session,每个会话用到此 bean 时创建,会话结束时销毁
application,web 容器用到此 bean 时创建,容器停止时销毁
当在单例对象中注入多例对象,直接使用@Autowired时会,导致注入的Bean始终都是同一个,不是多例的。
问题出现的原因是,对于单例对象,属性的注入只会发生一次,所以单例中注入的多例对象始终都是第一次注入的。
解决方案: 推迟多例Bean的注入
1.使@Lazy生成代理对象,每次使用时,代理对象会创建新的多例对象
@Component
public class E {
@Lazy
@Autowired
private F1 f1;
}
@Scope("prototype")
@Component
public class F1 {
}
2.在多例Bean的Scope中添加 proxyMode 属性 底层也是利用代理对象
@Component
public class E {
@Autowired
private F2 f2;
}
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
3.使用ObjectFactory工厂获取多例Bean
import org.springframework.beans.factory.ObjectFactory;
@Component
public class E {
@Autowired
private ObjectFactory f3;
public F3 getF3() {
//使用时,使用getObject就可得到多例的Bean对象
return f3.getObject();
}
}
@Scope("prototype")
@Component
public class F3 {
}
4.使用ApplicationContext获取多例Bean
@Component
public class E {
@Autowired
private ApplicationContext context;
public F4 getF4() {
return context.getBean(F4.class);
}
}
@Scope("prototype")
@Component
public class F4 {
}
使用代理对象对性能有影响,所以推荐3-4方式,在单例对象中获取多例对象实例
AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能
Spring底层实现是基于JDK和Cglib生成代理对象
除此以外,aspectj 提供了两种另外的 AOP 底层实现:
第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
作为对比,之前学习的代理是运行时生成新的字节码
简单比较的话:
aspectj 在编译和加载时,修改目标字节码,性能较高
aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行
@Aspect //注意此切面并未被 Spring 管理
public class MyAspect {
private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
@Before("execution(* com.itheima.service.MyService.*())")
public void before() {
log.debug("before()");
}
}
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
final public void foo() {
log.debug("foo()");
this.bar();
}
public void bar() {
log.debug("bar()");
}
}
aspectj-maven-plugin
编译器也能修改 class 实现增强
编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强
注意
版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器
org.codehaus.mojo
aspectj-maven-plugin
1.14.0
1.8
8
true
true
ignore
UTF-8
compile
test-compile
直接在编译阶段就会修改class文件,对其进行修改实现功能增强.在调用MyService的所有方法时,都会增强before方法
VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
使用上面agent,MyService在编译完成时,方法已经增强
public class JdkProxyDemo {
interface Foo {
void foo();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] param) {
// 目标对象
Target target = new Target();
// 代理对象
Foo proxy = (Foo) Proxy.newProxyInstance(
Target.class.getClassLoader(), new Class[]{Foo.class},
(p, method, args) -> {
System.out.println("proxy before...");
Object result = method.invoke(target, args);
System.out.println("proxy after...");
return result;
});
// 调用代理
proxy.foo();
}
}
jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系
前 16 次反射性能较低
第 17 次调用会生成代理类,优化为非反射调用
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] param) {
// 目标对象
Target target = new Target();
// 代理对象
Target proxy = (Target) Enhancer.create(Target.class,
(MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("proxy before...");
Object result = methodProxy.invoke(target, args);
// 另一种调用方法,不需要目标对象实例
// Object result = methodProxy.invokeSuper(p, args);
System.out.println("proxy after...");
return result;
});
// 调用代理
proxy.foo();
}
}
cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系
限制⛔:根据上述分析 final 类无法被 cglib 增强
调用目标时有所改进,见下面代码片段
method.invoke 是反射调用,必须调用到足够次数才会进行优化
methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
代理对象调用流程如下(以 JDK 动态代理实现为例)
代理对象调用流程如下(以 JDK 动态代理实现为例)
从 ProxyFactory 获得 Target 和环绕通知链,根据他俩创建 MethodInvocation,简称 mi
首次执行 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
进入环绕通知1,执行前增强,再次调用 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
进入环绕通知2,执行前增强,调用 mi.proceed() 发现没有环绕通知,调用 mi.invokeJoinPoint() 执行目标方法
目标方法执行结束,将结果返回给环绕通知2,执行环绕通知2 的后增强
环绕通知2继续将结果返回给环绕通知1,执行环绕通知1 的后增强
环绕通知1返回最终的结果
图中不同颜色对应一次环绕通知或目标的调用起始至终结