banner
NEWS LETTER

Spring学习笔记

  • Home
  • spring-studyNode
Scroll down

Spring是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。

一、什么是Spring框架

1、Spring框架基本介绍

Spring框架指的都是Spring Framework,多个模块的集合。模块:核心容器、数据访问/集成、Web、AOP(面向切面)、工具、消息和测试模块。

例:Core Container中的Core组件时Spring所有组件的核心,Beans组件和Context组件是实现IOC和依赖注入的基础,AOP组件用来实现面向切面编程。

核心技术:依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL
测试:模拟对象,TextContext框架,Spring MVC测试,WebTestClient
数据访问:事务,DAO支持,JDBC,ORM,编组XML
Web支持:Spring MVC和Spring WebFlux Web框架
集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存
语言:Kotlin,Groovy,动态语言

2、Spring重要模块

Spring Core:基础,可以说Spring其他所有的功能都需要依赖于该类库。主要提供IoC依赖注入功能
Spring Aspects:该模块为与AspectJ的集成提供支持
Spring AOP:提供了面向切面的编程实现
Spring JDBC:Java数据库连接
Spring JMS:Java消息服务
Spring ORM:用于支持Hibernate等ORM工具
Spring Web:为创建Web应用程序提供支持
Spring Test:提供了对JUnit和TestNG测试的支持

3、@RestController vs @Controller

@Controller:返回一个页面单独使用@Controller不加@ResponseBody的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC的应用(前后端不分离)

@ResponseBody:作用是将Controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到HTTP响应(Response)对象的body中,通常用来返回JSON或XML数据

@RestController返回JSON或XML形式数据
@RestController只返回对象,对象数据直接以JSON或XML形式希尔HTTP响应(Response)中,这种情况属于RESTful Web服务(前后端分离)

@Controller + @ResponseBody 返回JSON或XML形式数据

若在Spring4之前开发RESTful Web服务,需要@Controller + @ResponseBody = @RestController(Spring4之后新加的注解)

二、Spring IOC

1、IoC基本介绍

IoC(Inverse of Control):控制反转是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
IoC容器时Spring用来实现IoC的载体,IoC容器实际上就是个Map(key,value),Map中存放的时各种对象。

将对象之间的相互依赖关系交给IoC容器来管理,并由IoC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出现。IoC容器就像时一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象时如何被创建出来的。在实际项目中一个Service类可能有几百甚至上千个类作为它的底层,假如需要实例化这个Service,可能每次都要清楚这个Service所有底层类的构造函数,若利用IoC,只需配置好,在需要位置引用,降低项目的可维护性且降低了开发难度

IoC初始化过程:
xml -读取-> Resource -解析-> BeanDefinition -注册-> BeanFactory

三、Spring AOP

1、AOP基本介绍

AOP(Aspect-Oriented Programming):面向切面编程
能够将与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有可拓展性和可维护性

2、Spring切面五种类型

前置通知(@Before):在目标方法被调用之前调用通知功能
后置通知(@After):再目标方法完成之后调用通知,不用关心方法的输出结果
返回通知(@AfterReturning):再目标方法成功执行之后调用通知
异常通知(@AfterThrowing):再目标方法抛出异常后调用通知
环绕通知(@Around):通知包裹了被通知的方法,再被通知的方法调用之前和调用之后执行自定义的行为

3、连接点(Join point)

连接点是再应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时,切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为

4、切点(Pointcut)

切点的定义会匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许创建动态切点,可根据运行时的决策(例如方法参数值)决定是否应用通知

5、切面(Aspect)

通知 + 切点 = 切面

6、引入(Introduction)

引入允许我们向现有的类添加新方法或属性

7、织入(Weaving)

织入是把切脉你应用到目标对象并创建新的代理对象的过程。切面再指定的连接点被织入到目标对象中。再目标对象的生命周期里有多个点可以进行织入

编译期:切面再目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的
类加载期:切面再目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以再目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面
运行期:切脉你再应用运行的某个时刻被织入。一般情况下,再织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就时以这种方式织入切面的

Spring AOP就是基于动态代理的,若要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,无法使用JDK Proxy去进行代理,这时候Spring AOP会使用Cglib,这时Spring AOP 会使用Cglib生成一个被代理对象的子类来作为代理

也可使用AspectJ,Spring AOP已经集成AspectJ
使用AOP之后我们可以把一些通用功能抽象处理,在需要用到的地方直接使用即可,这样简化了代码量,提高了系统扩展性。日志功能、事务管理等都用到AOP。

Spring AOP和AspectJ AOP区别:

  • Spring AOP属于运行时增强,而AspectJ时编译时增强。
  • Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)

Spring AOP集成了AspectJ,AspectJ相比于Spring AOP功能更加强大,但Spring AOP相对更简单。
若切面较少,性能差异不大,若切面较多,推荐使用AspectJ(比Spring AOP快)。

四、Spring Bean

1、Spring Bean基本介绍

每个类实现了Bean的规范才可以由Spring接管
Bean规范:必须是公有(public)类,有无参构造函数,用 公共方法暴露内部成员属性(getter,setter)
实现这样规范的类,被称为Java Bean,既是一种可重用的组件

2、作用域

singleton(单例,默认):唯一Bean实例
prototype(原型):每次请求都会创建一个新的Bean实例
request(请求):每次请求都会创建一个新的Bean实例,该Bean仅在当前HTTP Request内有效
session(会话):每次HTTP请求产生新的Bean,该Bean仅在当前HTTP Session内有效
global-session(全局会话):全局session作用域,仅仅在基于Portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码片段的小型Java Web插件。基于Portlet容器,可以像Servlet一样处理HTTP请求,但与Servlet不同,每次Portlet都有不同会话

默认情况下,Spring应用上下文中所有Bean都是作为以单例(singleton)的形式创建的。既不管给定的一个Bean被注入到其他Bean多少次,每次所注入的都是同一个实例

多数情况下,单例Bean是很理想的方案,初始化和垃圾回收对象实例所带来的成本只留给一些小规模任务,在这些任务中,让对象保持无状态并且在应用中反复重用这些对象可能并不合理

有时候,使用的类是易变的(mutable),它们会保持一些状态,因此重用是不安全的。这种情况下,将class声明为单例的Bean会出现问题,因为对象会被污染,稍后重用时会出现意想不到的问题

3、单例Bean的线程安全问题

单例Bean存在线程问题,当多个线程操作同一个对象时,这个对象的非静态成员变量的写操作会存在线程安全问题

解决方法:
①、在Bean对象中尽量避免定义可变的成员变量
②、在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中

4、@Component和@Bean区别

①、作用对象不同:@Component注解作用于类,而@Bean注解作用于方法
②、@Component通常时通过类路径扫描来自动侦测以及自动装配到Spring容器中(可使用@ComponentScan注解定义要扫描的路径从中找出标识了需要装配的类自动装配到Spring的Bean容器中)
@Bean注解通常是定义产生这个Bean,@Bean告诉Spring这是某个类的示例,需要时使用
③、@Bean注解比@Component注解的自定义性更强

5、将一个类声明为Spring的Bean的注解

@Autowired注解自动装配Bean,若想把类表示为@Autowired注解自动装配的Bean类,可采用以下注解实现:
@Component:通用注解,可标注仍以类为Spring组件。(若一个Bean不知道属于哪一层,可使用@Component注解)
@Repository:对应持久层既Dao层,主要用于数据库相关操作
@Service:对应服务层,主要涉及一些复杂的逻辑,需要用到Dao层
@Controller:对应Spring MVC控制层。主要接受用户请求并调用Service层返回数据给前端页面

@Autowired不仅是对象,还有在构造器上,还能用在属性的Setter方法上不论是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖,假如有且只有一个Bean匹配依赖需求,那么这个Bean将会被装配进来若没有匹配的Bean,那么在应用上下文创建时,Spring会抛出一个异常。为避免异常出现,可将@Autowired的required属性设置为false

将required属性设置为false时,Spring会尝试执行自动装配,但若没有匹配的Bean的话,Spring将会让这个Bean处于未配置的状态。但是,把required属性设置为false时,在代码中没有进行null检查该处于为装配状态的属性可能出现NUllPointerException

@Inject注解来源于Java依赖注入规范,该规范同时还为我们定义了@Named注解。在自动装配中,Spring同时支持@Inject和@Autowired。尽管@Inject和@Autowired之间有着一些细微的差别,但在大多数场景下,他们都是可以互相替换的

6、Bean的生命周期

①、Spring对Bean进行实例化
②、Spring将值和Bean的引用注入到Bean对应的属性中
③、若Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBean-Name()方法
④、若Bean实现了BeanFacotryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
⑤、若Bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将Bean所在的应用上下文的引用传入进来
⑥、若Bean实现了BeanPostProcessor接口,Spring将调用他们的postProcessBeforeInitialization()方法
⑦、若Bean实现了InitializingBean接口,Spring将调用他们的after-PropertiesSet()方法,类似地,若Bean使用init-method声明了初始化方法,该方法也会被调用
⑧、若Bean实现了BeanPostProcessor接口,Spring将调用他们的post-ProcessAfterInitialization()方法
⑨、此时,Bean已经准备就绪,可被应用程序使用,他们将一直驻留在应用上下文中,知道应用上下文被销毁
⑩、若Bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,若Bean使用destroy-method声明了销毁方法,该方法也会被调用

请随意打赏

评论