Spring注解驱动开发笔记 1 一切的起点 1.1 pom父依赖
pom.xml
spring-boot-dependencies:核心依赖在父工程中
我们在写或者引入一些Springboot依赖的时候,不需要指定版本,就因为有这些版本仓库
1、其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件
<parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.1.RELEASE</version > <relativePath /> </parent >
2、点进去,发现还有一个父依赖
<parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > 2.3.1.RELEASE</version > </parent >
3、这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
4、以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
1.2 spring-boot-starter
依赖
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency >
springboot-boot-starter-xxx ,说白了就是Springboot的启动场景
比如spring-boot-starter-web ,他就会帮我们自动导入web的所有依赖
springboot会将所有的功能场景,都变成一个个的启动器
我们要使用什么功能,就只需要找到对应的启动器就好了start
官方的所有starter
1.3 @SpringBootApplication
默认的主启动类
@SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
但是,一个简单的启动类并不简单,我们来分析一下这些注解都干了什么。
拆分注解(@SpringBootApplication)
==@ComponentScan ==
这个注解在Spring中很重要,它对应XML配置中的元素。
作用:自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中。
==@SpringBootConfiguration ==
作用:SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类;
我们继续进去这个注解查看
@Configuration public @interface SpringBootConfiguration {}@Component public @interface Configuration {}
这里的 @Configuration ,说明这是一个spring的配置类,配置类就是对应Spring的xml 配置文件;
@Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
我们回到 SpringBootApplication 注解中继续看。
==@EnableAutoConfiguration ==
开启自动配置功能
点进注解接续查看:
@AutoConfigurationPackage :自动配置包
@Import({Registrar.class}) public @interface AutoConfigurationPackage { }
@import :Spring底层注解@import,给容器中导入一个组件
Registrar.class 作用:实现了ImportBeanDefinitionRegistrar接口,自动配置包注册,将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
这个分析完了,退到上一步,继续看
@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;
AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
获取所有的配置:==List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);==
protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this .getSpringFactoriesLoaderFactoryClass(), this .getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct." ); return configurations; }protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
这个方法getCandidateConfigurations()
又调用了 SpringFactoriesLoader
类的loadFactoryNames
静态方法,我们进入SpringFactoriesLoader
类loadFactoryNames() 方法,获取所有的加载配置
public static List<String> loadFactoryNames (Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
我们继续点击查看 loadSpringFactories 方法
项目资源:META-INF/spring.factories
系统资源:META-INF/spring.factories
从这些资源中配置了所有的nextElement(自动配置),封装成properties
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null ) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories" ) : ClassLoader.getSystemResources("META-INF/spring.factories" ); LinkedMultiValueMap result = new LinkedMultiValueMap(); while (urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while (var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for (int var11 = 0 ; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]" , var13); } } }
发现一个多次出现的文件:spring.factories,全局搜索它
spring.factories
我们根据源头打开spring.factories,看到了很多自动配置的文件;这就是自动配置根源所在。
每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;
案例说明
我们以HttpEncodingAutoConfiguration(Http编码自动配置) 为例解释自动配置原理;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Configuration @EnableConfigurationProperties({HttpProperties.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) @ConditionalOnClass({CharacterEncodingFilter.class}) @ConditionalOnProperty( prefix = "spring.http.encoding", value = {"enabled"}, matchIfMissing = true ) public class HttpEncodingAutoConfiguration { private final Encoding properties; public HttpEncodingAutoConfiguration (HttpProperties properties) { this .properties = properties.getEncoding(); } @Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter () { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this .properties.getCharset().name()); filter.setForceRequestEncoding(this .properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST)); filter.setForceResponseEncoding(this .properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE)); return filter; } }
一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效
一但这个配置类生效;这个配置类就会给容器中添加各种组件;
这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
配置文件能配置什么就可以参照某个功能对应的这个属性类
@ConfigurationProperties(prefix = "spring.http") public class HttpProperties { }
我们去配置文件里面试试前缀,看提示
所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories
配置文件,并将其中对应的 org.springframework.boot.autoconfigure
. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig
形式的IOC容器配置类,然后将这些都汇总成为一个实例并加载到IOC容器中。
结论
SpringBoot在启动的时候从类路径下的META-INF/spring.factories
中获取EnableAutoConfiguration
指定的值
将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作;
以前我们需要自动配置的东西,现在springboot帮我们做了
整合JavaEE,整体解决方案和自动配置的东西都在springboot-autoconfigure
的jar包中;
它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器中
它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件,并自动配置xxxProperties;
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;
springboot配置文件到底能写什么?怎么写?有两种查看方式:
官网配置详解
找到对应的xxxProperties类,查看源码
1.4 main方法 视频教程
SpringApplication.run方法主要分两部分,一是SpringApplication的实例化,二是run方法的执行。
@SpringBootApplication public class Springboot01HellowordApplication { public static void main (String[] args) { SpringApplication.run(Springboot01HellowordApplication.class, args); } }
整体流程:
创建 SpringApplication
保存一些信息。
判定当前应用的类型。ClassUtils。Servlet
==bootstrappers==:初始启动引导器 (List<Bootstrapper>
):去spring.factories 文件中找org.springframework.boot.==Bootstrapper==
找 ==ApplicationContextInitializer==;去spring.factories 找 ==ApplicationContextInitializer==
List<ApplicationContextInitializer<?>> initializers
找 ==ApplicationListener== 应用监听器。去spring.factories 找 ==ApplicationListener==
List<ApplicationListener<?>> listeners
运行 SpringApplication
创建引导上下文(Context环境)createBootstrapContext()
获取到所有之前的 ==bootstrappers==挨个执行 intitialize() 来完成对引导启动器上下文环境设置
让当前应用进入headless 模式。java.awt.headless
获取所有 ==RunListener==(运行监听器)【为了方便所有Listener进行事件感知】
getSpringFactoriesInstances 去spring.factories 找 ==SpringApplicationRunListener ==
遍历 SpringApplicationRunListener 调用 starting 方法;
==相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。==
保存命令行参数;ApplicationArguments
准备环境 prepareEnvironment();
返回或者创建基础环境信息对象。StandardServletEnvironment
配置环境信息对象。
绑定环境信息
监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
创建IOC容器(createApplicationContext())
根据项目类型(Servlet)创建容器,
当前会创建 AnnotationConfigServletWebServerApplicationContext
准备ApplicationContext IOC容器的基本信息 prepareContext()
应用初始化器;applyInitializers;
遍历所有的 ==ApplicationContextInitializer== 。调用 initialize.。来对ioc容器进行初始化扩展功能
遍历所有的 listener 调用 contextPrepared 。EventPublishRunListenr;通知所有的监听器contextPrepared
所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded;
创建容器中的所有组件(参见Bean的完整生命周期)
容器刷新完成后工作?afterRefresh
所有监听 器 调用 listeners.started (context); 通知所有的监听器 started
调用所有runners; callRunners()
获取容器中的 ==ApplicationRunner==
获取容器中的 ==CommandLineRunner==
合并所有runner并且按照@Order进行排序
遍历所有的runner。调用 run 方法
调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
2 Bean对象注入容器 在SpringBoot中将对象注入到容器中有多种方法。
2.1 @ComponentScan 通过@ComponentScan自动扫包+组件标注注解(@Component/@Controller/@Service/@Repository)实现。
@ComponentScan可以指定include/exclude某些类,具体的规则通过@Filter参数指定。在Java8中@ComponentScan可以重复标记。
@ComponentScan(value="com.meimeixia", includeFilters={ /* type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等 * classes:我们需要Spring在扫描时,只包含@Controller注解标注的类 */ @Filter(type=FilterType.ANNOTATION, classes={Controller.class}) }, useDefaultFilters=false) @ComponentScan(value="com.meimeixia", includeFilters={ /* type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等 * classes:我们需要Spring在扫描时,只包含@Service注解标注的类 */ @Filter(type=FilterType.ANNOTATION, classes={Service.class}) }, useDefaultFilters=false)
如果不是Java8,但又需要设置多个条件,可以使用如下方式:
@ComponentScans(value={ @ComponentScan(value="com.meimeixia", includeFilters={ /* type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等 * classes:我们需要Spring在扫描时,只包含@Controller注解标注的类 */ @Filter(type=FilterType.ANNOTATION, classes={Controller.class}) }, useDefaultFilters=false), // value指定要扫描的包 @ComponentScan(value="com.meimeixia", includeFilters={ /* type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等 * classes:我们需要Spring在扫描时,只包含@Service注解标注的类 */ @Filter(type=FilterType.ANNOTATION, classes={Service.class}) }, useDefaultFilters=false) // value指定要扫描的包 })
这种注入方式只适用于自己写的类,对于自己写的类,可以在类上面加上组件标注注解,但是对于引用的别人的类,该方法就无法使用。
@Filter使用进阶
Filter支持以下5种类型:
public enum FilterType { ANNOTATION, ASSIGNABLE_TYPE, ASPECTJ, REGEX, CUSTOM; private FilterType () { } }
前两种类型很好理解,重点讲CUSTOM类型的用法。
@ComponentScan(value="com.meimeixia", includeFilters={ /* * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等 */ // 指定新的过滤规则,这个过滤规则是我们自个自定义的,过滤规则就是由我们这个自定义的MyTypeFilter类返回true或者false来代表匹配还是没匹配 @Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class}) }, useDefaultFilters=false)
自定义过滤规则类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MyTypeFilter implements TypeFilter { @Override public boolean match (MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); ClassMetadata classMetadata = metadataReader.getClassMetadata(); Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->" + className); if (className.contains("er" )) { return true ; } return false ; } }
2.2 @Bean 使用@Configuration和@Bean注解,手动构建对象,并把它注入到容器中去。被@Configuration标记的配置类相当于Spring中的bean.xml
文件,@Bean则相当于<bean>
标签。
使用这种方式,被@Configuration标记的配置类也会被注入到容器中去。
用@Bean给容器中添加组件,方法名 就是组件的id,返回类型就是组件类型,返回值就是组件在容器中的实例。
被@Configuration标记的配置类的id就是该配置类的类名(首字母自动转为小写,不是全类名)。
以下是@Configuration的使用案例,proxyBeanMethods
属性用来设置Full模式与Lite模式
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 @Configuration(proxyBeanMethods = false) public class MyConfig { @Bean public User user01 () { User zhangsan = new User("zhangsan" , 18 ); zhangsan.setPet(tomcatPet()); return zhangsan; } @Bean("tom") public Pet tomcatPet () { return new Pet("tomcat" ); } }@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.atguigu.boot") public class MainApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tom" , Pet.class); Pet tom02 = run.getBean("tom" , Pet.class); System.out.println("组件:" +(tom01 == tom02)); MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean); User user = bean.user01(); User user1 = bean.user01(); System.out.println(user == user1); User user01 = run.getBean("user01" , User.class); Pet tom = run.getBean("tom" , Pet.class); System.out.println("用户的宠物:" +(user01.getPet() == tom)); } }
@Scope
默认@Bean使用的是Singleton单例模式,每次获取的都是同一个对象,并且是饿汉式加载,可以使用@Scope修改为Prototype,每次获取对象均不同,该模式下会启用懒汉式加载。
@Lazy
使用该注解会将单例模式的Bean修改为懒加载模式。
2.3 @Import 简单Import
也可以使用@Import注解注入bean到容器中去.
@Import注解接收一个Class[],被注入bean的id默认是导入类的全类名
,bean默认使用无参构造做初始化。
@Import({User.class, DBHelper.class}) @Configuration(proxyBeanMethods = false) public class MyConfig { }
ImportSelector
可以自己创建一个类,并且实现 ImportSelector
接口,重写 ImportSelector
的 selectImports
方法,selectImports
方法返回的是需要被注入类的全类名,然后把这个自己创建的类加入@Import的数组中。
被注入bean的名字默认是导入类的全类名
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Import(TestImportSelector.class) @Configuration public class TestConfiguration { @Bean public String StrBean () { return "this is a string" ; } }public class TestImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"qizi.Main" }; } @Override public Predicate<String> getExclusionFilter () { return null ; } }
ImportBeanDefinitionRegistrar
自己创建一个类,继承ImportBeanDefinitionRegistrar
并重写方法,手动注册beanDefinition,可以自定义bean的id名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Import({TestImportSelector.class, TestImportBeanDefinitionRegistrar.class}) @Configuration public class TestConfiguration { @Bean public String StrBean () { return "this is a string" ; } }public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registry.registerBeanDefinition("diy_hashmap_id" , new RootBeanDefinition(HashMap.class)); } }
2.4 @Conditional 有一个专门的注解叫@Conditional,需要和@Configuration等注解配合使用。可以根据自己指定的类来决定是否加载被标记类到IOC容器,必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效。
也有一些已经封装好的条件装配注解:当满足ConditionalOn指定的条件时,才进行组件注入。
@ConditionalOn扩展注解
作用(判断是否满足当前指定条件)
@ConditionalOnJava
系统的java版本是否符合要求
@ConditionalOnJava
容器中存在指定Bean
@ConditionalOnMissingBean
容器中不存在指定Bean
@ConditionalOnExpression
满足SpEL表达式指定
@ConditionalOnClass
系统中有指定的类
@ConditionalOnMissingClass
系统中没有指定的类
@ConditionalOnSingleCandidate
容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty
系统中指定的属性是否有指定的值
@ConditionalOnResource
类路径下是否存在指定资源文件
@ConditionalOnWebApplication
当前是web环境
@ConditionalOnNotWebApplication
当前不是web环境
@ConditionalOnJndi
JNDI存在指定项
那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。
自动配置类是否生效
我们可以在application.properties通过启用 debug=true
属性;在控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
Positive matches:(自动配置类启用的:正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)
Unconditional classes: (没有条件的类)
【演示:查看输出的日志】
2.5 @ImportResource 考虑到部分存量代码是用Spring时代的xml方法配置的,为了兼容历史代码,可以使用@ImportResource注解指定bean.xml配置的路径,加载xml配置中的bean。
@ImportResource("classpath:beans.xml") public class MyConfig { }
2.6 FactoryBean 一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,那么则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可以得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
FactoryBean接口对于Spring框架来说占有非常重要的地位,Spring自身就提供了70多个FactoryBean接口的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>
的形式。
public interface FactoryBean <T > { String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType" ; @Nullable T getObject () throws Exception ; @Nullable java.lang.Class<?> getObjectType(); default boolean isSingleton () { } }
需要注意的是:当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。如果确实需要获取FactoryBean,使用时需要在context#getBean的beanid前面加上一个&
符号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class ColorFactoryBean implements FactoryBean <Color > { @Override public Color getObject () throws Exception { System.out.println("ColorFactoryBean...getObject..." ); return new Color(); } @Override public Class<?> getObjectType() { return Color.class; } @Override public boolean isSingleton () { return false ; } }public class MainConfig { @Bean public ColorFactoryBean colorFactoryBean () { return new ColorFactoryBean(); } }
测试
@Test public void testImport () { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class); String[] definitionNames = applicationContext.getBeanDefinitionNames(); for (String name : definitionNames) { System.out.println(name); } Object bean2 = applicationContext.getBean("colorFactoryBean" ); System.out.println("bean的类型:" + bean2.getClass()); Object bean4 = applicationContext.getBean("&colorFactoryBean" ); System.out.println(bean4.getClass()); }
3 Bean常用拓展点 3.1 初始化和销毁 如果是使用XML配置文件的方式配置bean的话,那么可以在标签中指定bean的初始化和销毁方法,如下所示。
<bean id ="person" class ="com.meimeixia.bean.Person" init-method ="init" destroy-method ="destroy" > <property name ="age" value ="18" > </property > <property name ="name" value ="liayun" > </property > </bean >
这里,需要注意的是,在我们自己写的Person类中,需要存在init()方法和destroy()方法。而且Spring中还规定,这里的init()方法和destroy()方法必须是无参方法 ,但可以抛出异常。
@Bean注解指定
可以通过@Bean注解的两个参数指定初始化和销毁方法。
@Configuration public class MainConfigOfLifeCycle { @Bean(initMethod="init", destroyMethod="destroy") public Car car () { return new Car(); } }
初始化方法和销毁方法的调用时机?
初始化方法调用的时机:对象创建完成,如果对象中存在一些属性,并且这些属性也都赋好值之后,那么就会调用bean的初始化方法。对于单实例bean来说,在Spring容器创建完成后,Spring容器会自动调用bean的初始化方法;对于多实例bean来说,在每次获取bean对象的时候,调用bean的初始化方法。
销毁方法调用的时机:对于单实例bean来说,在容器关闭的时候,会调用bean的销毁方法;对于多实例bean来说,Spring容器不会管理这个bean,也就不会自动调用这个bean的销毁方法了。不过,用户可以手动调用多实例bean的销毁方法。
实现InitializingBean和DisposableBean接口
如果不使用@Bean指定初始化和销毁方法,也可以实现InitializingBean、DisposableBean这两个接口,这样也能实现指定初始化和销毁方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Component public class Cat implements InitializingBean , DisposableBean { public Cat () { System.out.println("cat constructor..." ); } @Override public void afterPropertiesSet () throws Exception { System.out.println("cat afterPropertiesSet..." ); } @Override public void destroy () throws Exception { System.out.println("cat destroy..." ); } }
@PostConstruct和@PreDestroy注解
@PostConstruct和@PreDestroy是Java依据JSR-250规范定义的两个注解。
Constructor(构造方法)→@Autowired(依赖注入)→@PostConstruct(注释的方法)
调用destroy()方法→@PreDestroy→destroy()方法→bean销毁
@Component public class Dog { public Dog () { System.out.println("dog constructor..." ); } @PostConstruct public void init () { System.out.println("dog...@PostConstruct..." ); } @PreDestroy public void destory () { System.out.println("dog...@PreDestroy..." ); } }
3.2 BeanPostProcessor BeanPostProcessor后置处理器。Spring容器中的每一个bean对象初始化前后,都会执行BeanPostProcessor接口的实现类中的这两个方法 。
postProcessBeforeInitialization方法会在bean实例化和==属性设置之后,自定义初始化方法之前==被调用,而postProcessAfterInitialization方法会在自定义==初始化方法执行之后==被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean); return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean); return bean; } @Override public int getOrder () { return 3 ; } }
3.3 XxxAware接口 XxxAware使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Red implements ApplicationContextAware , BeanNameAware , EmbeddedValueResolverAware { private ApplicationContext applicationContext; @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { System.out.println("传入的IOC:" + applicationContext); this .applicationContext = applicationContext; } @Override public void setBeanName (String name) { System.out.println("当前bean的名字:" + name); } @Override public void setEmbeddedValueResolver (StringValueResolver resolver) { String resolveStringValue = resolver.resolveStringValue("你好,${os.name},我的年龄是#{20*18}" ); System.out.println("解析的字符串:" + resolveStringValue); } }
XxxAware接口的底层原理是由XxxAwareProcessor实现类实现的,每一个XxxAware接口都有它自己对应的XxxAwareProcessor实现类。 例如,以ApplicationContextAware接口为例,ApplicationContextAware接口的底层原理就是由ApplicationContextAwareProcessor类实现的。从ApplicationContextAwareProcessor类的源码可以看出,其实现了BeanPostProcessor接口,本质上是一个后置处理器。
3.4 AOP使用及原理 引入AOP依赖
<dependency > <groupId > org.springframework</groupId > <artifactId > spring-aspects</artifactId > <version > 4.3.12.RELEASE</version > </dependency >
定义目标类
public class MathCalculator { public int div (int i, int j) { System.out.println("MathCalculator...div..." ); return i / j; } }
定义切面类
AOP中的通知方法及其对应的注解与含义如下:
前置通知(对应的注解是@Before):在目标方法运行之前运行
后置通知(对应的注解是@After):在目标方法运行结束之后运行,无论目标方法是正常结束还是异常结束都会执行
返回通知(对应的注解是@AfterReturning):在目标方法正常返回之后运行
异常通知(对应的注解是@AfterThrowing):在目标方法运行出现异常之后运行
环绕通知(对应的注解是@Around):动态代理,我们可以直接手动推进目标方法运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Aspect public class LogAspects { @Pointcut("execution(public int com.meimeixia.aop.MathCalculator.*(..))") public void pointCut () {} @Before("pointCut()") public void logStart (JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); System.out.println(joinPoint.getSignature().getName() + "运行......@Before,参数列表是:{" + Arrays.asList(args) + "}" ); } @After("pointCut()") public void logEnd (JoinPoint joinPoint) { System.out.println(joinPoint.getSignature().getName() + "结束......@After" ); } @AfterReturning("pointCut()") public void logReturn (JoinPoint joinPoint, Object result) { System.out.println(joinPoint.getSignature().getName() + "正常返回......@AfterReturning,运行结果是:{" + result + "}" ); } @AfterThrowing("pointCut()") public void logException () { System.out.println("除法出现异常......异常信息:{}" ); } }
开启基于注解的AOP模式
@EnableAspectJAutoProxy @Configuration public class MainConfigOfAOP { @Bean public MathCalculator calculator () { return new MathCalculator(); } @Bean public LogAspects logAspects () { return new LogAspects(); } }
AOP底层原理
最后,我们还需要对AOP原理做一个简单的总结,完美结束对其研究的旅程。
利用@EnableAspectJAutoProxy注解来开启AOP功能
这个AOP功能是怎么开启的呢?主要是通过@EnableAspectJAutoProxy注解向IOC容器中注册一个AnnotationAwareAspectJAutoProxyCreator组件来做到这点的
AnnotationAwareAspectJAutoProxyCreator组件是一个后置处理器
该后置处理器是怎么工作的呢?在IOC容器创建的过程中,我们就能清楚地看到这个后置处理器是如何创建以及注册的,以及它的工作流程。
首先,在创建IOC容器的过程中,会调用refresh()方法来刷新容器,而在刷新容器的过程中有一步是来注册后置处理器的,如下所示:
registerBeanPostProcessors(beanFactory);
其实,这一步会为所有后置处理器都创建对象。
在刷新容器的过程中还有一步是来完成BeanFactory的初始化工作的,如下所示:
finishBeanFactoryInitialization(beanFactory);
很显然,剩下的单实例bean自然就包括MathCalculator(业务逻辑类)和LogAspects(切面类)这两个bean,因此这两个bean就是在这儿被创建的。
创建业务逻辑组件和切面组件
在这两个组件创建的过程中,最核心的一点就是AnnotationAwareAspectJAutoProxyCreator(后置处理器)会来拦截这俩组件的创建过程
怎么拦截呢?主要就是在组件创建完成之后,判断组件是否需要增强。如需要,则会把切面里的通知方法包装成增强器,然后再为业务逻辑组件创建一个代理对象。我们也认真仔细探究过了,在为业务逻辑组件创建代理对象的时候,使用的是cglib来创建动态代理的。当然了,如果业务逻辑类有实现接口,那么就使用jdk来创建动态代理。一旦这个代理对象创建出来了,那么它里面就会有所有的增强器。这个代理对象创建完以后,IOC容器也就创建完了。接下来,便要来执行目标方法了。
执行目标方法
此时,其实是代理对象来执行目标方法
使用CglibAopProxy类的intercept()方法来拦截目标方法的执行,拦截的过程如下:
得到目标方法的拦截器链,所谓的拦截器链其实就是每一个通知方法又被包装为了方法拦截器,即MethodInterceptor
利用拦截器的链式机制(责任链模式),依次进入每一个拦截器中进行执行
最终,整个的执行效果就会有两套:(据说Spring5中下面的拦截顺序有调整)
目标方法正常执行:前置通知→目标方法→后置通知→返回通知
目标方法出现异常:前置通知→目标方法→后置通知→异常通知
3.5 声明式事务 添加c3p0数据源、MySQL数据库驱动以及spring-jdbc的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > 0.9.1.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.44</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 4.3.12.RELEASE</version > </dependency >
引入spring-jdbc模块后就可以用Spring提供的JDBC模板(即JdbcTemplate)来操作数据库,从而简化对数据库的操作以及事务控制。
配置类bean注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @EnableTransactionManagement @ComponentScan("com.meimeixia.tx") @Configuration public class TxConfig { @Bean public DataSource dataSource () throws Exception { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root" ); dataSource.setPassword("liayun" ); dataSource.setDriverClass("com.mysql.jdbc.Driver" ); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test" ); return dataSource; } @Bean public JdbcTemplate jdbcTemplate () throws Exception { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); return jdbcTemplate; } @Bean public PlatformTransactionManager platformTransactionManager () throws Exception { return new DataSourceTransactionManager(dataSource()); } }
测试
@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void insert () { String sql = "insert into `tbl_user`(username, age) values(?, ?)" ; String username = UUID.randomUUID().toString().substring(0 , 5 ); jdbcTemplate.update(sql, username, 19 ); } }
@Service public class UserService { @Autowired private UserDao userDao; @Transactional public void insertUser () { userDao.insert(); System.out.println("插入完成..." ); int i = 10 / 0 ; } }
声明式事务的原理
首先,使用AutoProxyRegistrar向Spring容器里面注册一个后置处理器,这个后置处理器会负责给我们包装代理对象。然后,使用ProxyTransactionManagementConfiguration(配置类)再向Spring容器里面注册一个事务增强器,此时,需要用到事务拦截器。最后,代理对象执行目标方法,在这一过程中,便会执行到当前Spring容器里面的拦截器链,而且每次在执行目标方法时,如果出现了异常,那么便会利用事务管理器进行回滚事务,如果执行过程中一切正常,那么则会利用事务管理器提交事务。
分析细节:Spring注解驱动开发第35讲——声明式事务原理的源码分析
3.6 BeanFactoryPostProcessor 与bean实例的后置处理器BeanPostProcessor相似,BeanFactoryPostProcessor是BeanFactory(创建bean的工厂)的后置处理器。
BeanFactoryPostProcessor是Spring提供的一个增强点,该增强点的增强时机是“在BeanFactory标准初始化之后,所有的beanDefinition已经保存加载到BeanFactory中,但是bean的实例还未被创建,可以来定制和修改BeanFactory里面的一些内容” 。
可以通过实现BeanFactoryPostProcessor
接口来自定义自己的bean工厂后置处理器。
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory..." ); int count = beanFactory.getBeanDefinitionCount(); String[] names = beanFactory.getBeanDefinitionNames(); System.out.println("当前BeanFactory中有" + count + "个Bean" ); System.out.println(Arrays.asList(names)); } }
分析细节:Spring注解驱动开发第36讲——从源码角度理解BeanFactoryPostProcessor的原理
3.7 BeanDefinitionRegistryPostProcessor BeanDefinitionRegistryPostProcessor
是BeanFactoryPostProcessor
的子接口,同样也是Spring提供的一个增强点。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException ; }
BeanDefinitionRegistryPostProcessor的切入时机是在所有bean定义信息将要被加载,但是bean实例还未创建的时候 。
即:BeanDefinitionRegistryPostProcessor ➡️ 加载beanDefinition ➡️ BeanFactoryPostProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的数量:" + beanFactory.getBeanDefinitionCount()); } @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { System.out.println("postProcessBeanDefinitionRegistry...bean的数量:" + registry.getBeanDefinitionCount()); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition(); registry.registerBeanDefinition("hello" , beanDefinition); } }
BeanDefinitionRegistryPostProcessor组件执行原理小结
创建IOC容器
创建IOC容器时,要调用一个刷新方法,即refresh方法
从IOC容器中获取到所有的BeanDefinitionRegistryPostProcessor组件,并依次触发它们的postProcessBeanDefinitionRegistry方法,然后再来触发它们的postProcessBeanFactory方法
再来从IOC容器中获取到所有的BeanFactoryPostProcessor组件,并依次触发它们的postProcessBeanFactory方法
分析细节:Spring注解驱动开发第37讲——BeanDefinitionRegistryPostProcessor执行原理
3.8 ApplicationListener ApplicationListener是Spring提供的基于观察者模式实现的应用监听器。当有指定的事件发生时,就会触发对应的监听器,执行onApplicationEvent方法。该接口中带的泛型就是我们要监听的事件。
public interface ApplicationListener <E extends ApplicationEvent > extends EventListener { void onApplicationEvent (E e) ; }
案例:
@Component public class MyApplicationListener implements ApplicationListener <ApplicationEvent > { @Override public void onApplicationEvent (ApplicationEvent event) { System.out.println("收到事件:" + event); } }
Spring默认的四个事件:
ContextClosedEvent 容器关闭事件
ContextRefreshedEvent 容器刷新事件
ContextStartedEvent 容器开始事件
ContextStoppedEvent 容器停止事件
发布自定义事件
写一个监听器来监听某个事件。当然了,监听的这个事件必须是ApplicationEvent及其子类。
把监听器加入到容器中,这样Spring才能知道有这样一个监听器。
只要容器中有相关事件发布,那么我们就能监听到这个事件。
public class IOCTest_Ext { @Test public void test01 () { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class); applicationContext.publishEvent(new ApplicationEvent(new String("diy事件" ))); applicationContext.close(); } }
事件监听机制原理
IOC容器启动过程中,通过在refresh()方法中调用registerListeners()方法,将所有的监听器注册到事件派发器中去
事件发布时,首先获取到事件派发器,遍历所有监听器,向所有符合条件的监听器派发事件
监听器获取到事件后执行onApplicationEvent方法
3.9 @EventListener 除了通过实现ApplicationListener接口完成事件监听以外,还可以通过使用@EventListener注解,让任意方法都能监听事件。
@Service public class UserService { @EventListener(classes={ApplicationEvent.class}) public void listen () { System.out.println("UserService...监听到的事件:" + event); } }
@EventListener注解原理
Spring会使用EventListenerMethodProcessor这个处理器来解析方法上的@EventListener注解。
EventListenerMethodProcessor实现了一个接口,叫SmartInitializingSingleton,这个接口有一个afterSingletonsInstantiated方法,该方法是在所有的单实例bean已经全部被创建完以后才会被执行。
在创建好所有的单实例bean后,判断每一个bean对象是否是SmartInitializingSingleton这个接口类型的,如果是,那么便调用它里面的afterSingletonsInstantiated方法,而该方法就是SmartInitializingSingleton接口中定义的方法。
4 完整生命周期小结 1、prepareRefresh(); 刷新前的预处理
1. initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法;2. getEnvironment().validateRequiredProperties();检验属性的合法等3. earlyApplicationEvents= new LinkedHashSet<ApplicationEvent > (); 保存容器中的一些早期的事件
2、obtainFreshBeanFactory(); 获取BeanFactory
1. refreshBeanFactory();刷新【创建】BeanFactory; - 创建了一个this.beanFactory = new DefaultListableBeanFactory(); - 设置id;2. getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory对象;3. 将创建的BeanFactory【DefaultListableBeanFactory】返回;
3、prepareBeanFactory(beanFactory); BeanFactory的预准备工作(BeanFactory进行一些设置)
1. 设置BeanFactory的类加载器、支持表达式解析器...2. 添加部分BeanPostProcessor【ApplicationContextAwareProcessor】3. 设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware、xxx;4. 注册可以解析的自动装配;我们能直接在任何组件中自动注入: - BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext5. 添加BeanPostProcessor【ApplicationListenerDetector】6. 添加编译时的AspectJ;7. 给BeanFactory中注册一些能用的组件; - environment【ConfigurableEnvironment】、 - systemProperties【Map<String, Object> 】、 - systemEnvironment【Map<String, Object> 】
4、postProcessBeanFactory(beanFactory); BeanFactory准备工作完成后进行的后置处理工作
- 子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
==以上是BeanFactory的创建及预准备工作==
5、invokeBeanFactoryPostProcessors(beanFactory); 执行BeanFactoryPostProcessor的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 BeanFactoryPostProcessor:BeanFactory的后置处理器。在BeanFactory标准初始化之后执行的; 两个接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor1. 先执行BeanDefinitionRegistryPostProcessor - 获取所有的BeanDefinitionRegistryPostProcessor; - 看先执行实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor、 postProcessor.postProcessBeanDefinitionRegistry(registry) - 在执行实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessor; postProcessor.postProcessBeanDefinitionRegistry(registry) - 最后执行没有实现任何优先级或者是顺序接口的BeanDefinitionRegistryPostProcessors; postProcessor.postProcessBeanDefinitionRegistry(registry) 2. 加载BeanDefinition 3. 再执行BeanFactoryPostProcessor的方法 - 获取所有的BeanFactoryPostProcessor - 看先执行实现了PriorityOrdered优先级接口的BeanFactoryPostProcessor、 postProcessor.postProcessBeanFactory() - 在执行实现了Ordered顺序接口的BeanFactoryPostProcessor; postProcessor.postProcessBeanFactory() - 最后执行没有实现任何优先级或者是顺序接口的BeanFactoryPostProcessor; postProcessor.postProcessBeanFactory()
6、registerBeanPostProcessors(beanFactory); 注册BeanPostProcessor(Bean的后置处理器)【 intercept bean creation】
- 不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的 - BeanPostProcessor、 - DestructionAwareBeanPostProcessor、 - InstantiationAwareBeanPostProcessor、 - SmartInstantiationAwareBeanPostProcessor、 - MergedBeanDefinitionPostProcessor【internalPostProcessors】、 1. 获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级 2. 先注册PriorityOrdered优先级接口的BeanPostProcessor; 把每一个BeanPostProcessor;添加到BeanFactory中 beanFactory.addBeanPostProcessor(postProcessor); 3. 再注册Ordered接口的 4. 最后注册没有实现任何优先级接口的 5. 最终注册MergedBeanDefinitionPostProcessor; 6. 注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener,如果是,applicationContext.addApplicationListener((ApplicationListener<?>) bean);
7、initMessageSource(); 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
1. 获取BeanFactory2. 看容器中是否有id为messageSource的,类型是MessageSource的组件 - 如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource; - MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;3. 把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource; - beanFactory.registerSingleton(MESSAGE_SOURCE_ BEAN_NAME, this.messageSource); - MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);
8、initApplicationEventMulticaster(); 初始化事件派发器
1. 获取BeanFactory2. 从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;3. 如果上一步没有配置;创建一个SimpleApplicationEventMulticaster4. 将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入
9、onRefresh(); 留给子容器(子类)
- 子类重写这个方法,在容器刷新的时候可以自定义逻辑;
10、registerListeners(); 从容器中将所有项目里面的ApplicationListener注册到事件派发器
1. 从容器中拿到所有的ApplicationListener2. 将每个监听器添加到事件派发器中; - getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);3. 派发之前步骤产生的事件;
11、finishBeanFactoryInitialization(beanFactory); 初始化所有剩下的单实例bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 beanFactory.preInstantiateSingletons(); 初始化后剩下的单实例bean1. 获取容器中的所有Bean,依次进行初始化和创建对象2. 获取Bean的定义信息;RootBeanDefinition3. Bean不是抽象的,是单实例的,不是懒加载; 1)、判断是否是FactoryBean;是否是实现FactoryBean接口的Bean; 2)、不是工厂Bean。利用getBean(beanName);创建对象 0、getBean(beanName); ioc.getBean(); 1、doGetBean(name, null, null, false); 2、先获取缓存中保存的单实例Bean。如果能获取到说明这个Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来) 从private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);获取的 3、缓存中获取不到,开始Bean的创建对象流程; 4、标记当前bean已经被创建 5、获取Bean的定义信息; 6、【获取当前Bean依赖的其他Bean;如果有按照getBean()把依赖的Bean先创建出来;】 7、启动单实例Bean的创建流程; 1)、createBean(beanName, mbd, args); 2)、Object bean = resolveBeforeInstantiation(beanName, mbdToUse);让BeanPostProcessor先拦截返回代理对象; 【InstantiationAwareBeanPostProcessor】:提前执行; 先触发:postProcessBeforeInstantiation(); 如果有返回值:触发postProcessAfterInitialization(); 3)、如果前面的InstantiationAwareBeanPostProcessor没有返回代理对象;调用4) 4)、Object beanInstance = doCreateBean(beanName, mbdToUse, args);创建Bean 1)、【创建Bean实例】;createBeanInstance(beanName, mbd, args); 利用工厂方法或者对象的构造器创建出Bean实例; 2)、applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); 调用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType, beanName); 3)、【Bean属性赋值】populateBean(beanName, mbd, instanceWrapper); 赋值之前: 1)、拿到InstantiationAwareBeanPostProcessor后置处理器; postProcessAfterInstantiation(); 2)、拿到InstantiationAwareBeanPostProcessor后置处理器; postProcessPropertyValues(); =====赋值之前:===== 3)、应用Bean属性的值;为属性利用setter方法等进行赋值; applyPropertyValues(beanName, mbd, bw, pvs); 4)、【Bean初始化】initializeBean(beanName, exposedObject, mbd); 1)、【执行Aware接口方法】invokeAwareMethods(beanName, bean);执行xxxAware接口的方法 BeanNameAware\BeanClassLoaderAware\BeanFactoryAware 2)、【执行后置处理器初始化之前】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); BeanPostProcessor.postProcessBeforeInitialization(); 3)、【执行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd); 1)、是否是InitializingBean接口的实现;执行接口规定的初始化; 2)、是否自定义初始化方法; 4)、【执行后置处理器初始化之后】applyBeanPostProcessorsAfterInitialization BeanPostProcessor.postProcessAfterInitialization(); 5)、注册Bean的销毁方法; 5)、将创建的Bean添加到缓存中singletonObjects; ioc容器就是这些Map;很多的Map里面保存了单实例Bean,环境信息。。。。; 所有Bean都利用getBean创建完成以后;1. 检查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就执行afterSingletonsInstantiated();
12、finishRefresh(); 完成BeanFactory的初始化创建工作、IOC容器就创建完成
1. initLifecycleProcessor();初始化和生命周期有关的后置处理器;LifecycleProcessor - 默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有,new DefaultLifecycleProcessor(); - 加入到容器; - 写一个LifecycleProcessor的实现类,可以在BeanFactory - void onRefresh(); - void onClose(); 2. getLifecycleProcessor().onRefresh(); - 拿到前面定义的生命周期处理器(BeanFactory);回调onRefresh();3. publishEvent(new ContextRefreshedEvent(this));发布容器刷新完成事件;4. liveBeansView.registerApplicationContext(this);
总结:
Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;
xml注册bean;<bean>
注解注册Bean;@Service、@Component、@Bean、xxx
Spring容器会合适的时机创建这些Bean
用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;
统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();
后置处理器;BeanPostProcessor
每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
AutowiredAnnotationBeanPostProcessor:处理自动注入
AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
…
增强的功能注解:
AsyncAnnotationBeanPostProcessor
…
事件驱动模型;
ApplicationListener;事件监听;
ApplicationEventMulticaster;事件派发: