Spring学习

一、spring特点

  1. 轻量级
  2. 控制反转
  3. 面向切面
  4. 容器
  5. 框架集合

Spring两大核心机制:

  1. IOC:工厂模式
  2. AOP:代理模

二、 IOC(控制反转)

2.1 对于IOC的理解

传统开发中,需要调用对象的时候,需要调用者手动来创建被调用者的实例,即对象是由调用者new出来的

但是在Spring框架中,创建对象的工作不再由调用者来完成,而是交给IOC容器来创建,再推送给调用者,整个流程完成反转,所以是控制反转

对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转

IOC的特点是解耦合。

比如说A需要用到B,传统的开发,我们要直接创建B的实例,但是在Spring中,IOC这个容器会创建B的实例,然后把这个B注入到A

2.2 如何使用IOC

1. 在resources路径下创建applicationContext.xml配置文件

2. 传统的开发方式:手动new Student

3. IOC容器通过读取配置文件,加载配置bean标签来创建对象

 4. 调用API,从IOC获取对象

2.3 IOC容器创建bean的两种方式

1. 无参构造函数(需要提供对应的set方法)

2. 有参构造函数

2.4 从IOC容器中取bean

  1. 通过id取值
applicationContext.getBean(“stu”);
  1. 通过类型取值
applicationContext.getBean(Student.class);
  1. 当IOC容器中存在两个以上Student Bean的时候就会抛出异常,因为此时没有唯一的bean

2.5 bean的属性如果包含特殊字符

2.6 DI

DI:指bean之间的依赖注入,设置对象之间的级联关系

这里是student和class的两个实体类 注入到spring中,要把学生班级关联起来

bean之间的级联需要使用ref属性,而不能用value属性,否则会抛出类型转换异常

2.7 Spring的依赖

  1. 用来设置两个bean的创建顺序
  2. IOC容器默认情况下是通过applicationContext.xml中bean的配置顺序来决定创建顺序的,配置在前面的bean会先被创建
  3. 在不更改applicationContext.xml配置顺序的前提下,通过设置bean之间的依赖关系来调整bean的创建顺序

三、IOC原理

3.1 基本概念

Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务

3.2 Spring 容器高层视图

Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表(beanDefinitionRegistry),然后根据这张注册表实例化 Bean,装配好 Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中 Bean 缓存池为 HashMap 实现

3.3 IOC 容器实现

3.3.1 BeanFactory-框架基础设施

BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用Spring 框架的开发者,几乎所有的应用场合我们都直接使用 ApplicationContext 而非底层的 BeanFactory。

3.3.2 BeanDefinitionRegistry 注册表

Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册BeanDefinition 对象的方法

3.3.3 BeanFactory 顶层接口

位于类结构树的顶端 ,它最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的 Bean,BeanFactory 的功能通过其他的接口得到不断扩展

3.3.4 ListableBeanFactory

该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数、获取某一类型Bean 的配置名、查看容器中是否包括某一 Bean 等方法;

3.3.5 ApplicationContext 面向开发应用

ApplicationContext 由 BeanFactory 派生而来,提供了更多面向实际应用的功能。

ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过多个其他的接口扩展了 BeanFactory 的功能:

  1. ClassPathXmlApplicationContext:默认从类路径加载配置文件
  2. FileSystemXmlApplicationContext:默认从文件系统中装载配置文件
  3. ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。
  4. MessageSource:为应用提供 i18n 国际化消息访问的功能;
  5. ResourcePatternResolver : 所 有 ApplicationContext 实现类都实现了类似于PathMatchingResourcePatternResolver 的功能,可以通过带前缀的 Ant 风格的资源文件路径装载 Spring 的配置文件。
  6. LifeCycle:该接口是 Spring 2.0 加入的,该接口提供了 start()和 stop()两个方法,主要用于控制异步处理过程。在具体使用时,该接口同时被 ApplicationContext 实现及具体Bean 实现,ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接口的 Bean,以达到管理和控制 JMX、任务调度等目的。
  7. ConfigurableApplicationContext 扩展于 ApplicationContext,它新增加了两个主要的方法: refresh()和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文,在已经启动的状态下,调用 refresh()则清除缓存并重新装载配置信息,而调用 close()则可关闭应用上下文。

3.3.6 Spring Bean 作用域

Spring 3 中为 Bean 定义了 5 中作用域,分别为 singleton(单例)、prototype(原型)、

requestsession global session,5 种作用域说明如下:

  • singleton:单例模式(多线程下不安全)

1. singleton:单例模式,Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:

<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>

  • prototype:原型模式每次使用时创建

2. prototype:原型模式,每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton作用域。

  • Request:一次 request 一个实例

3. request:在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会

产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean

实例也将会被销毁。

<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
  • session

4. session:在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。

<bean id="userPreference" class="com.ioc.UserPreference" scope="session"/>
  • global Session

5. global Session:在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在

使用 portlet context 时有效。

3.3.7 Spring Bean 生命周期

对于普通的 Java 对象来说,它们的生命周期就是:

  1. 实例化
  2. 该对象不再被使用时通过垃圾回收机制进行回收 

对于 Spring Bean 的生命周期来说:

  1. 实例化 Instantiation
  2. 属性赋值 Populate
  3. 初始化 Initialization
  4. 销毁 Destruction

实例化 -> 属性赋值 -> 初始化 -> 销毁

最终逻辑:

至于销毁,是在容器关闭时调用的

至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的类,只不过是对主流程四个步骤的一系列扩展点而已。

3.3.7.1 Spring Bean 的生命周期的扩展点

3.3.7.1.1 Bean 自身的方法

比如构造函数、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。

3.3.7.1.2容器级的方法(BeanPostProcessor 一系列接口

主要是后处理器方法,比如下图的InstantiationAwareBeanPostProcessorBeanPostProcessor 接口方法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

3.3.7.1.3 InstantiationAwareBeanPostProcessor 源码分析

我们翻一下源码发现InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor

  1. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 调用点

Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

返回值:如果返回的不为null,那么后续的Bean的创建流程【实例化、初始化afterProperties】都不会执行,而是直接使用返回的快捷Bean,此时的正常执行顺序如下:

InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用。

BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用。

 resolveBeforeInstantiation()方法详解 

applyBeanPostProcessorsBeforeInstantiation()方法,内部调用postProcessBeforeInstantiation()方法

         总之,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的 Bean 作为代理,这也是 AOP 等功能实现的关键点。

  1. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 调用点

boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException

正常情况下在实例化之后在执行populateBean之前调用,即属性赋值

返回值:如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入

总结:

  1. 实例化前:调用doCreateBean方法,内调用初始化的前处理方法postProcessBeforeInstantiation方法,判断当前bean是否已被创建,如果已经被创建,直接调用实例化的后处理方法postProcessAfterInitialization,如果没有被创建,调用doCreateBean方法创建bean,实际上就是通过反射的方式创建bean
  2. 实例化后:调用实例化的后处理方法 postProcessAfterInstantiation方法,用来判断是否进行属性填充,如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入将不会执行
  3. 设置属性:上述返回true则对bean遍历并,调用postProcessPropertyValues获取bean的属性值,调用applyPropertyValues方法,这一步是真正的设置属性
  4. 初始化前: 初始化的方法为initializeBean() ,方法内,在初始化之前,调用初始化前处理方法applyBeanPostProcessorsBeforeInitialization,里面循环所有的后置处理器并调用postProcessBeforeInitialization,判断此 bean 是不是各种的Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,可以向容器中导入相关的上下文环境,目的是为了 Bean 实例能够获取到相关的上下文,如果不是它列举的几个 Aware,那就调用 invokeAwareInterfaces(bean),向容器中添加相关接口的上下文环境,这里面涉及到大量后置处理器和Aware的接口回调
  5. 初始化中: 方法为invokeInitMethods(),内部使用@PostConstruct 标注方法、实现initializingBean接口的afterPropertySet方法、自定义初始化方法init-method
  6. 初始化后:调用方法applyBeanPostProcessorsAfterInitialization()方法,里面调用
    postProcessAfterInitialization()方法

四、AOP(面向切面)

4.1 概念

  • AOP (Aspect Oriented Programming) 面向切面编程
  • OOP (Object Oriented Programming) 面向对象编程,用对象化的思想来完成程序
  • AOP是对OOP的一个补充,是在另外一个维度上抽象出对象
  • 具体指程序运行时动态地把非业务代码切入到业务代码中,从而实现程序的解耦合,把非业务代码抽象成一个对象,对对象编程就是面向切面编程

4.2 优点

  • 可以降低模块之间的耦合性
  • 提供代码的复用性
  • 提高代码的维护性
  • 集中管理非业务代码,便于维护
  • 业务代码不受非业务代码影响,逻辑更加清晰

4.3 AOP如何实现? 

AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理

4.3.1 什么是代理?

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

简单示意图:

 为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。


4.3.2 Java 动态代理

实现方式:

  1. jdk动态代理:使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口
  2. cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的

jdk动态代理,一般主要涉及到以下两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法

public object invoke(Object obj,Method method, Object[] args)

(2)Proxy类:该类即为动态代理类

//构造函数,用于给内部的h赋值
protected Proxy(InvocationHandler h); 
//获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Class getProxyClass (ClassLoaderloader, Class[] interfaces); 
//返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h); 

jdk动态代理步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法

动态代理的作用:

​ 1)在目标类源代码不改变的情况下,增加功能。
​ 2)减少代码的重复
​ 3)专注业务逻辑代码
​ 4)解耦合,让你的业务功能和日志,事务非业务功能分离。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值