Spring入门笔记简要
总览:https://www.processon.com/view/link/605b14ab5653bb2225e6b0c5
一、Spring
1、概述
1、spring框架简介
为简化企业级开发而生,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。
2、优点
轻量、AOP支持、支持对各种框架的集成
3、体系结构
1、数据访问
2、Web开发
3、AOP
4、集成消息等
5、核心容器 IOC
2、IOC控制反转
ioc(Inversion of Control)是一种设计思想,把对象的创建、赋值、管理工作都交给代码之外的容器实现,也就是对象的创建是有其他外部资源完成。
控制:创建对象,对象赋值,对象之间的关系管理
反转:把原来开发人员管理、创建对象的权限转交给代码之外的容器实现,由容器代替开发人员管理对象,创建、赋值等
正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象‘;
容器:是一个服务器软件(Tomcat),一个框架(spring)等
目的:
减少对代码的改动,也能实现不同的功能,实现解耦合
所以:java中创建对象的方式:
1、new
2、反射,class的newInstance()方法,constructor的newInstance()方法
3、反序列化
4、克隆
5、ioc
ioc的技术实现:
DI 是 ioc 的技术实现
DI (Dependency Injection)依赖注入:只需要在程序中提供使用的对象名称就可以,至于对象如何在容器中创建、赋值、查找都由容器内部实现。
spring是使用的 DI 实现了 ioc 的功能,spring底层创建对象,使用的是反射机制。
2.1 由spring创建对象
实现步骤:
-
创建maven项目
-
加入maven依赖
spring依赖、junit依赖
-
创建类(接口和他的实现类)
和没有使用框架一样,就是普通类
-
创建spring需要使用的配置文件
声明类的信息,由spring创建和管理
仅限于初学的写法:
2.2 DI(依赖注入)的实现方式:
1、基于xml配置文件:在Spring配置文件中,使用标签和属性完成
2、基于注解的实现:使用spring中的注解,完成属性赋值
2.2.1 基于xml配置文件
适合经常需要修改
DI的语法分类:
1、 注入(set设值, 注入就是赋值):spring调用类的set方法,在set方法中实现属性的赋值(最常用)
2、构造注入,spring调用类的有参构造方法,创建对象,在构造方法中完成赋值
set注入:
- 简单类型的set注入:
property设值时name属性必须在相应的类中有set方法,且有set方法没有相应的变量也不会报错
2.引用类型的set注入:
引用类型的自动注入:
1、byName:java类中引用类型的属性名和spring容器中(配置文件)的id名称一样,且数据类型一致,这样的容器中的bean,spring能够赋值给引用类型
<bean id="xx" class="yy" autowire="byName"></bean>
让Student类中所有的引用类型都按照byName规则完成赋值
2、byType(按类型注入):java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系的,这样的bean能够赋值给引用类型
同源:1、java类中引用类型的数据类型和bean中的class值一样
2、java类中引用类型的数据类型和bean中的class值父子类关系的
3、java类中引用类型的数据类型和bean中的class值接口和实现类关系的
注:只能有一个符合条件的,多个会报错
class是Student类中引用的School的值:
构造注入
name属性实现构造注入
index属性实现
可以省略index=“”,但是必须按照参数的定义顺序书写
多配置文件优势:
-
每个文件的大小比一个文件小很多,效率高
-
避免多人竞争带来的冲突
按文件的分配方式:
-
按功能模块,一个模块一个配置文件
-
按类的功能,数据库相关的配置一个配置文件,做事务的功能一个配置文件,做service功能的一个配置文件
2.2.2 基于注解的实现
快捷,方便,适合不经常修改的
实现步骤:
-
加入maven依赖spring-context(间接加入了spring-aop依赖)
-
创建类,在类中加入spring 注解@Component(value=“xxx”),省略value=也可以(常用),如果只写@Component则会默认value=首字母小写的类名
还有 @Repository(用在持久层):放在dao的实现类,表示创建dao对象,dao对象是能访问数据库的 @Service(用在业务层):service对象是做业务处理,可以有事务等功能的 @Controller(用在控制器上):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象能够接受用户提交的参数,显示请求的处理结果 以上三个适用语法和@Component一样,都能创建对象,但是他们三个还有额外的功能
引用类型-@Autowired
引用类型@Resource
-
在spring配置文件中,加入一个组件扫描器的标签,说明注解在项目中的位置
需要指定多个包时:
3、AOP面向切面编程
3.1 动态代理
动态代理能创建对象,在原有类代码不变的情况下,实现功能的增加,功能增强。
3.1.1 JDK 动态代理
要求目标对象必须实现接口,java语言通过java.lang.reflect包提供三个类支持代理模式Proxy,Method和InvcationHandler,要求目标类和方法不能是final的,因为final不能被继承,不能被重写啊
实现步骤:
-
创建目标类,SomeserviceImpl目标类,给它的doSome,dother增加输出时间,事务。
-
创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
-
使用jdk中类Proxy ,创建代理对象。实现创建对象的能力。
作用:
-
在目标类源码不改变的情况下,增加功能
-
减少代码的重复
-
专注业务代码逻辑
-
解耦合,让业务与日志扥,事务非业务功能分离
3.1.2 CGLB动态代理(了解)
CDLIB(Code Generation Library)是一个开源项目,原理是生成目标类的子类,子类是代理对象。
3.2 AOP简介
AOP(Aspect Orient Programming) 面向切面编程,底层的实现就是采用动态代理模式实现的,采用了JDK的动态代理和CGLIB的动态代理。
Aspect:切面,给目标类增加的功能
特点:一般都是非业务方法,独立使用的
如何理解AOP:
以切面为核心,围绕切面来开发程序:
1)分析项目功能时找到切面
2)合理安排执行时间,(在目标方法前还是后)
3)合理的安排切面执行的位置, 在哪个类,哪个方法增加增强功能
3.3 AOP编程术语
-
AOP:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能
常见的切面功能有日志,事务,统计信息,参数检查,权限验证
-
JoinPoint:连接点,连接业务方法和切面的位置,就是某类中的业务方法
-
PointCut:切入点,指多个连接方法的集合,多个方法
-
目标对象:给哪个类的方法这个价功能,这个类就是目标对象
-
Advice:通知,表示切面功能 执行的时间,方法之前还是之后
切面三个关键要素:
- 切面的功能代码,切面干什么
- 切面的执行位置,使用PointCut表示切面执行的位置
- 切面的执行时间,使用Advice表示时间,在目标方法之前还是之后
3.4 AOP的实现
AOP是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
-
spring:spring内部实现了aop的规范,能做aop的工作
spring在处理事务时使用aop
在项目开发中很少使用spring的aop实现,因为spring的aop比较笨重
-
Aspect:一个开源的专门做aop的框架,spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能
aspectj框架实现aop的两种方式:
- 使用xml的配置文件:配置全局事务
- 使用注解,在项目中要做aop功能,一般使用注解
3.4 AspectJ框架的使用
-
切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)
在aspect框架中使用注解表示,也可以使用xml配置文件中的标签
<!-- 5个常用注解 @Before @AfterReturning @Around @After @AfterThrowing -->
-
表示切面执行的位置,使用的是切入表达式
execution(访问权限 方法返回值 方法参数 异常类型)
在其中可以使用以下符号: * :0至多个任意字符 .. : 用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包 + :用在类名后,表示当前类及其子类;用在接口名后,表示当前接口及其实现类
指定切入点为:任意公共方法 execution(public * *(..)) 指定切入点为:任意一个以set开始的方法 execution(* set*(..)) 指定切入点为:com.mhz.service包中的任意类中的任意方法,不包含子包 execution(* com.mhz.service.*.*(..)) 指定切入点为:com.mhz.service或者子包包中的任意类中的任意方法 execution(* com.mhz.service..*.*(..)) 指定切入点为:所有service包下的任意类的任意方法 execution(* *..service.*.*(..))
使用aspectJ实现aop的基本步骤:
-
新建maven项目
-
加入依赖
- spring依赖
- aspectj依赖
- junit单元测试(可有可无)
-
创建目标类:接口和他的实现类
要做的是给类中的方法增加功能
-
创建切面类:普通类
-
在类上加入注解@Aspect
-
定义方法,方法就是切面要执行的功能代码
在方法上面加入aspectj中的注解,例如@Before,
有需要指定切入点表达式execution()
-
-
创建spring配置文件:声明对象,把对象交给容器统一管理
声明对象可以使用xml配置文件或者注解
-
声明目标对象
-
声明切面类对象
-
声明aspectj框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能的。
<!--把对象交给spring容器,由spring容器统―创建,管理对象--> <! --声明目标对象--> <bean id="someservice" class="com.bjpowernode.bao1.SomeserviceImpl"/> <! --声明切面类对象--> <bean id="myAspect" class="com.bjpowernode.ba01.MyAspect”/> <!-- 声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。 创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象所以目标对象就是被修改后的代理对象 aspectj-autoproxy:会把spring容器内的所有目标对象,一次性都生成代理对象 --> <aop: aspectj-autoproxy />
-
-
创建测试类,从spring容器中获取目标对象(代理对象)
通过代理对象实现aop
六个注解:
1、@Before:
前置通知注解
属性: value,是切入点表达式,表示切面的功能执行的位置。
位置: 在方法的上面
特点:
1.在目标方法之前先执行的
2.不会改变目标方法的执行结果
3.不会影响目标方法的执行。
/*
* 指定通知方法中的参数:JoinPoint
* JoinPoint:业务方法,要加入切面功能的业务方法
* 作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。
* 如果你的切面 功能中需要用到方法的信息,就加入JoinPoint.
* 这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
*/
@Before(value = "execution(void *..SomeServiceImpl.doSome(String, Integer))")
public void myBefore(JoinPoint jp){
//获取方法的完整定义
system.out.println("方法的签名(定义)="+jp.getsignature());
system.out.println("方法的名称="+jp.getsignature().getName());//获取方法的实参
object args []= jp.getArgs();
for (object arg:args){
system.out.println("参数="+arg);
}
}
2、@AfterReturning:
后置通知定义方法,方法是实现切面功能的。
方法的定义要求:
1.公共方法 public
2.方法没有返回值
3.方法名称自定义
4.方法有参数的,推荐是object,参数名自定义
@AfterReturning:后置通知
属性:
1.value切入点表达式
2.returning自定义的变量,表示目标方法的返回值的。自定义变量名必须和通知方法的形参名一样。
位置:在方法定义的上面
特点:
1. 在目标方法之后执行的。
2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
3. 可以修改这个返回值
@AfterReturning(value="execution(* *..SomeServiceImpl.doOther(..))",returning="res")
// 此处returning的res名称=Object的res名称就行
public void myAfterReturing(object res){
// object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
// 思考:如果是对类对象res的更改会不会影响在程序执行后得到的输出结果?
system.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
if(res.equals("abcd"))
{
//做―些功能
}
e1se
{
//做其它功能
}
}
3、@Around
环绕通知
方法的定义格式:
1.public
2.必须有一个返回值,推荐使用object
3.方法名称自定义
4.方法有参数,固定的参数ProceedingjoinPoint
@Around:环绕通知
属性:value切入点表达式位宣:在方法的定义什么
特点:
1.它是功能最强的通知
2.在目标方法的前和后都能增强功能。
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果。影响最后的调用结果
等同于jdk动态代理的,InvocationHandler接口
参数:ProceedingJoinPoint 等同于Method
作用:执行目标方法
返回值:就是目标方法的执行结果,可以被修改
@Around(value = "execution(* *..SomeService1mpl.doFirst(..))")
public object myAround(ProceedingJoinPoint pjp) throws Throwable {
// 获取第一个参数值
Object[] args = pjp.getArgs();
String name = "";
if(args != null && args.length > 1){
Object arg = args[0];
name = (String)arg;
}
//实现环绕通知
object result = null;
system.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
//1.目标方法调用
if("xxx".equals(name)){
// 控制是否执行目标方法
result = pjp.proceed(); //method.invoke(); object result = doFirst();
}
system.out.println("环绕通知:在目标方法之后,提交事务");
//2.在目标方法的前或者后加入功能
//返回目标方法的执行结果
return result;
}
4、 @AfterThrowing
异常通知:
1、public
2、没有返回值
3、方法,名称自定义
4、方法有一个Exception,如果还有就是JoinPoint
@AfterThrowing:异常通知
属性:1、value
2、throwing自定义变量,表示目标方法抛出的异常对象,变量名和方法的参数名一样
特点:1、在目标方法抛出异常时执行
2、可以做异常的监控程序,如果有异常,可以发邮件,短信通知等
执行时:
没有异常就走正常逻辑,有异常就走定义的@AfterThrowing注解的方法
try{
SomeServiceImpl.doSecond(..);
}
catch(Exception ex){
myAfterThrowing(ex);
}
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
public void myAfterThrowing(Exception ex){
system.out.println("异常通知:方法发生异常时,执行: "+ex.getMessage());//发送邮件,短信,通知开发人员
}
5、@ After
最终通知
方法的定义格式
1.public
2.没有返回值
3.方法名称自定义
4.方法没有参数,如果还有是JoinPoint
@After:最终通知
属性:value 切入点表达式
位置:方法上面
特点:
1、总是执行
2、目标方法后执行,即使抛出了异常
类似于:
try/catch中的finally代码块
@After(value = "execution(* *..SomeserviceImpl.doThird(..))")
public loidmyAfter(){
//一般做资源清除工作的。
systemyout.println("执行最终通知,总是会被执行的代码");
}
6、 @PointCut
定义管理切入点
如果项目中很多个切入点表达式是重复的,,使用@PointCut
属性:value 切入点表达式
位置:方法上面
特点:
当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
@Pointcut(value = "execution(* *..SomeserviceImpl.doThird(..))”)
private void mypt(){
//无需代码,
}
// 然后:
@Before(value="mypt()")
public void myBefore(){
}
4、Spring事务
4.1 Spring的事务管理
-
什么是事务?
指的是一组sql语句的集合,集合中有多个sql语句,增删查改,希望这些sql语句斗殴成功或者都失败,这些sql 的执行是一致的,作为一个整体执行。
-
什么时候用到事务?
当操作涉及多个表或者多个sql语句的增删改,需要保证这些sql语句都成功才能完成功能,或者都失败,保证操作是符合要求的。
在java代码中写程序,控制事务,需要写在service类的业务方法上,因为业务方法汇调用多个dao方法,执行多个sql语句
-
jdbc、Mybatis访问数据库处理事务?
//jdbc Connection conn; conn.commit(); conn.rollback(); //mybatis SqlSession.commit(); SqlSession.rollback();
-
问3中处理事务的方式是不同的,所以有哪些不足:
-
不同的数据库访问技术,处理事务的对象、方法不同,需要掌握了解不同数据库访问技术使用事务的原理
-
掌握多种数据库中事务的处理逻辑,提交、回滚等
多种数据库访问技术,有不同的事务处理的机制,对象、方法
-
-
解决不足
spring提供一种统一处理事务的统一模型,能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
使用spring的事务处理机制,可以完成mybatis、hibernate等访问数据库的事务处理。
-
处理事务,需要怎么做,做什么
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了-
事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager ,定义了事务重要方法commit , rollback
实现类: spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的是DatasourceTransactionManagerhibernate 访问数据库—-spring创建的是HibernateTransactionManager
怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类,在spring的配置文件中使用声明就可以了例如,你要使用mybatis访问数据库,你应该在xml配置文件中<bean id="xxx" class=" . ..DataSourceTransactionManager">
-
业务方法需要什么样的事务,说明需要事务的类型。
说明方法需要的事务:
1)事务的隔离级别:
DEFAULT:采用 DB默认的事务隔离级别。Mysql的默认为REPEATABLE_READ;Oracle默认为 READ_COMITTED.READ
UNCOMMITTED:读未提交。未解决任何并发问题。
READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。 REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读SERIALIZABLE:串行化。不存在并发问题。
2)事务的超时时间:表示一个方法的最长执行时间,如果方法执行超过了时间,事务就回滚
3)事务的传播行为:控制业务方法是不是有事务的,是什么样的事务
7个传播行为,表示业务方法调用时,事务在方法之间是如何使用的
<!--!!! PROPAGATION REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATIONsUPPORTS --> PROPAGATIONMANDATORY PROPAGATION_NESTED PROPAGATION_NEVER PROPAGATIONNOT_SUPPORTED
-
事务提交事务,回滚事务的时机
1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit2)当你的业务方法抛出运行时异常或ERROR,spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常,例如Ntul1PointException , MunberFormatzxcept.
3)当你的业务方法抛出非运行时异常,主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException,SQLException
-
总结Spring事务:
- 管理事务的是 事务管理 和他的实现类
- spring的事务是一个统一模型
- 指定使用的事务管理器的实现类,使用
- 指定哪些类,哪些方法需要加入事务的功能
- 指定方法需要的隔离级别,传播行为,超时
4.2 Spring的事务传播机制
**Required **解义:如果上下文中已经存在事务,就加入到事务当中
上下文有事务 | 上下文没有事务 | |
---|---|---|
Required | 加入到事务当中 | 新建事务 |
Supports | 加入到事务当中 | 非事务方式运行 |
Mandatory | 必须要有事务 | 抛出异常 |
Requires_New | 新建事务 | 新建事务 |
Not_Supported | 事务挂起,方法结束后恢复事务 | |
Never | 抛出runtime异常,强制停止执行 | |
Nested | 嵌套事务执行 | 新建事务 |
二、SpringMVC
1.1 MVC在B/S下的应用
mvc是一个设计模式
1.2 springmvc框架
过程
1、发起请求到前端控制器
2、前端控制器请求HandlerMapping查找Handler
根据xml配置、注解进行查找
3、处理器映射器HandlerMapping向前端控制器返回Handler
4、前端控制器用处理器适配器去执行Handler
5、理器适配器执行Handler
6、Handler执行完成给适配器返回ModelAndView
7、处理器向前端控制器返回ModelAndView
ModelAndView是springmvc框架的一个底层对象,包括model 和 view
8、前端控制器请求视图解析器去进行视图解析
根据逻辑视图名解析程真正的视图jsp
9、视图解析器向前端控制器返回View
10、前端控制器进行视图渲染
视图渲染将模型数据(在ModelAndView中)填充到request域
11、前端控制器向用户返回响应结果
组件:
1、前端控制器DispatcherServlet,(不需要程序员开发)
接收请求,响应结果,相当于转发器,中央处理器
减少其他组件之间的耦合度
2、处理器映射器HandlerMapping,(不需要程序员开发)
根据请求的url查找Handler
3、处理器适配器HandlerAdapter(不需要程序员开发)
按照特定规则(HandlerAdapter要求的规则)去执行Handler
编写Handler时按照HandlerAdapter的要求去做才能正确执行Handler
4、处理器Handler,(需要程序员开发)
5、视图解析器View Resolver
视图解析,根据逻辑视图域名解析成真正的视图View
6、视图View,(需要程序员开发jsp)
是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)
2、 json数据交互
2.1、使用json交互的原因:
json数据格式在接口调用中、html页面中常用,json格式简单,解析比较方便
2.2、springmvc进行json交互
springmvc中使用jackson的jar包进行json转换
客户端请求key / value串 | |
---|---|
请求的是json串 contenttype=application/json |
请求的是key / value contenttype= |
@RequestBody将json串转成java对象 | 不需要@RequestBody将json串转成java对象 |
@ResponseBody将java对象转成json输出 | @ResponseBody将java对象转成json输出 |
最后都输出json数据,为了在前端页面方便对请求结果进行解析 |
1、请求json、输出json,要求请求的是json串,所以在前端页面中需要将请求的内容转成json,不太方便
2、请求key / value ,输出json,比较常用
3、Restful支持
restful是一种开发理念,是对http的一个诠释,即表现层状态的转化
对url进行规范,写restful格式的url,就是简洁
非rest的url:http://…/finditems.action?id=001
rest的url风格:http://…/items/001