Spring系列一(Spring是什么、IoC和DI、AOP)

文章目录

  • 1. Spring是什么?
  • 2. Spring的IoC、DI
  • 3. AOP
  • 4. Spring IoC容器的启动加载过程
  • 5. BeanFactory和ApplicationContext的区别
  • 4. Bean的生命周期(生产Bean的过程)

1. Spring是什么?

Spring是一个轻量级的IoC和AOP容器框架,提供了IoC和AOP两大能力,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。主要包括以下七个模块:

  1. Core Container(核心容器):Spring的核心容器由Core、Beans、Context、SpEL模块。其中,Core和Beans是整个框架爱最基础的部分,提供IOC和依赖注入特性。最重要的概念是BeanFactory。
  2. AOP模块:AOP模块提供了AOP提倡的面向切面编程的实现环境。AOP将代码按功能进行分离,降低了模块之间代码的耦合度。
  3. Data Access/Integration:数据访问/集成模块,方便了将持久化框架、消息中间件集成到Spring中。
  4. Web模块:Web模块提供了对Web开发相关技术的集成,主要集成了SpringMVC。
  5. Messaging:具有来自Spring Integration项目的关键抽象,如Message、MessageChannel、MessageHandler等。是基于消息的应用程序的基础。
  6. Test模块:Test模块支持Junit等单元测试的集成,方便独立测试代码。
  7. Spring ORM:对现有的ORM框架的支持;

Spring 的优点

  • spring属于低侵入式设计,代码的污染极低;
  • spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
  • Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。
  • spring对于主流的应用框架提供了集成支持。

2. Spring的IoC、DI

IoC(Inversion of Control)控制反转,指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。

最直观的表达就是,以前创建对象的时机和主动权都是由自己把控的,如果在一个对象中使用另外的对象,就必须主动通过new指令去创建依赖对象,使用完后还需要销毁(比如Connection等),对象始终会和其他接口或类耦合起来。而 IOC 则是由专门的容器来帮忙创建对象,将所有的类都在 Spring 容器中登记,当需要某个对象时,不再需要自己主动去 new 了,只需告诉 Spring 容器,然后 Spring 就会在系统运行到适当的时机,把你想要的对象主动给你。也就是说, 对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。

DI:依赖注入,把对应的属性的值注入到具体的对象中,最简单的方式@Autowired,等完成属性值的注入。

IoC原理: Spring 的 IoC 的实现原理就是工厂模式加反射机制,而在 Spring 容器中,Bean 对象如何注册到 IoC 容器,以及Bean对象的加载、实例化、初始化详细过程可以阅读这篇文章:Spring的Bean加载流程_张维鹏的博客-CSDN博客

3. AOP

OOP面向对象,允许开发者定义纵向的关系(互相调用),但并不适用于定义横向的关系,会导致大量代码的重复,而不利于各个模块的重用。

AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。

实现方式:目前流行的AOP框架有两个,分别为Spring AOPAspectJ。Spring AOP使用纯Java实现,不需要专门的编译器和类加载器,在运行期间通过代理方式向目标类植入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了专门的编译器,在编译时提供横向代码的植入。

在Spring AOP中,实现AOP的方式是动态代理。而实现动态代理有两种方式:JDK动态代理和CGLIB动态代理。Spring根据是否实现被代理接口来选择使用JDK动态代理还是CGLIB动态代理。详见:
JDK动态代理和CGLIB动态代理
AOP概念、相关术语、AOP实战(基于AspectJ实现)、Adice类型及执行顺序、切入点表达式写法

4. Spring IoC容器的启动加载过程

详细内容可以阅读这篇文章:Spring容器的启动流程
1、初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中:

  • ① 实例化BeanFactory【DefaultListableBeanFactory】工厂,用于生成Bean对象
  • ② 实例化BeanDefinitionReader注解配置读取器,用于对特定注解(如@Service、@Repository)的类进行读取转化成 BeanDefinition 对象,(BeanDefinition 是 Spring 中极其重要的一个概念,它存储了 bean 对象的所有特征信息,如是否单例,是否懒加载,factoryBeanName 等)
  • ③ 实例化ClassPathBeanDefinitionScanner路径扫描器,用于对指定的包目录进行扫描查找 bean 对象

2、将配置类的BeanDefinition注册到容器中:
3、调用refresh()方法刷新容器:

  • ① prepareRefresh()刷新前的预处理:
  • ② obtainFreshBeanFactory():获取在容器初始化时创建的BeanFactory:
  • ③ prepareBeanFactory(beanFactory):BeanFactory的预处理工作,向容器中添加一些组件:
  • ④ postProcessBeanFactory(beanFactory):子类重写该方法,可以实现在BeanFactory创建并预处理完成以后做进一步的设置
  • ⑤ invokeBeanFactoryPostProcessors(beanFactory):在BeanFactory标准初始化之后执行BeanFactoryPostProcessor的方法,即BeanFactory的后置处理器:
  • ⑥ registerBeanPostProcessors(beanFactory):向容器中注册Bean的后置处理器BeanPostProcessor,它的主要作用是干预Spring初始化bean的流程,从而完成代理、自动注入、循环依赖等功能
  • ⑦ initMessageSource():初始化MessageSource组件,主要用于做国际化功能,消息绑定与消息解析:
  • ⑧ initApplicationEventMulticaster():初始化事件派发器,在注册监听器时会用到:
  • ⑨ onRefresh():留给子容器、子类重写这个方法,在容器刷新的时候可以自定义逻辑
  • ⑩ registerListeners():注册监听器:将容器中所有的ApplicationListener注册到事件派发器中,并派发之前步骤产生的事件:
  • ⑪ finishBeanFactoryInitialization(beanFactory):初始化所有剩下的单实例bean,核心方法是preInstantiateSingletons(),会调用getBean()方法创建对象;
  • ⑫ finishRefresh():发布BeanFactory容器刷新完成事件:

5. BeanFactory和ApplicationContext的区别

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。区别如下:

  • BeanFactory是Spring的顶层核心接口,定义了IoC的基本功能。包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:国际化、资源文件访问…
  • BeanFactory在创建对象采取的策略是采用延迟加载的方式。只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
  • ApplicationContext创建对象采用的是立即加载的方式,也就是说在容器启动时一读取完配置文件就一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
  • ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。
  • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
  • BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

ApplicationContext常用的三个实现类

  • ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(常用)
  • AnnotationConfigApplicationContext:用于读取注解创建容器的。
  • FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件(必须有访问权限)。(不常用)

4. Bean的生命周期(生产Bean的过程)

Spring Bean的生命周期指的是一个普通的Java类变成Bean的过程,大致流程是:在创建Bean时,Spring会扫描指定包下面的Java类,然后根据Java类构建BeanDefinition对象,然后再根据BeanDefinition来创建Spring的bean,特别要记住一点,Spring是根据BeanDefinition来创建Spring bean的。

通过ClassPathXmlApplicationContext和AnnotationConfigApplicationContext类将XML定义或者注解定义的的Bean读取成统一的Bean规范,即BeanDefinition(BeanDefinition是Spring的一个核心接口,它封装了生产Bean的一切原料,关于Bean生产的一切定义都在BeanDefinition类里。),存放多个BeanDefinition的Map是BeanDefinitionMap,BeanDefinitionMap中的BeanDefinition由BeanDefinitionRegistry负责注册成Bean。最后由Spring的顶层核心接口BeanFactory负责生产Bean(通过getBean方法)。

大体上,Bean的什么周期分为以下几个步骤:

  1. 实例化Bean:反射的方式生成对象
  2. 填充Bean的属性:populateBean(),循环依赖的问题(三级缓存)
  3. 调用aware接口相关的方法:invokeAwareMethod(),完成BeanName、BeanFactory、BeanClassLoader对象的属性设置
    • BeanNameAware:可以获取容器中Bean的名称
    • BeanFactoryAware:获取当前BeanFactory,也可以调用容器的服务
    • ApplicationContextAware:当前的applicationContext,也可以调用容器的服务
    • MessageSourceAware:获取Message Source,也可以获得文本信息
    • ApplicationEventPublisherAware:应用事件发布器,可以发布事件
    • ResourceLoaderAware:获得资源加载器,可以获得外部资源文件的内容;
  4. 调用BeanPostProcessor中的前置处理方法:使用比较多的有ApplicationContextPostProcessor(设置ApplicationContext、Environment、ResourceLoader、EmbeddValueResolver等对象)
  5. 调用init-method方法:invokeInitMethod(),判断是否实现了initializingBean接口,如果有,调用afterPropertiesSet()方法,没有就不调用。
  6. 调用BeanPostProcessor的后置处理方法:Spring的AOP就是在此处实现的,AbstractAutoProxyCreator 。另外还有Destuction相关的回调接口
  7. 获取到完整的对象,可以通过getBean的方式来进行对象的获取。
  8. 销毁流程:1、判断是否实现了DisposableBean接口 2、调用destroyMethod方法

Java类变成BeanDefinition的过程,重点关注AbstractApplicationContext类中的refresh方法:

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing. 准备工作包括设置启动时间,是否激活标识位,
		// 初始化属性源(property source)配置
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		//返回一个factory 为什么需要返回一个工厂
		//因为要对工厂进行初始化
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		//准备工厂
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			//这个方法在当前版本的spring是没用任何代码的
			//可能spring期待在后面的版本中去扩展吧
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			//在spring的环境中去执行已经被注册的 factory processors
			//设置执行自定义的ProcessBeanFactory 和spring内部自己定义的
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			//注册beanPostProcessor
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			initMessageSource();

			// Initialize event multicaster for this context.
			//初始化应用事件广播器
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			onRefresh();

			// Check for listener beans and register them.
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

有关Spring Bean生命周期最主要的方法有三个invokeBeanFactoryPostProcessors、registerBeanPostProcessors和finishBeanFactoryInitialization(重要?)。

  • invokeBeanFactoryPostProcessors方法:执行BeanFactoryPostProcessors后置处理器及其子接口BeanDefinitionRegistryPostProcessor,执行顺序先是执行BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法,然后执行BeanFactoryPostProcessor接口的postProcessBeanFactory方法。对于BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法,该步骤会扫描到指定包下面的标有注解的类,然后将其变成BeanDefinition对象,然后放到一个Spring中的Map中,用于接下来创建Spring bean的时候使用这个BeanDefinition。
  • registerBeanPostProcessors方法根据实现了PriorityOrdered、Ordered接口,排序后注册所有的BeanPostProcessor后置处理器,主要用于Spring Bean创建时,执行这些后置处理器的方法,这也是Spring中提供的扩展点,让我们能够插手Spring Bean创建过程。

Spring中Bean的生命周期大致如下:

  1. 实例化Bean对象:这个时候Bean的对象是非常低级的,基本不能被使用,因为连最基本的属性都没有设置,可以理解为连Autowired注解都没有解析。
    • 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
    • 对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。(通过反射调用工厂方法)
  2. 填充Bean对象属性:实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
  3. 处理Aware接口:
    • 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
    • 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
    • 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
  4. BeanPostProcessor(Bean后置处理程序):也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调。
public interface BeanPostProcessor {
	//在每一个bean对象的初始化方法调用之前回调
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	
	//在每个bean对象的初始化方法调用之后被回调。
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
  1. InitializingBean 与 init-method:如果Bean在Spring配置文件中配置了init-method 属性,则会自动调用其配置的初始化方法(Bean的init-method方法)。
  2. DisposableBean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法。 destroy-method:最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
    Spring系列一(Spring是什么、IoC和DI、AOP)_第1张图片
    Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,默认从BeanFactory或ApplicationContext取得的实例是singleton(单例)的,也就是使用Singleton模式产生单一实例,Spring会帮我们管理单例模式Bean的完整生命周期,容器关闭Singleton的bean也就跟着销毁了。

而对于scope=prototype的bean,Spring在创建好交给使用者之后则不会再管理后续的生命周期,而是交给JVM的垃圾回收机制(GC)来管理Bean的生命周期。

@Component(value = "accountService")//默认名称是accountServiceImpl
@Scope(value = "singleton")//默认为singleton,可以不写
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void saveAccount() {
        accountDao.saveAccount();
    }

    @PostConstruct
    public void init_method(){
        System.out.println("初始化方法执行了");
    }

    @PreDestroy
    public void destroy_method(){
        System.out.println("销毁方法被执行了");
    }
}
public static void main(String[] args) {
        //1.获取核心容器对象
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取Bean对象
        AccountService accountService = (AccountService) classPathXmlApplicationContext.getBean("accountService");

        //3.打印获取的对象
        System.out.println(accountService);
        classPathXmlApplicationContext.close();
}

运行结果:

初始化方法执行了
com.xingze.service.impl.AccountServiceImpl@166fa74d
十月 29, 2019 9:26:44 上午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d76f3f8: startup date [Tue Oct 29 09:26:43 CST 2019]; root of context hierarchy
销毁方法被执行了

分析:容器在关闭后,scope=singleton的Bean也就跟着销毁了。

2、将@Scope(value = "singleton")改为@Scope(value = "prototype"),运行结果如下:

初始化方法执行了
com.xingze.service.impl.AccountServiceImpl@166fa74d
十月 29, 2019 9:32:18 上午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d76f3f8: startup date [Tue Oct 29 09:32:17 CST 2019]; root of context hierarchy

可以看出,配置为scope=prototype的bean在容器关闭之后仍然存在,销毁方法并没有执行。

你可能感兴趣的:(SSM,java,Spring)