1.Spring框架介绍
1.1 传统项目架构
传统项目遵循MVC开发模型。
view层与用户交互,显示数据或将数据传输给view层。
controller-service-dao
1.2 传统项目架构缺陷
存在耦合性,不易于程序扩展
service层代码变更,controller中代码需要修改
程序扩展,尽可能遵循开闭原则,对扩展开放,对修改关闭。
例如:用户注册功能中,需进行实名认证。存在2种用户,已注册未认证和尚未注册的用户。为避免对已有业务的改动,可新建一个service类,重写注册方法。则controller中需注入新的service对象,相关的controller都需要变动,并且每个controller都需要创建对象,造成资源的浪费。
面向过程-面向对象-面向接口-面向切面(组件)
1.3 解决方案
定义1个需要创建的对象的清单和一个存储对象的容器,根据清单创建对象,然后将所有service对象存储,每个controller只需要去容器中获取serivce对象,既解决了单例问题又提高了性能
spring框架源码中对象容器工厂:ApplicationContext,是 Interface
spring核心类:ClassPathXmlApplicationContext,此类实现了ApplicationContext接口
初步代码测试
Applicationtext applicationtext = new ClassPathXmlApplicationContrxt("spring-context.xml");
User user = applicationtext.getBean("u1",User.class);
2.Spring介绍
2.1 简介
由于软件开发的复杂性而创建的,初衷是为了让软件开发更简单。
spring使用的是简单的javabean来完成以前只能由EJB完成的事情。
spring不仅仅局限于服务器端的开发。
从简单性、可测试性、松耦合性角度而言,绝大部分java应用都可从spring中受益
Web Service有2个显著特点:数据格式是xml格式;配置繁琐,对象关联性大,需在配置文件中各种配置。
基于上述原因,spring提出了IOC/DI(控制反转/依赖注入),AOP(面向切面编程)
spring可在任何类型的部署平台上为基于java的应用程序提供全面的编程和配置模型。
spring的关键元素是在应用程序级别的基础架构支持:spring专注于企业应用程序的"管道",使得团队专注于应用程序级别的业务逻辑,而无需与特定的部署环境建立不必要的联系。
2.2 核心组件
核心容器:由spring-core、spring-beans、spring-context、spring-context-support、spring-expression(SpEL、Spring表达式语言、Spring Expression Langusge)等模块组成
spring-core:框架的基本组成部分,包含IoC和依赖注入功能
spring-beans:提供BeanFacory,工厂模式的微妙实现,移除了编码式单例的需要,并且可以将配置和依赖从实际编码逻辑中解耦。
spring-context:建立在core和beans的基础上,以一种类似于JNDI注册的方式访问对象。Context模块集成自Bean模块,并且添加了国际化(例如资源约束)、事件传播、资源加载和
透明地创建上下文(比如通过Servlet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。
spring-context-support提供了第三方库集成到spring的支持,比如缓存(EhCache,Guava,JCache)、邮件(JavaMail)、调度(CommonJ,Quartz)、
存储引擎(FreeMarker,JasperReports,Velocity,都是用于生成报告和文档的java模板引擎)。
补充资料:
JNDI:Java Naming and Directory Interface ,java命名和目录接口,是java提供的一种标准API,用于访问命名和目录服务
EJB:Enterprise JavaBeans,java ee平台的核心技术之一, 用户开发可伸缩、事务性、安全的企业级应用程序
JMX:Java Management Entensions,java管理扩展,是用于管理和监控java应用程序的标准框架。
EhCache:开源java缓存框架,旨在提高程序性能和扩展性。
Guava:google开发的一套核心java库,提供丰富且可靠的扩展功能,例如集合、缓存、并发工具、I/O支持
JCache:java temporary caching api,java官方的标准缓存规范,旨在为java程序提供统一的缓存管理接口
JavaMail:sun公司发布的用于处理电子邮件的api,提供了处理电子邮件相关的编程接口
CommonJ:是BEA和IBM联合推出的规范,可简化应用程序的开发、简化数据访问、处理多线程和定时器、
Quartz:开源的作业调度框架,由OpenSymohony开源组织开发,是完全用java编写的作业调度框架。用于定时任务调度的场景。
FreeMarker:生成HTML、XML、邮件、配置文件等
JasperReports:生成复杂报表
Velocity:用于web开发、邮件模板、代码生成等
spring-expression:提供表达式语言,用于在运行时查询和操作对象图。是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、
访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从spring ioc容器检索对象、列表的投影、选择及聚合等。
依赖关系为:beans依赖于core,expression依赖于core和beans,context依赖于core、beans、aop、expression
数据访问/集成:
数据访问/集成层包括JDBC、ORM、OXM、JMS和事务处理模块,细节如下
JDBC:Java Data Base Connectivity,此模块提供了JDBC抽象层,消除了冗长的的JDBC编码以及对数据库供应商特定错误代码的解析
ORM:Object Relational Mapping,提供了对象关系映射API的集成,包括JPA、JDO、Hibernate等。使这些ORM框架和spring的其他功能整合,比如事务管理
OXM:Object XML Mapping,提供了对OXM实现的支持,比如JAXB,Castor,XML Beans,JiBX,XStream等
JMS:Java Message Service,包含生产(produce)和消费(consume)消息的功能,从spring4.1开始,集成了spring-message模块
事务模块为实现特殊接口类及所有的POJO支持编程式和声明式事务管理(编程式事务式自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务式通过注解或配置
由spring自动处理,编程式事务粒度更细)
Web:
由Web、Web-MVC、Web-Socket、Web-Protlet组成
Web:提供面向web的基本功能和面向web的应用上下文,比如多部份(multipart)文件上传功能、使用Servlet监听器初始化IOC容器等。还包括HTTP客户端以及spring远程调用中web
Web-MVC:为web应用提供了模型视图控制(MVC)和REST Web服务的实现。spring的mvc框架可以使领域模型代码和web表单完全地分离,且可以与sping框架的其他功能进行集成。
Web-Socket:为WebSocket-based提供了支持,而且在web应用程序中提供了客户端和服务器端之间通信的两种方式
Web-Protlet:提供了用于Protlet环境的MVC支持,并反应了spring-webmvc模块的功能。
其他:
其他一些重要的模块,例如AOP、Aspects、Instrumentation、Messaging、测试模块
AOP:提供了面向切面的编程实现,允许定义方法拦截器和切入点对代码进行干净地解耦,从而让功能代码彻底接口出来。
Aspects:提供了与AspectJ的集成,是强大且成熟的面向切面编程(AOP)框架
Instrumentation:在一定的应用服务器中提供了类Instrumentation的支持和类加载器的实现
Messaging:为STOMP提供了支持作为在应用程序中WebSocket子协议的使用。也支持一个注解编程模型,是为了选择和处理来自WebSocket客户端的STOMP信息
注:STOMP协议是流文本定向消息协议,用于面向消息的中间件。
测试模块:支持对具有JUnit或TestNG框架的spring组件的测试
3.Spring的IOC的使用 IOC、DI
IOC:控制反转。将对象的创建、初始化、销毁等一系列的生命周期交给spring管理
面向过程-面向对象-面向接口-面向组件(切面)-面向服务-面向百度
3.1 基本使用
创建项目导入spring-ioc核心jar包:org.springframework.spring-context
创建实体类
创建spring核心配置文件spring-context.xml/applicationContext.xml,并将实体类作为bean注入
测试:通过 ClassPathXmlApplicationContext获取配置文件得到ApplicationContext对象,然后通过getBean方法实现从容器中获取实体类对象
3.2 Bean标签属性介绍
id:bean的唯一标识,一个bean,id值只能有1个。整个IOC容器中,id值不可重复,使用名称作为key
name:bean的一个名称,可以存在多个,多个之间使用逗号分隔。不论bean有无定义name属性,默认id都会当作name
class:bean具体的类型,包名和类名组成
scope:bean的作用域,如果不写scope,默认为单例。
prototype 非单例,每次获取都会创建1个新的bean对象
singleton 单例,多次获取永远同一个bean,默认
request 一次请求,基于web项目的bean的作用域
session 一次会话,基于web项目的bean的作用域
lazy-init:延迟初始化(懒加载)。默认只要加载了配置文件,bean对象就会被初始化。lazy-init则是获取时才会初始化。只针对单例模式有效,非单例每次获取都会创建,延迟初始化没意义。
depends-on:初始化时依赖的对象,当前对象初始化前需先初始化depends-on指定的对象
init-method:对象初始化后,调用的方法
destory-method:对象销毁时,调用的方法
autowire:属性自动装配,byName:根据属性名称装配,byType:根据类型装配
autowire-candidate:是否允许作为自动装配的候选项,true-是,false-否
primary:优先使用该bean,spring需要支持使用类型查找对象,一个大类型下,可能存在多个小类型。如果根据大类型装配对象时,不确定使用那个具体的对象,可根据primary设置优先级
3.3 Bean对象创建的4种方式
构造方法创建:默认的创建方式。例如 <bean id="user" class="com.ss.user"/>
静态工厂创建:需要有一个工厂类,工厂类中有一个创建对象的静态方法。class指向的是工厂类,factory-method指向的是工厂方法
例如 <bean id="user2" class="com.ss.factory.UserStaticFactory" factory-method="getUser"/>
非静态工厂创建:需要有一个工厂类,工厂类中有一个创建对象的普通方法。
例如:<bean id="userFactory" class="com.ss.factory.UserFactory"/> <bean id="user3" factory-bean="userFactory" factory-method="getUser"/>
注解创建:
组件注解
@Component("aa") 表示该类是一个被spring管理的组件。为了开发中代码可读性更高,spring基于分层思想,将需要创建的组件进行了以下分类:
@Controller 表示该类是controller层的类。注意在使用springmvc时,所有的Constroller,必须使用@Controller注解
@Service 表示该类是业务层的类
@Respority 表示该类是操作数据层的类
以上4注解是spring中定义的创建对象的注解,都可创建对象。类确定有明确的作用,建议使用相应的注解。如果无法区分类所属层次,则可使用@Component注解。
注解使用步骤
开启注解扫描:spring核心配置文件中,开启注解扫描,让spring将被注解修饰的类,创建对相关。<context:component-scan base-package="com.ss">
类上添加相应注解
注(创建方式优先顺序):优先使用注解,如类似于第三方类中,注解无法使用(无法在别人源码上添加spring注解),则使用XML配置形式,配置第三方类的Bean信息。
3.4 IOC属性注入的3种方式
属性注入定义:为对象属性设置值。
构造方法属性注入:DI(依赖注入,给对象的属性赋值)
无参构造方法示例:<bean class="com.ss.entity.user"/>
有参构造方法示例:<bean id="user" class="com.ss.entity.user"><constructor-arg name="sex" value="男"/></bean> 有一个sex属性的user对象
set方法属性注入:property表示属性,name表示属性对应的set方法名,去除set前缀,首字母小写,并非真正属性名
示例查看参考资料的 3.4.2
注解属性注入:为了简化属性的注入,spring提供注解:@Autowired,spring会自动从IOC容器中,为这个属性查找相应类型的值,进行注入。
先开启包的注解扫描,<context:component-scan base-package="com.*">
使用注解进行自动注入。
注意:使用自动注入时,可在bean标签上,配置autowire,但是必须有该属性的set方法。@Autowired是不需要set方法的。
如果是在xml中注入对象,值使用ref属性。value属性只支持boolean、数字、字符串等。
3.5 常见类型的属性注入
spring中提供了丰富的标签,进行各种属性的注入。常见的类型:数字、字符串、boolean、数组、set、list、map、properties
示例:
<bean id="person" class="con.ss.entity.person">
<property name="id" value="1"/> <!-- Interger 注入-->
<property name="name" value="张三"/> <!-- String 注入-->
<property name="sex" value="true"/> <!-- boolean 注入-->
<!-- 数组 注入-->
<property name="likes">
<array>
<value>足球</value>
<value>篮球</value>
<value>排球</value>
</array>
</property>
<!-- set类型 注入-->
<property name="friends">
<set>
<value>Lucy</value>
<value>rose</value>
<value>jack</value>
</set>
</property>
<!-- list 类型 注入-->
<bean id="dream1" class="com.bjpowernode.domain.Dream">
<property name="title" value="数钱数到手抽筋" />
</bean>
<property name="dreams" >
<list>
<bean class="com.bjpowernode.domain.Dream">
<property name="title" value="解放全人类" />
</bean>
<bean class="com.bjpowernode.domain.Dream">
<property name="title" value="世界和平" />
</bean>
<ref bean="dream1"/>
</list>
</property>
<!-- map 类型 注入-->
<property name="house">
<map>
<enter key="b" value="北京"/>
<enter key="s" value="上海"/>
<enter key="g" value="广州"/>
</map>
</property>
<!-- properties 类型 注入-->
<property name="properties" >
<props>
<prop key="driver">驱动</prop>
<prop key="url">url</prop>
</props>
</property>
</bean>
3.6 使用IOC容器,改造传统项目
xml配置版:
创建项目并添加org.springframework.spring-context依赖,此依赖可传递出aop、beans、core、expression
创建controller、service、dao、entity,并在spring-context.xml中进行注入bean
最后通过ClassPathXmlApplicationContext的实例化方法获取ApplicationContext对象,再通过getBean方法获取Controller对象,最后调用方法即可
注解版:
spring-context.xml中开启组件扫描<context:component-scan base-package="com.*">
controller类上使用@Controller注解、dao类上使用@Respority、service实现类上使用@Service
最后测试方法和"xml配置版"一致
4.AOP
4.1 AOP简介:面向切面(面向组件)
DefaultAopProxyFactory 代理
静态代理:每个被代理类都需要创建对应的代理类。随着程序的扩展,代理类会变多,为了解决此问题,java中提供了动态代理技术,开发者无需自定义代理类,代理类由JDK动态创建,
开发者只需指定被代理的类即可。
切面(aspect):除了核心类以外的其他类
通知(advisor):切面中的方法
核心类:项目中不可省略的类
核心方法:核心类中的方法
连接点:核心类中的某个方法
切入点(pointcut):某个包下某个类的某一批方法
代理(proxy):将多个切面和核心类组合在一起,形成的新的类
织入(weaver):书写代理类的过程
4.2 动态代理
JDK动态代理
Proxy:此类提供了"创建代理类和代理类的对象"的方法
创建1个代理类并返回代理对象的方法:
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler invocationhandler)
loader:类加载器,指定类加载器,用于精确的定位类
interfaces:接口Class类,使用JDK的反射,必须有接口
invocationhandler:代理的处理器,每一个代理类都有1个关联的处理器
InvocationHandler 是每个代理类对应的处理器
Object invoke(Object proxy,Method method,Object[]args);
Object:方法调用的返回值,可作为被代理的方法调用的返回值
proxy:代理类对象
method:目标类中代理的方法
args:目标类中被代理方法的运行参数
代码示例:创建目标类接口,创建目标类,创建代理类处理器实现InvocationHandler接口重写invoke方法,测试(创建目标类、创建处理器、创建具体的代理类和对象,调用代理类方法)
JDK动态代理不足:JDK使用动态代理,必须有类的接口。因为生成的代理需要实现此接口,随后生成的代理类对象,才能转换为代理目标的接口对象,
然后根据接口方法,调用处理器invoke方法
Cglib动态代理
为了弥补JDK动态代理的不足,三方组织封装了一套cglib工具包,此包不基于接口,基于父子继承,通过重写的方式扩展方法,子类是工具自动生成。
早期Cglib性能相对于JDK动态代理高一些。JDK进行一些列优化,目前spring默认使用的是JDK动态代理,也支持Cglib动态代理。
Cglib代码使用方式
MethodInterceptor:cglib中,提供对方法执行拦截的接口。intercept是对具体方法进行拦截处理的方法。
public Object intercept(Object obj,java.lang.reflect.Methon method,Object[]args,MethodProxy proxy);
obj:增强类的对象
method:目标方法
proxy:用于回调的方法的对象
代码示例:
pom.xml中引入cglib的jar包,创建被代理目标类,创建方法拦截器类(实现MethodInterceptor接口,重写intercept方法)
测试:先创建cglib下的增强类工具Enhancer,可创建代理类对象,使用增强类工具设置调用时回调,使用增强类工具创建代理类对象,最后通过代理类对象调用方法即可
Cglib动态代理不足:不论是JDK动态代理,还是第三方Cglib动态代理,都需要开发者编写程序。程序结构大同小异,重复造轮子。基于这样的情况,spring中提供了2种方式:
xml配置形式和注解形式。这种模式就是spring aop技术。底层依然是动态代理。
4.3 Spring中AOP的配置
spring中,AOP的配置有两类:xml配置和注解配置
XML配置也分为两种,一种是Spring的原生支持,一种是Spring的aspects这个相关的框架
AOP相关概念:
连接点(JoinPoint):被拦截的点,spring中这些点就是方法,因此spring只支持方法类型的连接点
切入点(PointCut):对JoinPoint进行拦截的定义,具体拦截的位置
增强/通知(Advice):对具体的连接点进行扩展的功能。一般对方法进行增强,分为方法执行前和方法执行后,或者发生异常执行等。被分为:前置增强、后置增强、环绕增强、异常增强
引介(Introduction):一种特殊的Advice,不修改代码的前提下,可在运行期为类动态的添加一些方法或Field
目标(Target):被代理的类(需要增强类)
织入(Weaving):把advice应用到target的过程
代理(Proxy):使用AOP配置后产生的类
切面(Aspect):切点和增强整合成切面
Spring自身AOP具体配置
引入aop相关jar包(spring-context、spring-context-support、spring-aspects)
定义增强类
前置增强,自定义类实现 MethodBeforeAdvice,重写before方法
后置增强,自定义类实现 AfterReturningAdvice,重写afterReturning方法
环绕增强,自定义类实现 MethodInterceptor,重写invoke方法,方法中调用 MethodInvocation invocation.proceed()方法(目标方法)前后分别为前置增强和后置增强
异常增强,自定义类实现 ThrowsAdvice,重写afterThrowing方法
目标类:目标接口和目标实现类,目标实现类中包含待增强的方法
aop配置:spring-context.xml中定义目标类对象、4个增强类对象,将增强类对象织入到目标类对象的待增强方法中
测试:通过ClassPathXmlApplicationContext获取Application对象,然后通过getBean获取目标对象,最后调用目标对象的待增强方法,验证有无调用增强方法即可
AspectJ框架AOP配置
原生的spring中,每种增强都需要单独定义一个类实现相应的接口。增强类本身很庞大,而且方法名固定。
由于上述原因,AspectJ提供了相对更加灵活的方式。在AspectJ中,只需定义一个增强类即可,并且方法名称可任意定义。
引入AspectJ相关jar(spring-context、spring-context-support、spring-aspects)
编写增强类:自定义类即可,无需继承类或实现接口。方法名可自定义(通常定义为beforeAdvice、afterAdvice、aroundAdvice、exceptionAdvice)
编写目标类:目标接口和目标实现类,目标实现类中包含待增强的方法
配置AspectJ的增强配置:spring-context.xml中定义目标类、增强类对象、AOP配置(先配置切点,最后将需待增强的方法和切点进行绑定匹配)
测试:通过ClassPathXmlApplicationContext获取Application对象,然后通过getBean获取目标对象,最后调用目标对象的待增强方法,验证有无调用增强方法即可
AspectJ的AOP注解方式
引入jar包(spring-context、spring-context-support、spring-aspects)
定义增强类,类上使用@Aspect、@Component注解修饰。前置、后置、环绕、异常增强方法分别在方法上使用@Before(value="")、@AfterReturning、@Around、@AfterThrowing注解修饰
定义目标类:目标接口和目标实现类,实现类上使用@Component修饰
开启相关注解:
spring-context.xml中编写 <context:component-scan base-package="com.*"/> <aop:aspectj-autoproxy proxy-target-class="true"/> 分别开启组件扫描、开启aspectj相关注解
测试:通过ClassPathXmlApplicationContext获取Application对象,然后通过getBean获取目标对象,最后调用目标对象的待增强方法,验证有无调用增强方法即可
5.Spring整合mybatis
本质上就是将mybatis交给spring管理,即mybatis的SqlSession对象,放入IOC容器中,并可利用自动装配功能,为每个数据库操作层,注入SqlSession。
并且spring内置可以有代理,可根据sqlsession对象及其数据操作接口,创建mapper接口的代理对象,此时mapper的代理在IOC容器中,便可将mapper接口的对象注入到service中
5.1 多XML版本
此版本,mybatis和spring的配置文件都是单独的
引入相关jar包:
spring相关jar包:spring-context、spring-context-support、spring-aspects、spring-jdbc
mybatis相关jar包:org.mybatis.mybatis
数据库相关jar包:mysql.mysql-connector-java
日志相关jar包:commons-logging、log4j、org.apache.logging.log4j.log4j-core
spring和mybatis整合包:org.mybatis.mybatis-spring
mybatis分页插件包:com.github.pagehelper.pagehelper
定义相关类:
实体类、mapper接口、service接口及实现类
注入类:spring-context.xml中注入实体类、mapper、service接口及实现类
测试:通过ClassPathXmlApplicationContext获取Application对象,然后通过getBean获取目标对象,最后调用目标对象的待增强方法,验证有无调用增强方法即可
相关配置文件:
jdbc配置文件(数据库连接信息)、日志配置文件(全局日志配置、mybatis日志配置、控制台输出日志)、mybatis核心配置文件、mybatis映射文件、
spring核心配置文件(引入jdbc配置文件、配置数据源、配置SqlSessionFactoryBean对象、扫描所有mapper接口自动创建mapper的代理对象、配置service层对象)
5.2 Spring配置文件版
使用spring配置文件,取代mybatis核心配置文件
导入的jar包和相关类完全一致,jdbc配置文件和日志配置文件也相同。
区别只有将mybatis的核心配置文件的配置,移动到spring核心配置文件中
spring核心配置文件:引入jdbc配置文件、配置数据源、配置日志、配置SqlSessionFactoryBean对象(包含mybatis核心配置文件内容)、扫描所有mapper接口自动创建mapper的代理对象、
配置service层对象
6.声明式事务
spring中可以管理数据源、管理连接,管理事务。
spring单独分了一个模块来管理事务。
spring简化了事务开发,只需通过配置的方式,即可对事务进行统一完善的管理。
spring事务管理基于spring的aop技术
spring的声明式事务,有两种方式:xml配置方式、注解
6.1 XML配置方式
在1934行对应的spring核心配置文件基础上,添加 事务管理器、配置事务增强、配置切面
6.2 注解版声明式事务
spring为了简化事务,提供注解@Transactional,此注解修饰类,如果修饰某类,则该类的所有方法都会使用事务。也可修饰方法,被修饰的方法会使用事务。
使用方式:配置事务管理器、开启事务注解(<tx:annotation-driven transaction-manager="transactionManager"/>)、需要使用事务的地方使用@Transactional注解修饰即可
事务的传播级别及说明、描述:
REQUIRED:必须的;默认值,表示当前若存在事务则使用当前事务,若不存在则创建新事务
SUPPORTS:支持的;当前存在事务则在当前事务中执行,不存在则在非事务中执行
MANDATORY:强制性;必须在事务中执行,当前没事务则抛出异常
NEVER:绝不;一定不在事务中执行,存在事务则报错
NESTED:嵌套的;若存在事务,则创建子事务,嵌套在当前事务中,外层事务出现异常会回滚子事务,子事务出现异常,不影响外层事务
REQUIRES_NEW:必须新建;当前无论是否有事务,都会创建1个新的事务,在新的事务中执行,只支持JtaTransactionManager
NOT_SUPPORTED:不支持事务,当前无论是否存在事务,都不会在事务中执行,只支持JtaTransactionManager
代码中示例:@Transactional(propagation=Propagation.NESTED) 表示当前存在1个事务,就创建1个子事务,嵌套在当前事务中。
事务的传播行为,是指事务会发生传递。例如:A方法存在事务,A调用B方法,那么B方法也会在事务中执行,这种便是事务的传播行为。
参考资料:https://blog.youkuaiyun.com/m0_47946173/article/details/134545542