Spring源码中涉及到的基本概念详解

24 篇文章 17 订阅
22 篇文章 11 订阅

Spring简单介绍

可以说spring是目前J2EE架构中最成功的轻量级的J2EE架构,spring发展了很多年了,现在使用已经很广泛了,可以说基本上每一家企业都在使用,包括BAT公司,都在spring架构的基础上做了很多的扩展,这个专题主要记录下spring的一些相关笔记,前段时间看了下spring的容器初始化的过程,发现里面的封装实在是太复杂了,封装了上千个类,如果不理解概念,直接去看源码会直接晕掉,spring最核心的功能就是容器化,它将我们项目中的所有java对象都拉入了容器进行管理,这里要说的是我们普通的java bean对象不一定是Bean 对象,但是Bean对象一定是java bean对象,这个概念要理解清楚,spring中提供的功能实在是太多了,spring源码中将面向接口编程体现的淋漓尽致,其中它将不同职责的功能封装到不同的接口中,如果你的bean工厂继承了它,那么你就应该具有这项功能,所以我们在看spring源码的时候需要把这些接口都要理清楚,那么在后面的源码研究上才不会被绕晕,这个专题主要对spring的容器初始化进行分析,看spring是如何管理我们的bean,如何进行DI的,如何进行AOP代理的,如何扩展spring,spring的设计原则遵循了开闭原则,该封装的职责封装起来,该对外开发的接口,向程序员进行开放,所以我们把spring的源码分析完成过后,一定要达到能够扩展spring,比如像spring boot,mybatis这些都是对spring提供的扩展点进行集成;其中比较重要的扩展点有spring提供的@Import注解进行扩展,BeanFactory的后置处理器,其中aop使用Bean的后置处理器。这些后面都会一一的记录,希望我自己之后也能对spring有很深的理解和能够随意扩展spring。

Bean定义

spring中的Bean定义都有哪几种方式,我这边简单介绍下,spring 的bean定义大概有三种方式:1.xml中定义Bean,使用了2.@Component(@Service,@Controller)3.使用java api的方式,就是通过spring给我们提供的api,通过java api的方式进行定义。但是不管以什么样的方式去定义Bean,最终spring都会解析成BeanDefinition,也就是说你在xml中定义了,那么最终也是解析成一个beanName是xx的BeanDefinition,其他的也一样,用注解的方式也是一样的。

XML定义Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.5.xsd
            http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">


   <bean id="user" class="com.spring.entity.User" init-method="init"/>

</beans>

UserBean

public class User {

   public User(){
      System.out.println("create user ");
   }

   public void init(){
      System.out.println("user init method...");
   }
}

public static void main(String[] args) {
   //AnnotationConfigApplicationContext
   ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
   System.out.println(classPathXmlApplicationContext.getBean("user"));
}

输出:
create user 
user init method...
com.spring.entity.User@28eaa59a

@Bean或者@Component

定义一个配置扫描类

@ComponentScan("com.spring")
public class AppConfig {


   @Bean
   public Person person(){
      return new Person();
   }
}

public class Person {

   public Person(){
      System.out.println("create Person ");
   }
}

那么启动spring容器过后就会生成两个Bean,分表为user和person

public static void main(String[] args) {
   //AnnotationConfigApplicationContext
   //ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
   //System.out.println(classPathXmlApplicationContext.getBean("user"));

   AnnotationConfigApplicationContext act = new AnnotationConfigApplicationContext(AppConfig.class);
   System.out.println(act.getBean("user"));
   System.out.println(act.getBean("person"));

}

输出:
create user
user init method…
create Person
com.spring.entity.User@1990a65e
com.spring.entity.Person@64485a47

我们通过debug也可以看到
在这里插入图片描述
可以到单例池里面已经把person和user放入了容器了

java api的方式

public static void main(String[] args) {

   AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
   AnnotatedBeanDefinitionReader r = new AnnotatedBeanDefinitionReader(annotationConfigApplicationContext);
   r.registerBean(Person.class);
   annotationConfigApplicationContext.refresh();
   System.out.println(annotationConfigApplicationContext.getBean("person"));

}

这是一种方式,就是我们的Persion是一个普通的javaBean,然后通过AnnotatedBeanDefinitionReader 读取器也会将它放入spring的容器中,不用通过注解或者xml的方式

public static void main(String[] args) {

   AbstractBeanDefinition ab = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
   ab.setBeanClass(Person.class);
   ab.setInitMethodName("init");
   ab.setScope("prototype");
   DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
   factory.registerBeanDefinition("person",ab);
   System.out.println(factory.getBean("person"));

}

这种方式是根据BeanDefinition的构建器去构建一个BeanDefinition,然后设置我们的bean参数,最终放入了容器中
上面的DefaultListableBeanFactory是spring的默认的bean工厂,这个bean工厂用的功能最全,具有最全的功能,用它还可以自己往容器里面直接注册单例池,比如:

AbstractBeanDefinition ab = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
ab.setBeanClass(Person.class);
ab.setInitMethodName("init");
ab.setScope("prototype");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("person",ab);
factory.registerSingleton("xx",new Person());
System.out.println(factory.getBean("person"));
System.out.println(factory.getBean("xx"));

BeanDefinitionReader

BeanDefinitionRead我这边理解为Bean的读取器,简单来说就是对BeanDefinition的读取,读取的BeanDefinition有我们自定义要加入容器的bean,有spring自己内部的beanDefinition,分为几类,有AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader,其中AnnotatedBeanDefinitionReader是对@注解的Bean进行读取成BeanDefinition,然后加入DefaultListableBeanFactory中,也就是说比如我们定义了一个AppConfig

@ComponentScan("com.spring")
public class AppConfig {


   @Bean
   public Person person(){
      return new Person();
   }
}

它就负责把Appconfig扫描成一个BeanDefinition,为后续扫描@Component注解做准备,也就是将Appconfig扫描成一个BeanDefinition,然后后面refresh方法的时候会读取这个Bean定义,然后解析@ComponentScan路径,将所有的Bean注册到BeanDefinition中,最后注册到单例池中;
XmlBeanDefinitionReader是处理xml中配置的,将其扫描成BeanDefinition;
AnnotatedBeanDefinitionReader
它主要是处理的以注解的方式启动的spring容器的Bean读取器,它主要处理的注解是@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description,但是这个类也可以通过api接口手动注册Bean,比如:

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory);

reader.register(Person.class);
System.out.println(factory.getBean(Person.class));

XmlBeanDefinitionReader

XmlBeanDefinitionRead主要是读取中bean,然后解析是BeanDefinition,比如看下面的例子:

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
int beanCont = reader.loadBeanDefinitions("spring.xml");
System.out.println(beanCont);

beancount表示加载了多少个BeanDefinition

ClassPathBeanDefinitionScanner

这个并不是BeanDefinitionReader,但是它的作用和BeanDefinitionReader类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition。

BeanFactory

BeanFactory是spring中核心所在,spring容器启动的最重要的过程就有将所有的Bean扫描成一个BeanDefinition,
然后将BeanDefinition中是单例的Bean注册到单例池中,如果是原型的,那么在每次getBean的时候再从beandefinitionMap中拿出来给你创建一个新的对象给你使用,使用完了也就被gc了,而单例的bean不一样,单例的每次会从单例池中获取一个唯一的对象,然后进行使用;spring中最最核心的BeanFactory就是DefaultListableBeanFactory,它具备了BeanFactory的所有功能,包括别名的注册、单例bean的直接注册、父子容器的管理、依赖注入的实现、环境信息的获取和配置等等,我们先来看下DefaultListableBeanFactory的继承关系:
在这里插入图片描述
看上图可知,DefaultListableBeanFactory在整个继承关系的最下层,则说明它具有最全的功能,我们先来一个一个的分析每个接口和类的作用,spring为什么写了这么多接口,它把每种职责都分表定义到指定的类或者接口中,所以如果你了解了这些接口和类的含义,那么打开一个这个继承关系,你一看就知道它具有什么功能了,比如说你的类实现了Aliasregistry,那么我不看你的类实现细节,我就知道你这个Bean是具有多个别名的,不仅仅是spring,你自己在设计的时候也差不多是这样,别人看你的类继承关系从宏观的角度就知道你这个类具有哪些功能;
Aliasregistry:支持别名功能,一个名字可以对应多个别名,在spring的核心工厂DefaultListableBeanFactory中是由SimpleAliasRegistry来实现的,实现的原理很简单,所以我们看到SimpleAliasRegistry实现了Aliasregistry,那么SimpleAliasRegistry就具有注册别名的功能;
BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition,通过继承关系可以知道BeanDefinitionRegistry目前只给了DefaultListableBeanFactory使用,它的功能就是对BeanDefinition进行管理,如果你自己定义的Bean工厂,那么你实现了BeanDefinitionRegistry则证明你的工厂具有BeanDefinition的管理功能;
SingletonBeanRegistry:该SingletonBeanRegistry接口表示可以对单例bean进行直接注册,就是我们程序员可以手动进行注册这个bean到单例池中,也就是说继承了SingletonBeanRegistry就具有手动注册bean到单例池中的功能;
ListableBeanFactory:在BeanFactory的基础上,增加了其他功能,可以获取所有BeanDefinition的beanNames,可以根据某个类型获取对应的beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
HierarchicalBeanFactory:在BeanFactory的基础上,添加了获取父BeanFactory的功能
DefaultSingletonBeanRegistry:它是一个类,实现了SingletonBeanRegistry接口,拥有了直接注册、获取某个单例Bean的功能
ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上,添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
FactoryBeanRegistrySupport:支持了FactoryBean的功能
AutowireCapableBeanFactory:是直接继承了BeanFactory,在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配
AbstractBeanFactory:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
ConfigurableListableBeanFactory:继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
AbstractAutowireCapableBeanFactory:继承了AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
DefaultListableBeanFactory:继承了AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大。

ApplicationContext

在这里插入图片描述
HierarchicalBeanFactory:拥有获取父BeanFactory的功能
ListableBeanFactory:拥有获取beanNames的功能
ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
MessageSource:拥有国际化功能

AnnotationConfigApplicationContext

在这里插入图片描述
ConfigurableApplicationContext:继承了ApplicationContext接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
AbstractApplicationContext:实现了ConfigurableApplicationContext接口
GenericApplicationContext:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,拥有了所有ApplicationContext的功能,并且可以注册BeanDefinition,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
AnnotationConfigRegistry:可以单独注册某个为类为BeanDefinition(可以处理该类上的@Configuration注解,已经可以处理@Bean注解),同时可以扫描
AnnotationConfigApplicationContext:继承了GenericApplicationContext,实现了AnnotationConfigRegistry接口,拥有了以上所有的功能.

国际化MessageResource

国际化在实际的项目过程中还是会遇到比较多的情况,比如你的前端是需要根据不同语言进行切换的情况,那么就需要国际化,前端传入不同的语言环境,那么我们后端根据不同的语言取出对应语言的数据也是比较常见的场景;首先我们创建两个Resources文件Messages.properties和Messages_en_US.properties
在这里插入图片描述
然后spring底层为我们进行国际化的时候需要一个单例的Bean MessageResourcce
所以我们还需要将MessageResourcce注册为一个Bean并且交给spring进行管理

@Bean
public MessageSource messageSource(){
   ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
   messageSource.setBasenames("messages");
   return messageSource;
}

然后通过下面的方式就可以取出对应的国际化后的值

AnnotationConfigApplicationContext c = new AnnotationConfigApplicationContext();
c.register(AppConfig.class);
c.refresh();
//Local传入空或者传入null取默认语言的
String test = c.getMessage("test", null, new Locale("en_US"));
System.out.printf("test=%s\n",test);

资源加载

ApplicationContext还拥有资源加载的功能,比如,可以直接利用ApplicationContext获取某个文件的内容


AnnotationConfigApplicationContext c = new 
AnnotationConfigApplicationContext();
c.register(AppConfig.class);
c.refresh();
Resource resource = c.getResource("file://D:\\kfk_zk.tar.gz");
System.out.printf("resource.leng=%d\n",resource.contentLength());

还可以获取多个文件,比如获取某个包下面的class文件

AnnotationConfigApplicationContext c = new
 AnnotationConfigApplicationContext();
c.register(AppConfig.class);
c.refresh();
String test = c.getMessage("test", null, null);
System.out.printf("test=%s\n",test);

Resource[] resources = c.getResources("classpath:com/bml/annoation/*.class");
for (Resource resource : resources) {
   System.out.printf("class %s \n",resource.getFile().getAbsolutePath());

}

获取运行时环境

spring现在不仅仅是提供了一些api给我们操作,也不仅仅是做了容器该做的事情,它还提供了一些额外的功能,比如获取系统环境变量信息,其实我们在平时的开发过程中如果要获取系统环境设置的一些参数是通能过System.getProperty来获取,其实spring底层也是这么做

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(AppConfig.class);
    ac.refresh();
ConfigurableEnvironment environment = ac.getEnvironment();
System.out.println(environment.getSystemEnvironment());
System.out.println(environment.getSystemProperties());

通过这种方式我们还可以获取到运行中自定义设置的参数,比如我们设置了某个参数表示启用某项功能也可以在启动参数中进行设置也就是-D参数,比如我们设置一个-D参数,-DRunFlag=2,通过下面的代码可以得到

System.out.println(environment.getProperty("RunFlag"));

spring给我们提供了一个注解也可以将一些参数加入到环境变量中@PropertySource
我们在resource目录下创建一个user.properties文件,然后在Appcaonfig中添加@PropertySource注解
user.properties文件

name=admin
age=23
@ComponentScan("com.spring")
@PropertySource("classpath:user.properties")
public class AppConfig {


   @Bean
   public Person person(){
      return new Person();
   }
}


public static void main(String[] args) {

      AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();
      ConfigurableEnvironment environment = ac.getEnvironment();
      System.out.printf("name=%s ,age=%s\n", 
      environment.getProperty("name"),environment.getProperty("age"));

   }	

不管你使用-D在启动参数中加入还是使用@PropertySource加入的,我们都可以通过ApplicationContext中来获取,还可以通过spring提供的注解@Value来获取

@Value("${name}")
private String name;

@Value("${RunFlag}")
private String runFlag;

也是一样的能获取到

事件发布

定义事件

@Bean
public ApplicationListener applicationListener() {
   return new ApplicationListener() {
      @Override
      public void onApplicationEvent(ApplicationEvent event) {
         System.out.println("接收到了一个事件");
      }
   };
}

发布事件

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(AppConfig.class);
    ac.refresh();
    ac.publishEvent("aaa");

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值