开源免费的、轻量级的、非侵入式的容器(一个服务器软件,一个框架),类与类之间的管理,帮助开发人员创建对象,管理对象等的一款 Java 开发框架。
针对接口编程,解耦合。
1.2 核心思想IOC(控制反转)AOP(面向切面编程)
1.3 优缺点 1.3.1 优点思想是上层建筑,不是具体的实现。好比:JVM 中方法区和元空间之间的关系。
轻量
Spring 核心功能所需的jar总共3M左右
针对接口编程,解耦合
通过 Spring 提供的IOC容器,可以将对象间的依赖关系交由 Spring 控制,避免在代码中通过代码相互使用。
用户也不必为单例模式类、属性文件解析等底层需求编写代码,可以更专注于上层的应用。
AOP 编程的支持
通过 Spring 的AOP功能,方便进行面向切面编程,许多不容易用传统 OOP (面向对象编程)实现的功能可以通过AOP轻松实现。
方便集成各种优秀框架
1.3.2 缺点框架封装内部复杂不易精通 2.IOC 2.1 简介
IOC(Inversion of Controller)(控制反转),分为控制和反转两部分。
把对象的创建、赋值、管理工作都交给代码之外的容器实现,也就是对象的创建是其它外部资源完成。
2.1.1 控制对象的创建、管理、赋值、对象的声明周期和对象之间的关系等权限。
2.1.2 反转 2.1.2.1 正转开发人员在代码中,使用 new 构造器创建对象,开发人员掌握了对象的创建、属性赋值、对象从开始到销毁的全部过程,开发人员对 对象有全部的控制。
2.1.2.2 反转开发人员将管理对象的权限转移给代码之外的容器实现。由容器完成对象的管理,只需定义对象。
2.2 Spring 中的 IOC在 Spring 中 IOC 的思想是通过 DI(依赖注入)实现,DI 底层通过反射机制实现(序列化也可以实现相关功能)。
开发人员只要知道需要使用对象的名称就行,其他的一切交给容器,其他容器内部的实现细节,无需关注。
通过 IOC 减少对代码的改动,也能实现不同的功能,实现业务对象之间的解耦合。例如:能实现service和dao对象之间的解耦合。
2.2.1 依赖Class A 使用了 Class B 的属性或方法,就称为 A 依赖了 B。
2.2.2 注入每一个 Bean(类的实例,在 Spring 中称为Bean)的属性都通过容器注入。注入的方式分为构造注入(构造器,显示的在类中进行声明包括无参构造器)、Set 注入(Spring 中通过 Set 进行 Bean 的属性注入)和自动注入
2.2.2.1 自动注入构造器注入和 Set 注入在 Java 中比较常见,在 Spring 中还有自动注入,可以根据 类的类型或实例的名字进行注入赋值。
byName:按名称注入,Java 类中引用类型的属性名和 Spring 容器中 Bean 的【id】一样,
数据类型一样,将容器中的这个 Bean 赋值给引用类型
byType:按类型注入,Java 类中引用类型的是【数据类型】和 Spring 容器中的【class】是同源关系的,
这样的 Bean 就能赋值给引用类型
2.2.2.2 实例演示注意:如果出现多个同源,则会注入失败
通过 Spring 中的配置文件的方式演示。
构造注入
第一行:
id:指定实例的名称,也就是在容器中存在的名字,其他 Bean 通过这个id进行调用,id区分大小写
class:实例类的静态类型的全路径
Set 注入
注:value 注入一般都是 Java 中的简单类型:基本数据类型 + String
通过这种方式,需要在具体 Bean 类中有具体属性的 Set 方法
ref 注入引用类型,指定在容器存在的id即可。
2.2.3 对象的创建方式(Java) 构造方法,new Student()反射序列化(对象的数据传输)克隆动态代理IOC:容器创建对象 2.3 IOC 的体现 - TomCatSpring执行set注入时,只会调用set方法,不会关注set中的代码,如果通过set注入,只要有相关的set方法就行(需要传递参数),无需在方法体中进行指定处理过程。
但在注入的过程中会执行方法体。
public void setName(String name){//没有执行过程 //正常的是有:this.name = name; System.out.println(email + "该方法执行了");}
像上面这种,只有方法名,但是没有实现过程的,Spring 也可以通过这个 set 方法进行 name 的赋值并且会运行方法体中的代码。
在我们使用 TomCat 服务器时,我们在 JavaWeb 中编写了 Servlet 以后,并没有进行 Servlet 类的实例化,只需要在 web.xml 文件中编写具体的映射和 myservlet 即可,这是因为 TomCat 帮我们进行了对象实例化的管理。
Tomcat作为容器:里面存放的有Servlet对象,Listener,Filter对象
这也是一种 IOC,在很多类似的框架或服务器中都频繁运用了 IOC 的思想,帮助程序员减少代码的编写和对象的管理,将更多的精力多放在业务代码的编写上。但并不是所有的类都交给容器管理。
2.4 总结 IOC将对象放入容器中,实现对 对象的管理,Spring中通过依赖注入(DI)实现IOC通过IOC来管理对象,来实现解耦合spring作为容器适合管理的对象:service对象dao对象工具类对象 spring作为容器不适合管理的对象:
实体类(容易变,每一次都不一样,如果交给容器管理,改变困难)servletlistenerfilter…等web中的对象这些web对象应该交给服务器来管理(tomCat) 3.Spring 创建 Bean 3.0 Bean 范围
Spring容器中的bean可以分为5个范围:
singleton(单例模式):默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。prototype(原型模式):为每一个bean请求提供一个实例。request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。 3.1 配置文件 读取配置文件扫描用户自定义配置文件,将Bean创建进容器中(需要指定扫描的文件)当用户需要使用时,直接从容器中获取 2.扫描用户配置文件,将Bean创建进容器中
将 Bean 按功能或模块进行划分,每一个模块建立一个xml配置文件
创建一个 applicationContext 总的配置文件进行管理其他配置文件
各个配置文件中的 Bean都加入到了总容器中,可以相互引用使用
3.2 注解扫描指定路径下的配置文件并进行加载。
classpath:项目路径开始的相对路径
注解赋值,需要在配置文件中添加开启注解支持
3.2.1 实例类的容器注入 @Component 1.添加注解如:使用了 @Component 注解,就需要在配置文件中添加一个组件扫描器,不然虽然添加了 Bean 注解,但是无法将 Bean 扫描到容器中。
@Component(value=“id”)
value:该组件的名称,可写可不写在当前类的开头可自定以赋值名称,也可以使用默认名称,默认名称为类名小字母 2.在配置文件中添加注解支持
通常对于一个类使用@@Component即可,为了方便,对于不同的层次有不同的注解:
@Repository:放在dao层,表示可以为持久层,可以和数据库交互@Service:表示业务层对象@Controller:表示为Servlet层 3.2.2 类中基本属性赋值 @Value
@Value:给简单数据类型赋值
无需set方法在属性上面直接赋值也可以在set上面进行赋值
@Value("pox")private String name;// 也可以是@Value(value="pox")@Value("pox1")public void setName(String name){ this.name = name;}
除了指定值以外,还可以通过加载外部的配置文件,然后读取配置文件的值来进行赋值。
通过在总的 Spring 配置中,读取外部的 properties 文件。然后通过@Value("${key}")来读取值。
1.在Spring配置文件中读取配置文件
2.通过注解给具体属性进行赋值@Value("${具体key}") // 加载的配置文件上的keyprivate String name;
3.2.3 类中引用属性赋值@Autowire(required=“true(默认)”:使用自动注入
支持byName、byType
默认为byType
可以通过set方法赋值,也可以直接在属性上面赋值
使用byName,通过两个注解实现
@Autowire@Qualifier(value=“xxx”)value为指定容器id的bean赋值给当前引用类型
如果没有指定required的值,默认为true,如果没有在容器中找到相应的bean就会爆出异常。如果将required改为false,如果没有找到相应的bean会继续往下执行,不会爆出异常
@Resource(name=“bean的id”),来自 JDK 自带,给引用类型赋值,可以byName和byType,默认为byName
先按照byName,如果没有找到,则会按照byType寻找赋值在jdk1.8中自带,高于1.8则没有这个注解,需要添加相关依赖当指定了具体的bean id,则只能通过byName注入
4.AOP 4.1 简介推荐使用 @Resource 注解实现引用类型的赋值注入。在 IDEA 中使用 @Autowire 会有提示,网上有很多解释,笔者采纳因为 @Autowire 是 Spring 的在移植性会有问题,脱离了 Spring 框架就不能使用了,但是 @Resource 是 JDK 自带的,不存在这个问题。
AOP(Aspect Oriented Program)(面向切面编程)(OOP面向对象编程的升级),将具体的方法当成一个切面,可以在切面基础上在方法的执行的前、后、环绕等添加新的功能。底层动过动态代理实现。
AOP是一个动态思想,在程序运行期间,创建代理。这个代理对象是存在内存中。
4.2 动态代理 4.2.1 JDK 动态代理抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
AOP代理主要分为静态代理和动态代理。
静态代理的代表为AspectJ;
动态代理则以Spring AOP为代表(JDK动态代理和CGLIB动态代理)。
JDK 自带。使用 JDK 反射包中的类实现创建代理对象的功能,InvocationHandler接口———调用处理程序
Method Proxy类代理。
使用第三方的工具库,实现代理对象的创建,继承类的动态代理,应对有些类没有实现接口,采用继承,所以方法不能是final MethodInterceptor方法拦截器接口、Enhancer增强器。
4.2.3 总结看做是动态代理的规范化,把实现动态代理的步骤进行了一个规范、定义。
万物皆切面,编写程序的时候,考虑有哪些切面组成程序。以切面为核心设计开发。
4.3 作用 在目标类源代码不改变的情况下,增加功能减少代码的重复专注业务逻辑代码解耦合,给你的业务功能和日志,事务等非业务功能分离4.4 AOP 中的概念名词在Spring中的作用:提供声明式事务,允许用户自定义切面。
在非业务代码中使用面向切面编程,不要在业务代码中使用,面向切面编程主要用于日志、事务、权限检查等这类操作代码无需根据业务进行变化的代码。
给一个系统中存在的类修改功能,但是原有类的功能不完善,但你还没有源代码,使用aop就增加功能。
aspect:切面
即它是一个类。表示增强的功能,就是一堆代码,完成某个功能,非业务功能,常见的切面功能有日志、事务、统计信息、参数检查、权限验证
JoinPoint:连接点,连接切面的业务方法。在这个业务方法执行时,会同时执行切面的功能;切面功能执行的位置
Pointcut:切入点,是一个或多个连接点的集合。表示在执行这些方法时,都能增加切面的功能
target:目标,被通知对象
给哪个类的方法增加功能,这个类就是目标对象。
Advice:通知,切面必须要完成的工作。即它是类中的一个方法。通知表示切面功能执行的时间。
Proxy:代理,向目标对象应用通知之后创建的对象
4.5 AOP 技术实现 4.5.1 概述通过框架实现
Spring:实现了 AOP 思想中的部分功能
实现的操作比较复杂、繁重
Aspectj:独立的框架,专门做AOP
4.5.2 AspectJ推荐使用 AspectJ,后面的实例演示也是根据 AspectJ 编写代码
使用 xml 配置文件中的标签实现 AOP 功能。
4.5.2.1 注解
@Advice:通知注解
@Before:前置通知
方法是公共的方法的返回值为void
@AfterReturnring:后置通知
@Around:环绕通知
@AfterThrowing:异常通知
@After:最终通知
4.5.2.2 实例演示Pointcut:切面执行的位置,使用AspectJ中的切入点表达式
语法:execution([方法执行体])
execution()方法。
modifiers-pattern:访问权限类型ret-type-pattern:返回值类型declaring-type-pattern:包名,类名name-pattern(param-pattern):方法名(方法参数)throws-pattern:抛出异常类型?:代表可选 实例 导入依赖: spring-aspects依赖创建切面类
在类的上面加上@Aspect注解在具体的方法上面加入表示执行的时间,比如:@Before(value=“execution([方法体])”) 声明目标对象声明切面类对象声明自动代理生成器
寻找spring容器中的所有aspect bean给每一个目标方法生成一个代理类在执行目标方法时,通过代理类执行
@Before(value="execution(public void [具体方法]([如果这个方法有参数就需要标注,如果没有就不需要,只需要参数类型,不需要说明参数名称]))")public void myBefore(){...}
-- 自动代理生成器
在切面中添加JoinPoint参数,可以获取代理方法的详细信息
@Before(value="execution(public void [具体方法]([如果这个方法有参数就需要标注,如果没有就不需要,只需要参数类型,不需要说明参数名称]))")public void myBefore(JoinPoint jp){jp.getSignature();// 等等,获取目标方法中的各种详细信息}
4.6 总结 主要的作用是业务功能和非业务功能进行解耦合一些事务、日志等非业务功能实现复用给一些方法增加一些新的功能,通过AOP生成动态代理对象,来增强目标方法切面类,加上@Aspect注解,以便于让容器扫描到 5.Spring 中的事务 5.1 事务概述事务保证一组操作中,要么都成功要么都失败。事务管理来确保数据的完整性和一致性。
事务中的 ADID:
Atomicity:事务是个原子操作,由一系列动作完成,事物的原子性确保动作要么都完成,要么都失败。Consistency:一旦所有事务动作完成,事务就被提交,数据和资源处于一种满足业务规则的一致性状态中。Isolation:可能有许多事务会同时处理相同的数据,因此每个事务都应与其他事务隔离开来,防止数据损坏。Durability:一旦事务完成,无论发生什么系统错误,结果不受影响。通常情况下,事物的结果被写到持久化存储器中。 5.2 事务管理
通过 Spring 的事务管理器,管理不同数据库访问技术的事务处理方法。
5.2.1 事务管理分类编程式事务
声明式事务:把事务相关的资源和内容都提供给 Spring,Spring 就能处理事务提交,回滚了。几乎不用代码。
5.2.2 使用时机声明式事务管理(提倡)(Spring Aop 支持声明式事务管理)
操作涉及多表或多个sql语句的insert、update、delete,要么都成功要么都失败在 Java 代码中事务应该放在 service 类的业务方法上,因为业务方法会调用多个 dao 方法,执行多个sql语句 5.3 缺点 不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理掌握多种数据库中事务的处理逻辑,何时提交或回滚事务处理事务的多种方法
5.4 事务的隔离级别总结:多种数据库的访问技术,有不同的事务处理机制,对象,方法。
read_uncommitted(读未提交):为解决任何并发问题
read_committed(读以提交):解决脏读
repeatable_read(MySQL默认,可重复读):解决脏读、不可重复读、存在幻读
在 MySQL 中使用这种隔离级别,通过 MVCC 方法其实是可以解决幻读的
serializable:串行化。不存在并发问题(安全性高,性能低)
5.4.1 事务的超时时间事务的隔离级别越高,并发性就越低、安全性越高;隔离级别越低,反之。
表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。单位秒,整数值,默认是-1。
5.5 事务的7个传播行为 Propagation.REQUIRED(默认)Propagation.REQUIRES_NEWPropagation.SUPPORTSPropagation.MANDATORYPropagation.NOT_SUPPORTEDPropagation.NEVERPropagation.NESTED5.5.1 Propagation.REQUIRED重点理解前三个。
Spring 默认的传播行为,方法在调用的时候如果有事务就用当前的事务,如果没有事务,就新建一个事务,方法在新建的事务中执行。
5.5.2 Propagation.REQUIRES_NEW每一个方法都需要一个新的事务。如果调用方法时,存在一个事务,那么将该事务停止,开启一个新的事务执行调用方法,当新事务执行完毕以后,继续执行原有的事务。
如果本身没有事务,则直接开启一个新的事务。
5.5.3 Propagation.SUPPORTS支持,方法有事务可以正常执行,没有事务也可以正常执行。适用于查询操作。
5.6 Spring 中的事务控制通过设置隔离级别、传播行为、超时来进行事务控制。通过注解 @Transactional 来开启事务支持。
在类的上面和方法的方面开启事务支持。
5.6.1 注解中的属性propagation:事务的传播行为,通过propagation的枚举类,保存7种传播行为isolation:表示隔离级别,同样通过相关枚举类实现,默认为READ_COMMITreadOnly:boolean类型的值,表示数据库操作不是只读的,默认为falsetimeout:超时时间,默认为-1,单位是秒rollbackFor:表示回滚的异常类列表,是一个数组,数组中的每一个值是具体的class
框架先检查方法抛出的异常是不是在rollbacFor列表中,如果在会回滚;如果没有指定rollbackFor,框架则会直接检查这个异常是不是一个运行时异常,如果是,那么也会回滚更多的作用是增加其他异常的回滚 rollbackForClassName:表示回滚的异常类列表,它的值是异常类的名称,是String类型的值noRollbackFor:表示不需要回滚的异常类列表。是class类型noRollbackForClassName:不需要回滚的异常类列表名称。是String
5.6.2 使用方式 在配置类中在具体的类上 5.6.2 优势 适合中小型项目Spring 自己提供的事务控制使用方便,效率高 6.Spring Bean 的生命周期 6.1 Bean 的一生通过对属性的设置来进行事务控制。
实例化Bean
对于 ApplicationContext 容器,当容器启动结束后,通过获取 BeanDefinition 对象中的信息,实例化所有的配置文件中的所有 Bean。
设置对象属性(依赖注入)
实例化后的对象被封装在 BeanWrapper 对象中,紧接着,Spring 根据 BeanDefinition 中的信息,以及通过 BeanWrapper 提供的设置属性的接口完成依赖注入。
处理Aware接口
Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给Bean。
BeanPostProcessor
如果想对 Bean 进行一些自定义的处理,那么可以让 Bean 实现了 BeanPostProcessor 接口,
那将会调用 postProcessBeforeInitialization(Object obj, String s) 方法。如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
InitializingBean 与 init-method
如果 Bean 在 Spring 配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Objectobj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
DisposableBean
当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的destroy()方法;
destroy-method
底部如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法。