Spring注解驱动
1、注册组件
(1)配置类
@Configuration注解来标注该类是一个Spring的配置类。
@Bean注解将Person类注入到Spring的IOC容器中。
1 @Configuration
2 public class MainConfig {
3
4 //给容器中注册一个Bean;类型为返回值的类型,id默认为用方法名
5 @Bean
6 public Person person(){
7 return new Person("zs",18);
8 }
9 }
1 @Test
2 public void test(){
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
4 Person person = applicationContext.getBean(Person.class);
5 System.out.println(person);
6 }
(2)@ComponentScan
@ComponentScan开启组件扫描,使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。
①value指定要扫描的包名
1 @Configuration
2 @ComponentScan(value = "com.my")
3 public class MainConfig {}
②excludeFilters指定扫描的时候按照什么规则排除哪些组件
1 //excludeFilters = Filter[]
2 @ComponentScan(value = "com.my",excludeFilters = {
3 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
4 })
③includeFilters指定扫描的时候只需要包含哪些组件
必须将使用默认扫描方式置为false,否则不起效果。
1 //includeFilters = Filter[]
2 @ComponentScan(value = "com.my",includeFilters = {
3 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
4 },useDefaultFilters = false)
④@ComponentScans
一个类可以定义多个@ComponentScan,因此可以将这些@ComponentScan放进一个@ComponentScans注解中。
1 @ComponentScans(value = {
2 @ComponentScan(value = "com.my",includeFilters = {
3 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
4 },useDefaultFilters = false)
5 })
(3)过滤规则
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则表达式
FilterType.CUSTOM:使用自定义规则
1 public class MyTypeFilter implements TypeFilter {
2
3 //MetadataReader:读取到的当前正在扫描的类的信息
4 //MetadataReaderFactory:可以获取到其他任何类的信息
5
6 @Override
7 public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
8 //获取当前类注解的信息
9 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
10 //获取当前正在扫描的类的类信息
11 ClassMetadata classMetadata = metadataReader.getClassMetadata();
12 //获取当前类资源(类的路径)
13 Resource resource = metadataReader.getResource();
14 String className = classMetadata.getClassName();
15 System.out.println("类名:" + className);
16 if(className.contains("er")){
17 return true;
18 }
19 return false;
20 }
21 }
1 @Configuration
2 @ComponentScan(value = "com.my",includeFilters = {
3 @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
4 },useDefaultFilters = false)
(4)@Scope
设置组件作用域。
1 @Configuration
2 public class MainConfig2 {
3 @Scope("prototype")
4 @Bean
5 public Person person(){
6 return new Person("zs",18);
7 }
8 }
1 @Test
2 public void test03(){
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
4 Person bean = applicationContext.getBean(Person.class);
5 Person bean2 = applicationContext.getBean(Person.class);
6 System.out.println(bean == bean2);
7 }
8 //Scope值为默认时,即单实例时,结果为true
9 //Scope值为prototype时,即多实例时,结果为false
(5)@Lazy
懒加载只对单实例bean有用。
单实例bean默认是在容器启动的时候创建对象。懒加载使得容器启动时不创建对象,在第一次使用(获取)Bean时创建对象,并初始化。
1 @Lazy
2 @Bean
3 public Person person(){
4 System.out.println("给容器添加Person...");
5 return new Person("zs",18);
6 }
(6)@Conditional
@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。
@Conditional注解不仅可以添加到类上,也可以添加到方法上。
①编写条件类
1 public class LinuxCondition implements Condition {
2
3 //ConditionContext:判断条件能使用的上下文(环境)
4 //AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
5
6 @Override
7 public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
8 // 判断操作系统是否是Linux系统
9
10 // 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
11 ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
12 // 2. 获取到类加载器
13 ClassLoader classLoader = conditionContext.getClassLoader();
14 // 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
15 Environment environment = conditionContext.getEnvironment();
16 // 4. 获取到bean定义的注册类
17 BeanDefinitionRegistry registry = conditionContext.getRegistry();
18
19 String property = environment.getProperty("os.name");
20 if (property.contains("linux")) {
21 return true;
22 }
23
24 return false;
25 }
26 }
1 public class WindowsCondition implements Condition {
2
3 @Override
4 public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
5 Environment environment = conditionContext.getEnvironment();
6 String property = environment.getProperty("os.name");
7 if (property.contains("Windows")) {
8 return true;
9 }
10 return false;
11 }
12 }
②在配置类中添加@Conditional注解
1 @Conditional({WindowsCondition.class})
2 @Bean("window")
3 public Person person02(){
4 return new Person("Windows",25);
5 }
6 @Conditional({LinuxCondition.class})
7 @Bean("linux")
8 public Person person03(){
9 return new Person("Linux",55);
10 }
③测试
1 @Test
2 public void test(){
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
4 String[] definitionNames = applicationContext.getBeanDefinitionNames();
5 for (String name : definitionNames) {
6 System.out.println(name);
7 }
8 }
(7)@Import
向Spring容器中注册bean通常有以下几种方式:
-
包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component),但这种方式比较有局限性,局限于我们自己写的类
-
@Bean注解,通常用于导入第三方包中的组件
-
@Import注解,快速向Spring容器中导入一个组件
@Import注解提供了@Bean注解的功能,@Import注解只允许放到类上面,不允许放到方法上。
简单示例:
1 public class Color {}
1 @Configuration
2 @Import(Color.class)
3 public class MainConfig2 {}
(8)@Import—使用ImportSelector
通过实现ImportSelector接口的类,可以实现批量导入的功能。返回需要导入的组件的全类名数组。
1 //自定义逻辑,返回需要导入的组件
2 public class MyImportSelector implements ImportSelector {
3
4 //返回值就是要导入到容器中的组件全类名
5 //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
6 @Override
7 public String[] selectImports(AnnotationMetadata annotationMetadata) {
8 return new String[]{"com.my.bean.Blue","com.my.bean.Green"};
9 }
10 }
1 @Configuration
2 @Import({Color.class, MyImportSelector.class})
3 public class MainConfig2 {}
(9)@Import—使用ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过该方法,我们可以向Spring容器中注册bean实例。
1 public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
2
3 //AnnotationMetadata:当前类的注解信息
4 //BeanDefinitionRegistry:BeanDefinition注册类
5 //把所有需要添加到容器中的bean;调用BeanDefinitionRegistry.registerBeanDefinition()手工注册进来
6
7 @Override
8 public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
9 boolean definition = registry.containsBeanDefinition("com.my.bean.Green");
10 boolean definition2 = registry.containsBeanDefinition("com.my.bean.Blue");
11 if(definition && definition2){
12 //指定Bean的定义信息
13 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Color.class);
14 //注册Bean,指定bean名
15 registry.registerBeanDefinition("color",rootBeanDefinition);
16 }
17 }
18 }
1 @Configuration
2 @Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
3 public class MainConfig2 {}
(10)FactoryBean
用户可以通过实现FactoryBean接口定制实例化bean的逻辑。
1 //创建一个Spring定义的FactoryBean
2 public class ColorFactoryBean implements FactoryBean<Color> {
3 //返回一个Color对象,这个对象会添加到容器中
4 @Override
5 public Color getObject() throws Exception {
6 return new Color();
7 }
8
9 @Override
10 public Class<?> getObjectType() {
11 return Color.class;
12 }
13 //是否为单例
14 @Override
15 public boolean isSingleton() {
16 return true;
17 }
18 }
1 //配置类
2 @Bean
3 public ColorFactoryBean colorFactoryBean(){
4 return new ColorFactoryBean();
5 }
1 @Test
2 public void test05(){
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
4 Object bean = applicationContext.getBean("colorFactoryBean");
5 Object bean2 = applicationContext.getBean("&colorFactoryBean");
6 System.out.println("bean类型:" + bean.getClass());
7 System.out.println("bean2类型:" + bean2.getClass());
8 }
9 //输出结果:
10 //bean类型:class com.my.bean.Color
11 //bean2类型:class com.my.bean.ColorFactoryBean
注意:
当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
2、Bean生命周期
通常意义上讲的bean的生命周期,指的是bean从创建到初始化,经过一系列的流程,最终销毁的过程。只不过,在Spring中,bean的生命周期是由Spring容器来管理的。在Spring中,我们可以自己来指定bean的初始化和销毁的方法。我们指定了bean的初始化和销毁方法之后,当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。
(1)指定初始方法和销毁方法
@Bean注解中的initMethod指定初始化方法,destroyMethod指定销毁方法。
对象创建完成,如果对象中存在一些属性,并且这些属性也都赋好值之后,那么就会调用bean的初始化方法。
①构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
②销毁
单实例:容器关闭的时候
多实例:容器不会管理这个bean,容器不会调用销毁方法
1 public class Car {
2 public Car() {
3 System.out.println("car constructor...");
4 }
5
6 public void init() {
7 System.out.println("car ... init...");
8 }
9
10 public void destroy() {
11 System.out.println("car ... destroy...");
12 }
13 }
1 @Bean(initMethod = "init",destroyMethod = "destroy")
2 public Car car(){
3 return new Car();
4 }
(2)InitializingBean和DisposableBean接口
Spring中提供了一个InitializingBean接口,该接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。
Spring为bean提供了两种初始化的方式,第一种方式是实现InitializingBean接口(也就是要实现该接口中的afterPropertiesSet方法),第二种方式是在配置文件或@Bean注解中通过init-method来指定,这两种方式可以同时使用,同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。
实现了DisposableBean接口的bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法。
1 public class Car implements InitializingBean, DisposableBean {
2 public Car() {
3 System.out.println("car constructor...");
4 }
5
6 @Override
7 public void afterPropertiesSet() throws Exception {
8 System.out.println("car ... afterPropertiesSet...");
9 }
10
11 @Override
12 public void destroy() throws Exception {
13 System.out.println("car ... destroy...");
14 }
15 }
1 @Bean
2 public Car car(){
3 return new Car();
4 }
(3)@PostConstruct和@PreDestroy注解
@PostConstruct和@PreDestroy注解是Java中的注解,并不是Spring提供的注解。
1 @Component
2 public class Dog {
3 public Dog() {
4 System.out.println("dog constructor...");
5 }
6
7 // 在对象创建完成并且属性赋值完成之后调用
8 @PostConstruct
9 public void init() {
10 System.out.println("dog...@PostConstruct...");
11 }
12
13 // 在容器销毁(移除)对象之前调用
14 @PreDestroy
15 public void destory() {
16 System.out.println("dog...@PreDestroy...");
17 }
18 }
(4)BeanPostProcessor后置处理器
BeanPostProcessor是一个接口,其中有两个方法,即postProcessBeforeInitialization和postProcessAfterInitialization这两个方法,这两个方法分别是在Spring容器中的bean初始化前后执行。
postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。
1 @Component
2 public class MyBeanPostProcessor implements BeanPostProcessor {
3
4 @Override
5 public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
6 System.out.println("postProcessBeforeInitialization..." + s + "=>" + o);
7 return o;
8 }
9
10 @Override
11 public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
12 System.out.println("postProcessAfterInitialization..." + s + "=>" + o);
13 return o;
14 }
15 }
(5)BeanPostProcessor原理
可以将关键代码的调用过程使用如下伪代码表述出来。
1 populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值
2 initializeBean(beanName, exposedObject, mbd)
3 {
4 applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
5 invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化
6 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
7 }
在Spring中,调用initializeBean()方法之前,调用了populateBean()方法为bean的属性赋值,为bean的属性赋好值之后,再调用initializeBean()方法进行初始化。
在initializeBean()中,调用自定义的初始化方法(即invokeInitMethods())之前,调用了applyBeanPostProcessorsBeforeInitialization()方法,而在调用自定义的初始化方法之后,又调用了applyBeanPostProcessorsAfterInitialization()方法。至此,整个bean的初始化过程就这样结束了。
(6)BeanPostProcessor在Spring底层的使用
①ApplicationContextAwareProcessor类
org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一个实现类,这个类的作用是可以向组件中注入IOC容器。
②BeanValidationPostProcessor类
org.springframework.validation.beanvalidation.BeanValidationPostProcessor类主要是用来为bean进行校验操作的,当我们创建bean,并为bean赋值后,我们可以通过BeanValidationPostProcessor类为bean进行校验操作。
③InitDestroyAnnotationBeanPostProcessor类
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor类主要用来处理@PostConstruct注解和@PreDestroy注解。
④AutowiredAnnotationBeanPostProcessor类
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类主要是用于处理标注了@Autowired注解的变量或方法。
3、属性赋值
(1)@Value
Spring中的@Value注解可以为bean中的属性赋值。
@Value注解可以标注在字段、方法、参数以及注解上,而且在程序运行期间生效。
(2)不通过配置文件注入属性的情况
-
注入普通字符串
@Value("zs")
private String name; // 注入普通字符串 -
注入SpEL表达式结果
@Value("#{20-2}")
private Integer age; -
注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName; // 注入操作系统属性 -
注入其他bean中属性的值
@Value("#{person.name}")
private String username; // 注入其他bean中属性的值,即注入person对象的name属性中的值 -
注入文件资源
@Value("classpath:/config.properties")
private Resource resourceFile; // 注入文件资源 -
注入URL资源
@Value("http://www.baidu.com")
private Resource url; // 注入URL资源
(3)通过配置文件注入属性的情况
①在项目src/main/resources目录下新建一个属性文件,例如person.properties。
person.nickName=zhangsan
②在配置类中引入properties文件
1 //使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中,加载完外部配置文件,通过${}取出配置文件中的值
2 @PropertySource(value = {"classpath:/person.properties"})
3 @Configuration
4 public class MainConfigOfPropertyValue {
5 @Bean
6 public Person person(){
7 return new Person();
8 }
9 }
③加载完外部的配置文件以后,接着我们就可以使用${key}取出配置文件中key所对应的值,并将其注入到bean的属性中
1 @Value("${person.nickName}")
2 private String nickName;
(4)#{}和${}区别
-
#{···}:用于执行SpEl表达式,并将内容赋值给属性
-
${···}:主要用于加载外部属性文件中的值
-
${···}和#{···}可以混合使用,但是必须#{}在外面,${}在里面
4、自动装配
(1)@Autowired
@Autowired注解可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。@Autowired注解可以放在类、接口以及方法上。
-
@Autowired注解默认是优先按照类型去容器中找对应的组件,相当于是调用了如下这个方法:
applicationContext.getBean(类名.class);
若找到则就赋值。
-
如果找到多个相同类型的组件,那么是将属性名称作为组件的id,到IOC容器中进行查找,这时就相当于是调用了如下这个方法:
applicationContext.getBean("组件的id");
如果要自动装配的类没有进行注册,默认情况下会报异常。如果希望其找不到时置为null,可以设置@Autowired注解的属性required为false。
1 @Autowired(required = false)
2 private BookDao bookDao;
(2)@Qualifier
@Autowired是先根据类型进行自动装配的,存在多个再通过属性名进行匹配。如果需要按指定的名称进行装配,则可以通过@Qualifier注解来实现。
1 @Qualifier("bookDao2")
2 @Autowired
3 private BookDao bookDao;
4 //注册了两个BookDao:bookDao、bookDao2
5 //默认情况下是匹配bookDao
6 //由于@Qualifier注解进行了指定,所以匹配bookDao2
(3)@Primary
在Spring中使用注解时,常常会使用到@Autowired这个注解,它默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口而言,可能会有几种不同的实现类,而在默认只会采取其中一种实现的情况下,就可以使用@Primary注解来标注优先使用哪一个实现类。
如果使用了@Qualifier注解,无论是否使用了@Primary注解,都会装配@Qualifier注解标注的对象。
(4)@Resource
@Resource注解是Java规范里面的,也可以说它是JSR250规范里面定义的一个注解。该注解默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,那么默认取字段名将其作为组件的名称在IOC容器中进行查找,如果注解写在setter方法上,那么默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。
-
不支持@Primary注解的功能
-
不支持@Autowired(required = false)功能
1 @Resource(name = "bookDao2")
2 private BookDao bookDao;
(5)@Inject
@Inject注解也是Java规范里面的,也可以说它是JSR330规范里面定义的一个注解。该注解默认是根据参数名去寻找bean注入。
①导入javax.inject包
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
②使用
1 @Inject
2 private BookDao bookDao;
-
支持@Primary注解优先注入
-
不支持@Autowired(required = false)功能
(6)实现方法、构造器位置的自动装配
①标注在实例方法上
当@Autowired注解标注在方法上时,Spring容器在创建当前对象的时候,就会调用相应的方法为对象赋值。如果标注的方法存在参数时,那么方法使用的参数和自定义类型的值,需要从IOC容器中获取。
1 @Autowired
2 //标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值
3 //方法使用的参数,自定义类型的值从IOC容器中获取
4 public void setCar(Car car) {
5 this.car = car;
6 }
7 public void setCar(@Autowired Car car) {
8 this.car = car;
9 }
1 //@Bean+参数:参数从容器中获取,可以省略@Autowired
2 @Bean
3 public Boss boss(Car car){
4 Boss boss = new Boss();
5 boss.setCar(car);
6 return boss;
7 }
②标注在构造方法上
使用@Autowired注解标注在构造方法上时,构造方法中的参数对象也是从IOC容器中获取的。
使用@Autowired注解标注在构造方法上时,如果组件中只有一个有参构造方法,那么这个有参构造方法上的@Autowired注解可以省略,并且参数位置的组件还是可以自动从IOC容器中获取。
1 public Car getCar() {
2 return car;
3 }
4
5 @Autowired
6 public Boss(Car car) {
7 this.car = car;
8 System.out.println("Boss...有参构造器");
9 }
10 public Boss(Car car) {
11 this.car = car;
12 System.out.println("Boss...有参构造器");
13 }
(7)注入Spring底层的组件
自定义的组件要想使用Spring容器底层的一些组件,比如ApplicationContext(IOC容器)、底层的BeanFactory等等,那么只需要让自定义组件实现XxxAware接口即可。此时,Spring在创建对象的时候,会调用XxxAware接口中定义的方法注入相关的组件。
XxxAware接口的底层原理是由XxxAwareProcessor实现类实现的,也就是说每一个XxxAware接口都有它自己对应的XxxAwareProcessor实现类。
1 @Component
2 public class Color implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
3
4 @Override
5 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
6 System.out.println("传入的IOC:" + applicationContext);
7 }
8
9
10 @Override
11 public void setBeanName(String s) {
12 System.out.println("当前bean的名字:" + s);
13 }
14
15 @Override
16 public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
17 String resolveStringValue = stringValueResolver.resolveStringValue("你好,${os.name},我的年龄是#{20*18}");
18 System.out.println("解析的字符串:" + resolveStringValue);
19 }
20 }
(8)@Profile
在容器中如果存在同一类型的多个组件,那么可以使用@Profile注解标识要获取的是哪一个bean。也可以说@Profile注解是Spring为我们提供的可以根据当前环境,动态地激活和切换一系列组件的功能。这个功能在不同的环境使用不同的变量的情景下特别有用,例如,在开发环境、测试环境、生产环境中使用不同的数据源,在不改变代码的情况下,可以使用这个注解来动态地切换要连接的数据库。
-
@Profile注解不仅可以标注在方法上,也可以标注在配置类上
-
写了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中。默认是default环境
-
如果@Profile注解标注在配置类上,那么只有是在指定的环境的时候,整个配置类里面的所有配置才会生效
-
如果一个bean上没有使用@Profile注解进行标注,那么这个bean在任何环境下都会被注册到IOC容器中,当然了,前提是在整个配置类生效的情况下
1 @Configuration
2 public class MainConfigOfProfile {
3
4 @Profile("test")
5 @Bean
6 public Blue blue(){
7 return new Blue();
8 }
9
10 @Profile("dev")
11 @Bean
12 public Green green(){
13 return new Green();
14 }
15
16 @Bean
17 public Red red(){
18 return new Red();
19 }
20 }
1 @Test
2 public void test(){
3 // 1. 使用无参构造器创建一个IOC容器
4 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
5 // 2. 在我们容器还没启动创建其他bean之前,先设置需要激活的环境(可以设置激活多个环境哟)
6 applicationContext.getEnvironment().setActiveProfiles("dev");
7 // 3. 注册主配置类
8 applicationContext.register(MainConfigOfProfile.class);
9 // 4. 启动刷新容器
10 applicationContext.refresh();
11
12 String[] definitionNames = applicationContext.getBeanDefinitionNames();
13 for (String name : definitionNames) {
14 System.out.println(name);
15 }
16
17 // 关闭容器
18 applicationContext.close();
19 }
5、AOP
(1)AOP简介
AOP(Aspect Orient Programming),直译过来就是面向切面编程。AOP是指在程序的运行期间动态地将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。
案例:
①导入AOP依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
②定义目标类(业务逻辑类)
1 public class MathCalculator {
2
3 public int div(int i, int j) {
4 System.out.println("MathCalculator...div...");
5 return i / j;
6 }
7
8 }
③定义切面类
通过@Aspect注解进行标识。
AOP中的通知方法及其对应的注解与含义如下:
-
前置通知(对应的注解是@Before):在目标方法运行之前运行
-
后置通知(对应的注解是@After):在目标方法运行结束之后运行,无论目标方法是正常结束还是异常结束都会执行
-
返回通知(对应的注解是@AfterReturning):在目标方法正常返回之后运行
-
异常通知(对应的注解是@AfterThrowing):在目标方法运行出现异常之后运行
-
环绕通知(对应的注解是@Around):动态代理,我们可以直接手动推进目标方法运行(joinPoint.procced())
注意:
可以在参数中添加上JoinPoint,通过该参数获取目标方法信息
JoinPoint参数一定要放在参数列表的第一位,否则Spring是无法识别的
1 //切面类
2 @Aspect
3 public class LogAspects {
4
5 // 如果切入点表达式都一样的情况下,那么我们可以抽取出一个公共的切入点表达式
6 @Pointcut("execution(public int com.my.aop.MathCalculator.*(..))")
7 public void pointCut() {}
8
9 @Before("pointCut()")
10 public void logStart(JoinPoint joinPoint) {
11 Object[] args = joinPoint.getArgs();
12 System.out.println(joinPoint.getSignature().getName() + "运行......@Before,参数列表是:{"+ Arrays.asList(args) +"}");
13 }
14
15 //如果切入点表达式在外部类,则需要写上包路径
16 @After("com.my.aop.LogAspects.pointCut()")
17 public void logEnd(JoinPoint joinPoint) {
18 System.out.println(joinPoint.getSignature().getName() + "结束......@After");
19 }
20
21 @AfterReturning(value = "pointCut()",returning = "returns")
22 public void logReturn(JoinPoint joinPoint,Object returns) {
23 System.out.println(joinPoint.getSignature().getName() + "正常返回......@AfterReturning,运行结果是:{"+ returns +"}");
24 }
25
26 @AfterThrowing(value = "pointCut()",throwing = "e")
27 public void logException(JoinPoint joinPoint,Exception e) {
28 System.out.println(joinPoint.getSignature().getName() + "出现异常......异常信息:{"+ e +"}");
29 }
30
31 }
④将目标类和切面类加入到IOC容器
使用@EnableAspectJAutoProxy注解开启基于注解的AOP模式。
1 @EnableAspectJAutoProxy
2 @Configuration
3 public class MainConfigOfAop {
4
5 @Bean
6 public MathCalculator calculator(){
7 return new MathCalculator();
8 }
9
10 @Bean
11 public LogAspects logAspects(){
12 return new LogAspects();
13 }
14 }
⑤测试
1 @Test
2 public void test(){
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAop.class);
4 MathCalculator bean = applicationContext.getBean(MathCalculator.class);
5 bean.div(1,1);
6 applicationContext.close();
7 }
(2)@EnableAspectJAutoProxy分析
向Spring的配置类上添加@EnableAspectJAutoProxy注解之后,会向IOC容器中注册AnnotationAwareAspectJAutoProxyCreator,翻译过来就叫注解装配模式的AspectJ切面自动代理创建器。
AnnotationAwareAspectJAutoProxyCreator
->AspectJAwareAdvisorAutoProxyCreator(父类)
->AbstractAdvisorAutoProxyCreator(父类)
->AbstractAutoProxyCreator(父类)
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware(两个接口)
AnnotationAwareAspectJAutoProxyCreator类的结构图:
(3)分析创建和注册AnnotationAwareAspectJAutoProxyCreator的过程
①传入配置类
②注册配置类,调用refresh()刷新容器
③registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建
1)、先获取IOC容器已经定义了的需要创建对象的所有BeanPostProcessor
2)、给容器中加别的BeanPostProcessor
3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor
4)、再给容器中注册实现了Ordered接口的BeanPostProcessor
5)、注册实现优先级接口的BeanPostProcessor
6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中
创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
1、创建Bean的实例
2、populateBean;给bean的各种属性赋值
3、initializeBean;初始化bean
(1)invokeAwareMethods();处理Aware接口的方法回调
(2)applyBeanPostProcessorsBeforeInitialization();应用后置处理器的
PostProcessorsBeforeInitialization()
(3)invokeInitMethods();执行自定义的初始化方法
(4)applyBeanPostProcessorsAfterInitialization();执行后置处理器的PostProcessorsAfterInitialization()
4、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator);创建成功—>aspectJAdvisorsBuilder
7)、把BeanPostProcessor注册到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
(4)完成BeanFactory的初始化工作
④finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作,创建剩下的单实例bean
1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
getBean() —> doGetBean() —> getSingleton()
2)、创建bean
【AnnotationAwareAspectJAutoProxyCreator 在所有bean创建之前会有一个拦截,因为它是InstantiationAwareBeanPostProcessor类型,会先调用postProcessorBeforeInstantiation()】
1、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接去使用,否则再创建
只要创建好的Bean都会被缓存起来
2、createBean();创建bean
【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
(1)、resolveBeforeInstantiation(beanName,mbdToUse);解析BeforeInstantiation()
希望后置处理器在此能返回一个代理对象;如果能返回对象就使用,如果不能就继续往下执行
一、后置处理器先尝试返回对象;
bean=applyBeanPostProcessorsBeforeInstantiation();拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行 postProcessorBeforeInstantiation()
if(bean!=null){
bean=applyBeanPostProcessoresAfterInitialization()
}
(2)、doCreateBean(beanName,mbdToUser,args);真正的去创建一个bean实例;和3.6流程一样
(5)创建AOP代理
AnnotationAwareAspectJAutoProxyCreator 【InstantiationAwareBeanPostProcessor】作用:
①每一个bean创建之前,调用postProcessorBeforeInstantiation();
1)、判断当前bean是否在advisedBean中(保存了所有需要增强的bean)
2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
3)、判断是否需要跳过
1、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdivisors】
每一个封装的通知方法的增强器是InstantiationModelAwarePointcutAdvisor;
判断每一个增强器是否是AspectJPointcutAdvisor类型的,是则返回为true
2、返回false
②创建对象
调用postProcessorBeforeInstantiation() —>
return wrapIfNecessary(bean,beanName,cacheKey);//如果有必要则包装
1)、获取当前bean的所有增强器(通知方法)
1、找到候选的所有的增强器
2、获取到能在bean使用的增强器(找哪些通知方法是需要切入到当前bean方法的)
3、给增强器排序
4、封装成Object[] specificInterceptors
2)、保存当前bean在advisedBeans中
3)、如果当前bean需要增强,创建当前bean的代理对象
1、获取所有增强器(通知方法)
2、保存到proxyFactory
3、创建代理对象:Spring自行决定
JdkDynamicAopProxy(config);jdk代理
ObjenesisCglibAopProxy(config);cglib代理
4)、给容器中返回当前组件使用cglib增强了的代理对象
5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程
(6)目标方法执行
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象等)。
①CglibAopProxy.intercept();拦截目标方法的执行
②根据ProxyFactory对象获取将要执行的目标方法拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
1)、List<Object> interceptorList保存所有拦截器(5)
一个默认的ExposeInvocationInterceptor和4个增强器
2)、遍历所有的增强器,将其转为Interceptor;
registry.getInterceptors(advisor);
3)、将增强器转为List<MethodInterceptor>;
如果是MethodInterceptor,直接加入集合中
如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor
转换完成返回MethodInterceptor数组
③如果没有拦截器链,直接执行目标方法
拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
④如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入并创建一个CglibMethodInvocation对象,并调用Object retval = mi.proceed();
(7)拦截器链的执行过程
①如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法
②链式获取每一个拦截器,拦截器执行invoke()方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行
拦截器链的机制,保证通知方法与目标方法的执行顺序
(8)AOP原理总结
6、声明式事务
(1)搭建环境
①导入依赖
<!--c3p0数据源-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--MySQL数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
<!--spring-jdbc模块-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
②配置类
1 @ComponentScan("com.my.tx")
2 @Configuration
3 public class TxConfig {
4
5 // 注册c3p0数据源
6 @Bean
7 public DataSource dataSource() throws Exception {
8 ComboPooledDataSource dataSource = new ComboPooledDataSource();
9 dataSource.setUser("root");
10 dataSource.setPassword("12345678");
11 dataSource.setDriverClass("com.mysql.jdbc.Driver");
12 dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
13 return dataSource;
14 }
15
16 @Bean
17 public JdbcTemplate jdbcTemplate() throws Exception {
18 //Spring对@Configuration的文件会进行特殊处理,配置类中多次调用同一方法,并不是重新执行方法,而是从容器中找组件
19 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
20 return jdbcTemplate;
21 }
22
23 }
③开发Dao层
1 @Repository
2 public class UserDao {
3
4 @Autowired
5 private JdbcTemplate jdbcTemplate;
6
7 public void insert() {
8 String sql = "insert into `users`(username, pwd) values(?, ?)";
9 String username = UUID.randomUUID().toString().substring(0, 5);
10 jdbcTemplate.update(sql, username, "123456"); // 增删改都来调用这个方法
11 }
12 }
④开发Service层
1 @Service
2 public class UserService {
3
4 @Autowired
5 private UserDao userDao;
6
7 public void insertUser() {
8 userDao.insert();
9 System.out.println("插入完成...");
10 }
11 }
(2)开启事务
①给相应方法标注@Transactional,表示当前方法是事务方法
1 @Transactional
2 public void insertUser() {}
②开启基于注解的事务管理功能
1 @EnableTransactionManagement
2 @ComponentScan("com.my.tx")
3 @Configuration
4 public class TxConfig {}
③配置事务管理器来控制事务
1 @Bean
2 public PlatformTransactionManager transactionManager() throws Exception {
3 return new DataSourceTransactionManager(dataSource());
4 }
(3)声明式事务原理的源码分析
①@EnableTransactionManagement
利用TransactionManagementConfigurationSelector向容器中导入组件
导入两个组件:
i.AutoProxyRegistrar
ii.ProxyTransactionManagementConfiguration
②AutoProxyRegistrar:给容器中注册一个InfrastructureAdvisorAutoProxyCreator组件
InfrastructureAdvisorAutoProxyCreator:利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行执行
③ProxyTransactionManagementConfiguration
1)、给容器中注册事务增强器
(1)、事务增强器要用到事务注解的信息,通过AnnotationTransactionAttributeSource解析事务注解
(2)、事务拦截器:TransactionInterceptor:保存了事务属性信息,事务管理器,是一个MethodInterceptor
在目标方法执行的时候,执行拦截器链
事务拦截器执行流程:
1、先获取事务相关属性
2、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionManager,最终会从容器中按照类型获取一个TransactionManager
3、执行目标方法
如果异常,获取到事务管理器,利用事务管理回滚操作
如果正常,利用事务管理器,提交事务
7、扩展原理
(1)BeanFactoryPostProcessor
BeanFactoryPostProcessor其实就是BeanFactory(创建bean的工厂)的后置处理器。
BeanFactoryPostProcessor的调用时机是在BeanFactory标准初始化之后,这样一来,我们就可以来定制和修改BeanFactory里面的一些内容了,此时,所有的bean定义已经保存加载到BeanFactory中了,但是bean的实例还未创建。
调用步骤:
①IOC容器创建对象
②invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor
如何找到所有的BeanFactoryPostProcessor并执行他们的方法:
1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
2)、在初始化创建其他组件前面执行
(2)BeanDefinitionRegistryPostProcessor
继承了BeanFactoryPostProcessor。
调用时机是在所有bean定义信息将要被加载,bean实例还未创建。
在BeanFactoryPostProcessor前面执行的。
利用BeanDefinitionRegistryPostProcessor给容器再额外添加一些组件。
调用过程:
①IOC创建对象
②refresh() —> invokeBeanFactoryPostProcessors(beanFactory);
③从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件
1)、依次触发所有的postProcessBeanDefinitionRegistry()
2)、再来触发postProcessBeanFactory()方法
④再从容器中找到BeanFactoryPostProcessor组件,然后依次触发postProcessBeanFactory()方法
(3)ApplicationListener
ApplicationListener的作用主要是来监听IOC容器中发布的一些事件(ApplicationEvent及其子类),只要事件发生便会来触发该监听器的回调,从而来完成事件驱动模型的开发。
用法:
首先编写监听器,把监听器加入到容器中。
1 @Component
2 public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
3
4 // 当容器中发布此事件以后,下面这个方法就会被触发
5 @Override
6 public void onApplicationEvent(ApplicationEvent applicationEvent) {
7 System.out.println("收到事件:" + applicationEvent);
8 }
9 }
其次,可以在测试时发布一个事件。
1 @Test
2 public void test2(){
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
4
5 applicationContext.publishEvent(new ApplicationEvent(new String("发布的事件")) {
6 });
7
8 // 关闭容器
9 applicationContext.close();
10 }
11 /*
12 收到三个事件:
13 ContextRefreshedEvent:容器刷新完成事件。
14 IOCTest_Ext$1[source=发布的事件]:自己发布的事件。
15 ContextClosedEvent:容器关闭事件。
16 */
源码分析:
分析ContextRefreshedEvent事件:
①容器创建对象;调用refresh();
②refresh()中调用finishRefresh();容器刷新完成
③finishRefresh()中调用以下语句,发布事件
publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)))
④事件发布流程
分析ContextClosedEvent事件:
①容器关闭时,applicationContext.close();
②close()方法中调用doClose();
③doClose()中调用以下语句,发布事件
publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
【事件发布流程】:
1)、获取事件多播器(派发器):getApplicationEventMulticaster()
2)、multicastEvent()派发事件
1、获取到所有的ApplicationListener,并进行遍历
2、如果有Executor,可以支持使用Executor进行异步派发
Executor executor = this.getTaskExecutor();
3、否则,同步的方式执行listener方法:invokeListener(listener, event);
拿到listener回调onApplicationEvent方法
【事件多播器(派发器)】:
①容器创建对象,refres();
②refres()中调用initApplicationEventMulticaster();初始化ApplicationEventMulticaster
1)、先去容器中找有没有“id=applicationEventMulticaster”组件
2)、有则直接获取;如果没有则创建
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
并且加入到容器中,我们就可以在其他组件要派发事件时,自动注入这个applicationEventMulticaster
【容器中有哪些监听器】
①容器创建对象,refres();
②refres()中调用registerListeners();注册监听器
1)、从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中
String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
2)、将listener注册到ApplicationEventMulticaster中
this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
(4)@EventListener
我们只需要简单地给该方法上标注一个@EventListener注解,就可以让它来监听事件了。可以通过@EventListener注解中的classes属性来指定要监听哪些事件。
1 @Service
2 public class UserService {
3
4 @EventListener(classes= ApplicationEvent.class)
5 public void listen() {
6 System.out.println("UserService...");
7 }
8 }
原理:
使用EventListenerMethodProcessor处理器来解析方法上的@EventListener。
8、Spring容器创建过程
(1)BeanFactory预准备
Spring容器的refresh()【创建刷新】;
①prepareRefresh( )刷新前的预处理;
1)、initPropertySources( )初始化一些属性设置;子类自定义个性化的属性设置方法
2)、getEnvironment( ). validateRequiredProperties( ) ;获取其环境变量,然后校验属性的合法性
3)、earlyApplicationEvents= new LinkedHashSet < ApplicationEvent >() ;保存容器中的一些早期的事件;【允许收集早期的容器事件,等待事件派发器可用之后,即可进行发布】
②obtainFreshBeanFactory() ;获取BeanFactory;
1)、refreshBeanFactory() ;刷新【创建】 BeanFactory;
创建了一个this.beanFactory = new DefaultListableBeanFactory();
设置id;
2)、getBeanFactory( ) ;返回刚才GenericApplicationContext创建的BeanFactory对象;
3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;
③prepareBeanF actory(beanFactory) ; BeanFactory的预准备工作(BeanFactory进行一些设置) ;
1)、设置BeanFactory的类加载器、支持表达式解析器...
2)、添加部分BeanPostProcessor 【Applicat ionContextAwareProcessor】
3)、设置忽略的自动装配的接口【作用就是,这些接口的实现类不能通过接口类型来自动注入】EnvironmentAware、EmbeddedValueResolverAware、XXX;
4)、注册可以解析的自动装配;我们能直接在任何组件中自动注入:
BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
5)、添加BeanPostProcessor 【ApplicationListenerDetector】
6)、添加编译时的AspectJ;
7)、给BeanFactory中注册一些能用的组件;
environment【ConfigurableEnvironment】、
systemProperties【Map<String, Object>】、
systemEnvironment【Map<String,Object>】
④postProcessBeanFactory( beanFactory ); BeanFactory准备工作完成后进行的后置处理工作;
1)、子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
(2)执行BeanFactoryPostProcessor
⑤invokeBeanFactoryPostProcessors( beanFactory ) ;执行BeanFactoryPostProcessor;
BeanFactoryPostProcessor: BeanFactory的后置处理器。在BeanF actory标准初始化之后执行的;
两个接口: BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
1)、执行BeanFactoryPostProcessor的方法;
先执行BeanDefinitionRegistryPostProcessor的方法
1、获取所有的BeanDefinitionRegistryPostProcessor;
2、先执行实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor.
postProcessor . postProcessBeanDefinitionRegistry(registry);
3、再执行实现了Ordered顺序接口的BeanDefinitionRegi stryPostProcessor;
postProcessor . postProcessBeanDefinitionRegistry(registry);
4、最后执行没有实现任何优先级或者是顺序接口的BeanDefinitionRegistryPostProcessors;
postProcessor . postProcessBeanDefinitionRegistry(registry);
再执行BeanFactoryPostProcessor的方法
1、获取所有的BeanFactoryPostProcessor;
2、先执行实现了PriorityOrdered优先级接口的BeanFactoryPostProcessor.
postProcessor. postProcessBeanFactory( )
3、再执行实现了Ordered顺序接口的BeanFactoryPostProcessor;
postProcessor. postProcessBeanFactory( )
4、最后执行没有实现任何优先级或者是顺序接口的BeanFactoryPostProcessor;
postProcessor. postProcessBeanFactory( )
(3)注册BeanPostProcessor
⑥registerBeanPostProcessors (beanFactory) ;注册BeanPostProcessor (Bean的后置处理器)【作用是拦截bean的创建过程】
不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的
-
BeanPostProcessor
-
DestructionAwareBeanPostProcessor
-
Instantiat ionAwareBeanPostProcessor
-
SmartInstantiationAwareBeanPostProcessor
-
MergedBeanDefinitionPostProcessor【internalPostProcessors】
1)、获取所有的BeanPostProcessor ;
后置处理器都默认可以通过PriorityOrdered、ordered接口来执行优先级
2)、先注册PriorityOrdered优先级接口的BeanPostProcessor;
把每一个BeanPostProcessor;添加到BeanFactory中
beanFactory.addBeanPostProcesson( postProcessor);
3)、再注册Ordered接口的
4)、最后注册没有实现任何优先级接口的
5)、最终注册MergedBeanDefinitionPostProcessor;
6)、注册一个ApplicationListenerDetector; 来在Bean创建完成后检查是否是ApplicationListener,如果是
applicationContext.addApplicationListener( (ApplicationListener<?>) bean);
(4)初始化MessageSource组件
⑦initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析) ;
1)、获取BeanFactory
2)、看容器中是否有id为messageSource的,类型是MessageSource的组件
如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
3)、把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource,通过调用它的getMessage方法获取
beanFactory. registersingleton(MESSAGE_ SOURCE_ BEAN NAME, this . messageSource);
(5)初始化事件派发器、监听器等
⑧initApplicationEventMulticaster( ) ;初始化事件派发器;
1)、获取BeanFactory
2)、从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;
3)、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster
4)、将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入
⑨onRefresh();留给子容器(子类)
1)、子类重写这个方法,在容器刷新的时候可以自定义逻辑;
⑩registerListeners() ;将所有项目里面的ApplicationListener注册进容器中来;
1)、从容器中拿到所有的ApplicationListener
2)、将每个监听器添加到事件派发器中;
getApplicationEventMulticaster(). addApplicationListenerBean(listenerBeanName)
3)、派发之前步骤产生的事件;
(6)初始化所有剩下的单实例bean
⑪finishBeanFactoryInitialization( beanFactory) ;初始化所有剩下的单实例bean;
1)、beanFactory.preInstantiateSingletons( ) ;初始化后剩下的单实例bean
1、获取容器中的所有Bean,依次进行初始化和创建对象
2、获取Bean的定义信息; RootBeanDefinition
3、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(256);
3.缓存中获取不到,开始Bean的创建对象流程;
4.标记当前bean已经被创建
5.获取Bean的定义信息;
6.【获取当前Bean依赖的其他Bean ;如果有按照getBean( )把依赖的Bean先创建出来; 】(getDependsOn())
7.启动单实例Bean的创建流程;
1).createBean( beanName, mbd, args) ;
2).object bean = resolveBeforeInstantiation( beanName, mbdToUse) ;让BeanProcessor先拦截返回代理对象
【InstantiationAwareBeanPostProcessor 】提前执行;
先触发: postProcessBeforeInstantiation();
如果有返回值:触发postProcessAfterInitialization();
3).如果前面的InstantiationAwareBeanPostProcessor没有返回代理对象;调用4)
4).Object beanInstance = doCreateBean( beanName, mbdToUse, args) ;创建Bean
1创建Bean实例; createBeanInstance( beanName, mbd, args);
利用工厂方法或者对象的构造器创建出Bean实例;
2applyMergedBeanDefinitionPostProcessors ( mbd, beanType, beanName);
调用MergedBeanDefinitionPostProcessor
bdp.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, exposed0bject, mbd);
1.【执行Aware接口方法】invokeAwareMethods ( beanName, bean) ;执行xxxAware接口
BeanNameAware \ BeanClassLoaderAware \BeanFactoryAware
2.【执行后置处理器初始化之前】 applyBeanPostProcessorsBeforeInitialization()
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创建完成以后;
检查所有的Bean是否是SmartInitializingsingleton接口的;如果是;就执行afterSingletonsInstantiated()
(7)容器创建完成
⑫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);
(8)总结
9、Servlet3.0
(1)ServletContainerInitializer
①Servlet容器在启动我们的应用的时候,它会来扫描jar包里面的ServletContainerInitializer的实现类
②用法:首先要提供ServletContainerInitializer的一个实现类,还必须得把它绑定在META-INF/services/目录下面的名字叫javax.servlet.ServletContainerInitializer的文件里面
③Servlet容器在启动应用的时候,会扫描当前应用每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的实现类,然后,再运行该实现类中的方法
1 //@HandlesTypes注解里面指定上我们感兴趣的类型,那么Servlet容器在启动的时候就会自动地将该类型(即HelloService接口)下面的子类,包括实现类或者子接口等全部都传递过来
2
3 @HandlesTypes(value={HelloService.class})
4 public class MyServletContainerInitializer implements ServletContainerInitializer {
5
6 /*
7 * 参数:
8 * ServletContext arg1:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,
9 * 我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到
10 *
11 * Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型
12 *
13 */
14 @Override
15 public void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {
16 System.out.println("我们感兴趣的所有类型:");
17 // 好,我们把这些类型来遍历一下
18 for (Class<?> clz : arg0) {
19 System.out.println(clz);
20 }
21 }
22 }
23 //javax.servlet.ServletContainerInitializer
24 com.my.servlet.MyServletContainerInitializer
(2)注册web三大组件
利用基于运行时插件的ServletContainerInitializer机制得到ServletContext对象,然后再往其里面注册组件。在ServletContainerInitializer的onStartup()方法中注册Servlet、Listener和Filter。
1 @Override
2 public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
3 // TODO Auto-generated method stub
4 System.out.println("我们感兴趣的所有类型:");
5 // 好,我们把这些类型来遍历一下
6 for (Class<?> clz : arg0) {
7 System.out.println(clz);
8 }
9
10 // 注册Servlet组件
11 ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
12 // 配置Servlet的映射信息
13 servlet.addMapping("/user");
14
15 // 注册Listener组件
16 sc.addListener(UserListener.class);
17
18 // 注册Filter组件
19 FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
20 // 配置Filter的映射信息
21 //第一个参数是Filter拦截的请求类型,第三个参数是Filter要拦截的路径
22 filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
23 }
(3)Servlet3.0与SpringMVC整合分析
①web容器在启动的时候,会扫描每个jar包下的META-INF /services/javax. servlet . ServletContainerInitializer
②加载这个文件指定的类SpringServletContainerInitializer
③Spring的应用一启动会加载感兴趣的WebApplicationInitializer接口下的所有组件;
④并且为WebApplicat ionInitializer组件创建对象(组件不是接口,不是抽象类)
1)、AbstractContextLoaderInitializer:创建根容器; createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
创建一个web的ioc容器:createServletApplicationContext();
创建了DispatcherServlet:createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中;
3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
创建根容器:createRootApplicationContext( )
getRootConfigClasses();传入一个配置类
创建web的ioc容器:createServletApplicationContext();
获取配置类; getServletConfigClasses();
总结:
以注解方式来启动SpringMVC;继承AbstractAnnotatienConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息;
(4)定制与接管SpringMVC
(5)异步请求
1 // @WebServlet注解表明该Servlet应该处理哪个请求
2 @WebServlet(value="/async", asyncSupported=true)
3 public class HelloAsyncServlet extends HttpServlet {
4
5 @Override
6 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
7 // 1. 先来让该Servlet支持异步处理,即asyncSupported=true
8 // 2. 开启异步模式
9 // 我们可以来打印一下主线程究竟是谁?
10 System.out.println("主线程开始..." + Thread.currentThread());
11 AsyncContext startAsync = req.startAsync();
12 // 3. 调用业务逻辑,进行异步处理,这儿是开始异步处理
13 startAsync.start(new Runnable() {
14
15 @Override
16 public void run() {
17 // TODO Auto-generated method stub
18 try {
19 System.out.println("副线程开始..." + Thread.currentThread());
20 sayHello();
21
22 startAsync.complete();
23 /*
24 * 通过下面这种方式来获取响应对象是不可行的哟!否则,会报如下异常:
25 * java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
26 */
27 // 获取到异步上下文
28 //AsyncContext asyncContext = req.getAsyncContext();
29 // ServletResponse response = asyncContext.getResponse();
30
31 // 4. 获取响应
32 ServletResponse response = startAsync.getResponse();
33 // 然后,我们还是利用这个响应往客户端来写数据
34 response.getWriter().write("hello async...");
35 System.out.println("副线程结束..." + Thread.currentThread());
36 } catch (Exception e) {
37 // TODO Auto-generated catch block
38 e.printStackTrace();
39 }
40 }
41 });
42 System.out.println("主线程结束..." + Thread.currentThread());
43 }
44
45 public void sayHello() throws Exception {
46 // 我们可以来打印一下究竟是哪些线程在工作
47 System.out.println(Thread.currentThread() + " processing...");
48 Thread.sleep(3000); // 睡上3秒
49 }
50
51 }
(6)异步请求处理(返回Callable)
1 /*
2 第一步,控制器返回Callable。其实,这里要说的是控制器中方法的返回值要写成Callable了,而再也不能是以前普通的字符串对象了。
3 第二步,控制器返回Callable以后,Spring MVC就会异步地启动一个处理方法(即Spring MVC异步处理),也即将Callable提交到TaskExecutor(任务执行器)里面,并使用一个隔离的线程进行处理。
4 第三步,与此同时,DispatcherServlet和所有的Filter将会退出Servlet容器的线程(即主线程),但是response仍然保持打开的状态。既然response依旧保持打开状态,那就表明还没有给浏览器以响应,因此我们还能给response里面写数据。
5 第四步,最终,Callable返回一个结果,并且Spring MVC会将请求重新派发给Servlet容器,恢复之前的处理。也就是说,之前的response依旧还保存着打开的状态,仍然还可以往其里面写数据。
6 第五步,如果还是把上一次的请求再发过来,假设上一次的请求是async01,那么DispatcherServlet依旧还是能接收到该请求,收到以后,DispatcherServlet便会再次执行,来恢复之前的处理。
7 */
8 @Controller
9 public class AsyncController {
10
11 @ResponseBody
12 @RequestMapping("/async01")
13 public Callable<String> async01() {
14 Callable<String> callable = new Callable<String>() {
15
16 @Override
17 public String call() throws Exception {
18 // 响应给客户端一串字符串,即"Callable<String> async01()"
19 return "Callable<String> async01()";
20 }
21
22 };
23 return callable;
24 }
25 }