Spring框架详解
一、spring框架的概述以及spring中基于XML中的IOC配置
二、spring中基于注解的IOC和ioc的案例
三、spring中的aop和基于XML以及注解的AOP配置
四、spring中的JdbcTemlate以及Spring事务控制
一:spring框架的概述以及spring中基于XML中的IOC配置
1.spring的概述
spring是什么
Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:
反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring
MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名
的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。
spring的两大核心
spring的发展历程
1997 年 IBM提出了EJB 的思想
1998 年,SUN制定开发标准规范 EJB1.0
1999 年,EJB1.1 发布
2001 年,EJB2.0 发布
2003 年,EJB2.1 发布
2006 年,EJB3.0 发布
Rod Johnson(spring之父)
Expert One-to-One J2EE Design and Development(2002)
阐述了 J2EE 使用EJB 开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了 J2EE 开发不使用 EJB的解决方式(Spring 雏形)
2017 年 9 月份发布了 spring 的最新版本 spring 5.0 通用版(GA)
spring的优势
方便解耦,简化开发
通过Spring提供的 IoC容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造
成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可
以更专注于上层的应用。
AOP编程的支持
通过 Spring的 AOP功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以
通过 AOP 轻松应付。
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,
提高开发效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可
做的事情。
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz
等)的直接支持。
降低 JavaEE API的使用难度
Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的
使用难度大为降低。
Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以
及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
2.spring体系结构
3.程序的耦合及解耦
曾经案例中问题
工厂模式解耦
4.IOC概念和spring中的IOC
spring中基于XML的IOC华宁搭建
5.依赖注入(Dependency Injection)
1.1.程序的耦合与jdbc
/**
程序的耦合
耦合:程序间的依赖关系
包括:
类之间的依赖
方法间的依赖
解耦:
降低程序间的依赖关系
实际开发中:
应该做到:编译期不依赖,运行时才依赖。
解耦的思
第一步:使用反射来创建对象,而避免使用new关键字
第二步:通过读取配置文件来获取要创建的对象全限定类名
*/
1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/eesy", "root", "1143809298");
3.获取操作数据库的预处理对象
PreparedStatement pstm = conn.prepareStatement("select * from account");
4.执行sql语句,得到结果集
ResultSet rs = pstm.executeQuery();
5.遍历结果集
while (rs.next()) {
System.out.println(rs.getString("name"));
}
6.释放资源
rs.close();
pstm.close();
conn.close();
1.2.factory
/**
一个创建Bean对象的工厂
Bean:在计算机英语中,有可重用组件的含义。
JavaBean:用java语言编写的可重用组件
javabean > 实体类
他就是创建我们的service和dao对象的。
第一个:需要一个配置文件来配置我们的service和dao
配置的内容:唯一表示=全限定类名(key=value)
第二个:通过读取配置文件中配置的内容,反射创建对象
我的配置文件可以使xml也可以是properties
*/
1.3.spring的ioc
/**
* 获取spring的Ioc核心容器,并根据id获取对象
*
*ApplicationContext的三个常用实现类:
* ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下、不在的话,加载不了。(实际开发更常用)
* FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
*
* AnnotationConfigApplicationContext:它是用于读取注解创建容器的。
*
* 核心容器的两个接口引发出的问题:
* ApplicationContext: 单例对象使用 采用此接口
* 它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
* BeanFactory: 多例对象使用
* 它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象
* @param args
*/
1.4 spring对bean对象的管理细节
<!-- spring对bean的管理细节
1.创建bean的三种方式
2.bean对象的作用范围
3.bean对象的生命周期
-->
1.4.1创建bean的三种方式
<!-- 创建Bean的三种方式-->
<!-- 第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,如果此时类中没有默认构造函数,则对象无法创建。
-->
<!-- <bean id="accountService" class="com.marchsoft.service.impl.AccountServiceImpl"></bean>-->
<!-- 第二种方式:使用普通工厂中的方法创建对象(使用某个类汇总的方法创建对象,并存入spring容器)-->
<!-- <bean id="instanceFactory" class="com.marchsoft.factory.InstanceFactory"></bean>-->
<!-- <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->
<!-- 第三种方法:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)-->
<!-- <bean id="accountService" class="com.marchsoft.factory.StaticFactory" factory-method="getAccountService"></bean>-->
1.4.2bean的作用范围调整
<!-- bean的作用范围调整
bean标签的scope属性
作用:用于指定bean的作用范围
取值:
singleton:单例的(默认值)
prototype:多例的
request:作用域web应用的请求范围
session: 作用域web应用的会话范围
global-session: 作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
-->
<!-- <bean id="accountService" class="com.marchsoft.service.impl.AccountServiceImpl" scope="prototype"></bean>-->
1.4.3bean对象的生命周期
<!-- bean对象的生命周期
单例模式
出生:当容器创建时对象出生
或者:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的声明周期和容器相同
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着。
死亡:当对象长时间不用,且没有别的对象引用时,有Java的垃圾回收器回收
-->
1.5.dependency Injection(依赖注入DI)
<!-- spring中的依赖注入
依赖注入:
Dependency Injection
IOC的作用:
降低程序间的耦合(依赖关系)
依赖关系的管理:
以后都交给spring来维护
在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
依赖关系的维护:
就成为依赖注入。
依赖注入:
能注入的数据:有三类
基本类型和String
其他bean类型(在配置文件中后者注解配置过的Bean)
复杂类型/集合类型
注入的方式:有三种
第一种:使用构造函数提供
第二种:使用set方法提供
第三种:使用注解提供(明天的内容)
-->
1.5.1构造函数注入
<!-- 构造函数注入:
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值(常用的)
==============以上是三个用于指定给构造函数中的那个参数赋值===============
value:用提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.marchsoft.service.impl.AccountServiceImpl" >
<constructor-arg name="aaa" value="泰斯特"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象-->
<bean id="now" class="java.util.Date"></bean>
1.5.2set方法注入(set方法注入)
<!-- set方法注入 更常用的方式
涉及的标签:property
出现的位置:bean标签的内部
标签的属性
name:用于指定注入时所调用的set方法名称
value:用提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象时有可能set方法没有执行
-->
<bean id="accountService2" class="com.marchsoft.service.impl.AccountServiceImpl2" >
<property name="name" value="test"></property>
<property name="age" value="25"></property>
<property name="birthday" ref="now"></property>
</bean>
1.5.3复杂类型的注入/集合类型的注入
<!-- 复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:
list array set
用于给Map结构集合注入的标签
map props
结构相同,标签可以互换
-->
<bean id="accountService3" class="com.marchsoft.service.impl.AccountServiceImpl3" >
<property name="myStrs">
<array>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</array>
</property>
<property name="myList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="mySet">
<set>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB" >
<value>bbb</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testC">ccccc</prop>
<prop key="testD">ddddd</prop>
</props>
</property>
</bean>
二、spring基于注解的IOC以及ioc的实例
学习基于注解的IOC配置,大家脑海里首先得有一个认知,即注解配置和项目了配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
环境搭建
1.spring中ioc的常用注解
2.案例中使用xml方式和注解方式实现单表的CRUD操作
持久层技术选型:dbutils
3.改造基于注解的ioc案例,使用纯注解的方式实现
4.spring和Junit整合
2.1.Dependency Injection(DI 依赖注入)
曾经XML的配置:
<bean id="accountService" class="com.marchsoft.service.impl.AccountServiceImpl"
cope="" init-method="" destroy-method="">
<property name="" value=""|ref=""></property>
</bean>
用于创建对象的
它们的作用就和在XMl配置文件汇总编写一个<bean>标签实现的功能是一样的
@Component:
作用:用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写
@Controller:一般用再表现层
@Service:一般用在业务层
@Repository:一般用在持久层
以上三个注解它们的作用和属性与Component一模一样。
它们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
用于注入数据的
它们的作用就和在xml配置中的bean标签中写一个<property>标签的作用是一样的
@Autowired:
作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器汇中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果ioc容器中有多个类型匹配时:
出现位置:
可以是变量上,也可以方法上
细节:
在使用注解注入时,set方法就不是必须的了。
@Qualifier
作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以
属性:
value:用于指定注入bean的id。
@Resource
作用:直接按照bean的id注入。它可以独立使用
属性:
name:用于指定bean的id。
以上三个注解都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过XML来实现。
@Value
作用:用于注入基本类型和String类型的数据
属性:
value:用于指定数据的值。可以使用。它可以使用spring中SpEl(也就是spring的el表达式)
SpEl的写法:${表达式}
用于改变作用范围的
它们的作用就和在bean标签中使用scope属性实现的功能是一样的
@Scope
作用:用于指定bean的作用范围
属性:
value:指定范围的取值。常用取值:singleton prototype
和生命周期相关的 (了解)
它们的作用就和在bean标签中使用init-method和destroy-method的作用是一样的
@PreDestroy
作用:用于指定销毁方法
@PostConstruct
作用:用于指定初始化方法
2.2 spring中的新注解
spring中的新注解
@Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
@ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value: 它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
我们使用此注解就等同于在xml中配置了
<context:component-scan base-package="com.marchsoft"></context:component-scan>
@Bean
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:
name:用于指定bean的id。当不写时,默认值是当前方法的名称
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象
查找的方法和@Autowired注解的作用是一样的
@Import
作用:用于导入其他的配置类
属性:
value:用于指定其他配置类的字节码。
当我们使用Import的注解之后,有Import注解的类就为父配置类,而导入的都是子配置类
@PropertySource
作用:用于指定properties文件的位置
属性:
value:指定文件的名称和路径。
关键字:classpath,表示类路径下
2.3Spring整合Junit的配置
应用程序的入口
main方法
Junit单元测试中,没有main方法也能执行
junit集成了一个main方法
该方法就会判断当前测试类中哪些方法有@Test注解
Junit就让有Test注解的方法执行
junit不会管我们是否采用spring框架
在执行测试方法时,junit根本不知道我们是不是使用了spring狂爱
所以也就不会为我们读取配置文件/配置类创建spring核心容器
由以上三点可知
当测试方法执行时,没有Ioc容器,就算写了Autowired注解,也无法注入
使用Junit单元测试:测试我们的配置
Spring整合Junit的配置
1.导入spring整合junit的jar(坐标)
2.使用Junit提供的一个注解把原有的main方法替换了,提传承spring提供的
@Runwith
3.告知spring的运行器,spring和ioc创建时基于xml还是注解的,并且说明位置
@ContextConfiguration
locations:指定项目了文件的位置,加上classpath关键字,表示在类路径下
classes:指定类注解所在的位置
当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
三、spring中的aop和基于XML以及注解的AOP配置
1.完善我们的account案例
2.分析案例汇总问题
3.回顾之前讲过的一个技术:动态代理
4.动态代理另一种实现方式
5.解决案例中的问题
6.AOP的概念
AOP:全称是Aspect Oriented Programming 即:面向切面编程
AOP(面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的同意维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring狂阿基中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
简单的说他就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们已有方法进行增强。
AOP的作用及优势:
作用:
在程序运行期间,不修改源码对已有方法进行增强
优势:
减少重复代码
提高开发效率
维护方便
AOP的实现方式
使用动态代理技术
7.spirng中的AOP相关术语
8.spring中基于XML和注解的AOP配置
3.1动态代理
动态代理:
特点:字节码随用隋创建,随用隋加载
它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。装饰者模式就是静态代理的一种体现。
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理
基于子类的动态代理
3.1.1基于接口的动态代理
基于接口的动态代理:
涉及的类:Proxy
提供者:JDK官方
如何创建代理对象:
使用Proxy类中的newProxyInstance方法
如何代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用
newProxyInstance方法的参数:
ClassLoader:类加载器
它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]:字节码数组
它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
此接口的实现类都是谁用谁写。
3.1.2基于子类的动态代理
基于子类的动态代理:
涉及的类:Enhancer
提供者:第三方cglib库
如何创建代理对象:
使用Enhancer类中的create方法
如何代理对象的要求:
被代理类不能是最终类
newProxyInstance方法的参数:
Class:字节码
它是用于指定代理对象的字节码
Callback:用于提供增强的代码
它是让我们写如何代理。我们一般都是写一个改接口的实现类,通常情况下都是匿名内部类,但不是必须的
此接口的实现类都是谁用谁写
我们一般写的都是该接口的子接口实现类:MethodInterceptor
3.2.Spring中AOP的细节
3.2.1 AOP的相关术语
Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spirng只支持方法类型的来拿节点。(例如业务层中所有的方法都叫连接点)
Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。(例如业务层中被增强的方法才叫切入点)
Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
前置通知:在切入点方法调用之前的通知就是前置通知
后置通知:在切入点方法调用之后的通知就是前置通知
异常通知:在catch里边的就是异常通知
最终通知:在finally里边的就是最终通知
环绕通知:整个的invoke方法在执行就是环绕通知。在环绕通知中有明确的切入点方法调用。
Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
Target(目标对象):
代理的目标对象,即被代理对象。
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程
spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面):
是切入点和通知(引介)的结合。
3.2.2.学习spring中的AOP要明确的事
a、开发阶段(我们做的)
编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。
把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP编程人员来做。
在配置文件中,声明切入点与通知间的关系,即切面。:AOP编程人员来做
b、运行阶段(Spring框架完成的)
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对选哪个,根据通知类型,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
3.2.3.关于代理的选择
在spring中,框架会根据目标类是否实现了接口来决定采用哪些动态代理的方法。
3.3 spring中基于XML的AOP配置步骤
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="accountService" class="com.marchsoft.service.impl.AccountServiceImpl"></bean>
spring中基于XML的AOP配置步骤
1.把通知Bean也交给spring来管理
2.使用aop:config标签表明开始AOP的配置
3.使用aop:aspect标签表明配置切面
id属性:是给切面体用一个唯一标识。
ref属性:是指定通知类bean的Id。
4.在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在示例是让printLog方法在切入点方法执行之前执行:所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
切入点表达式的写法:
关键词:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
public void com.marchsoft.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void com.marchsoft.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* com.marchsoft.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*
* *.*.*.*.AccountServiceImpl.saveAccount()
包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
* *..*.*()
参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符*表示任意类型,但是必须有参数
* *..*.*(*)
可以使用..表示有无参数均可,有参数可以是任意类型
* *..*.*(..)
全通配写法:
* *..*.*(..)
实际开发中切入点表达式的通用写法:
切到业务层实现类下的所有方法
* com.marchsoft.service.impl.*.*(..)
<!-- 配置Logger类-->
<bean id="logger" class="com.marchsoft.utils.Logger"></bean>
<!-- 配置AOP-->
<aop:config>
<!-- 配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点的关联-->
<aop:before method="printLog" pointcut="execution(* com.marchsoft.service.impl.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
</beans>
3.4.spring中基于XML的AOP配置不同的通知类型
配置AOP-->
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.marchsoft.service.impl.*.*(..))"/>
配置切面-->
<aop:aspect id="logAdvice" ref="logger">
配置前置通知:在切入点方法执行之前执行-->
<aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
配置后置通知:在切入点方法正常执行之后执行。它和异常通知永远只能执行一个-->
<aop:after-returning method="afterPrintLog" pointcut-ref="pt1"></aop:after-returning>
配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
<aop:after method="afterFinallyPrintLog" pointcut-ref="pt1"></aop:after>
配置切入点表达式,
id属性用于指定表达式的唯一表示
expression属性用于指定表达式内容
此标签写在aop:aspect标签内部只能当前切面使用。
它还可以卸载aop:aspect外面,此时就变成了所有切面可用
</aop:aspect>
</aop:config>
3.5.环绕通知
/**
* 环绕通知
* 问题:
* 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
* 解决:
* Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当一明确调用切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
*
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方法。
* 配置环绕通知
* <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
*/
public Object aroundPrintLog(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。后置");
return rtValue;
} catch (Throwable t) {
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
} finally {
System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。环绕");
}
}
3.6注解配置AOP
@Component("")
@Aspect//表示当前类是一个切面类
@Pointcut("切入点表达式表达式")
private void pt1(){}
@Before("pt1()"):前置通知
@AfterReturning("pt1()"):后置通知
@AfterThrowing("pt1()"):异常通知
@After("pt1()"):最终通知
@Around("pt1()"):环绕通知
注意:在使用注解配置AOP时,后置通知/异常通知的执行顺序会颠倒执行
四、spring中的JdbcTemlate以及Spring事务控制
1.spring中的JdbcTemplate
它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装,spring框架为我们提供了很多的操作模板类。
操作关系型数据库的:
JdbcTemplate
HibernateTemplate
操作nosql数据库的
RedisTemplate
操作消息队列的:
JmsTemplate
我们今天的主角在spring-jdbc-5.0.2.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包外,还需要导入一个spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)
JdbcTemplate的作用:
他就是用于和数据库交互的,实现对表的CRUD操作
如何创建该对象
对象中的常用方法
2.作业:
spring基于AOP的事务控制
3.spring中的事务控制
基于XML的
基于注解的
4.1JdbcTemplate的crud操作
1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
2.获取对象
JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
3.执行操作
//保存
jt.update("insert into account(name,money) values ('eee', 4589)");
//更新
jt.update("update account set name=?,money=? where id=?","test",4567,10);
//删除
jt.update("delete from account where id=?",10);
//查询所有
List<Account> accounts = jt.query("select * from account where money > ? ", new AccountRowMapper(), 1000);
List<Account> accounts = jt.query("select * from account where money > ? ", new BeanPropertyRowMapper<Account>(Account.class), 1000);
for (Account account:accounts
) {
System.out.println(account);
}
//查询一个
List<Account> accounts1 = jt.query("select * from account where id = ? ", new BeanPropertyRowMapper<Account>(Account.class), 2);
System.out.println(accounts1.isEmpty()?"没有内容":accounts1.get(0));
//查询返回一行一列(使用聚合函数,但不加grounp by子句)
Integer integer = jt.queryForObject("select count(*) from account where money > ?", Integer.class, 1000);
System.out.println(integer);
4.2.spring事务控制
4.2.1.spring事务控制我们要明白的
第一:JavaEE体系进行分层开发,事务处理位于业务层,spring提供了分层设计业务层的事务处理解决方案。
第二:spring框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar
第三:spring的事务控制都是基于AOP的,它既可以使用办成的方式实现,也可以使用配置的方式实现,我们学习的重点是使用配置的方式实现。
4.2.2.spring中事务控制的API介绍
1.PLatformTransactionManager
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法。
platformTransactionManager接口提供事务操作的方法,包含有3个具体的操作
1.获取事务状态信息:
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
2.提交事务:
void commit(TransactionStatus status)
3.回滚事务:
void rollback(TransactionStatus status)
真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager:
使用SpringJDBC或iBatis进行持久化数据时使用
org.springframework.hibernate5.HibernateTransacManag:
使用Hibernate版本进行持久化数据时使用
2.TransaDefinition
它是事务的定义信息对象,里面有如下方法
获取事务对象名称
-String getName
获取事务隔离级
-int getIsolationLevel()
获取事务传播行为
-int getPropagationBehavior()
获取事务超市时间
-int getTimeout()
获取事务是否制度
-boolean isReadOnly()
4.3.基于xml的事务配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
配置事务的属性-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
<!-- 配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.marchsoft.service.impl.*.*(..))"/>
<!-- 建立切入点表达式和事务通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
4.4.spring中基于注解的声明式事务控制配置步骤
<!-- spring中基于注解的声明式事务控制配置步骤
1.配置事务管理器
2.开启spring对注解事务的支持
3.在需要事务支持的地方使用@Transactional注解
-->
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
4.5.spring编程式事务控制
4.6.Spring5的新特性
4.6.1.jdk版本要求:
spring5.0在2017年9月发布了它的GA(通用)版本。该版本是基于jdk8编写的,所以jdk8以下版本将无法使用。同时,可以兼容jdk9版本
tomcate版本要求8.5及以上
注:
我们使用jdk8构建工程,可以降版搬移。但是不能使用jdk8以下版本构建工程。
由于jdk和tomcate版本的更新,我们的IDE也需要同时更新。(目前使用的eclipse 4.7.2)
4.6.2.利用jdk8版本更新的内容
第一:基于JDK8的反射增强
第二:@NonNull注解和@Nullable注解的使用
用@Nullable和@NotNull来显示表名可为空的参数和以及返回值。这样就能够在编译的时候处理空值而不是运行时抛出NullPointerExceptions
第三:日志记录方面
spring framework5.0代理了Commons Logging桥接模块的封装,它被叫做spring-jcl而不是标准的Commons Loggin。当然,无需任何人额外的桥接,新百本也会对Log4j 2.x,SEF4J,JUL(java.util.logging)进行自动检测。
4.6.3.核心容器的更新
Spring framework5.0现在支持后弦组件索引作为类路径草庙的代替方案。该功能已经在类路径草庙器中添加,以简化添加候选组件标识的步骤。
应用程序构建人物可以定义单签项目自己的
META-INF/spring.components文件。在编译时,源模型是自包含的,JPA实体和spring组件时已被标记的
从索引读取实体而不是扫描类路径对应小于200个类的小项目是没有明显差异。但对大型项目影响较大。加载组件索引开销更低。因此,锁着类数的增加,索引启动的启动时间将保持不变
加载组件索引的耗费是廉价的。因此当类的数量不断增长,加上构建索引的启动时间荣然可以维持一个常数,不过对于组件扫描而言,启动时间则会有明显的增长
这个对于我们处于大型spring项目的开发者所意味着的,是应用程序的启动时间将被大大缩减,虽然20或30秒钟看似没什么,但如果每天要这样登上好几百次,加起来就够你受的了。使用了组件索引的话,就能帮 助你每天过的更加高效。 你可以在 Spring 的 Jira 上了解更多关于组件索引的相关信息。
4.6.4.JetBrains Kotlin语言支持
Kolin概述:是一种支持函数式编程编程风格的面向对象语言.Kotlin运行在JVM之上,但运行环境并不限于JVM
4.6.7.响应式编程风格
此次 Spring 发行版本的一个激动人心的特性就是新的响应式堆栈 WEB 框架。这个堆栈完全的响应式且非 阻塞,适合于事件循环风格的处理,可以进行少量线程的扩展。
Reactive Streams 是来自于 Netflix, Pivotal, Typesafe, Red Hat, Oracle, Twitter 以及 Spray.io 的工程师特地开发的一个 API。它为响应式编程实现的实现提供一个公共的 API,好实现 Hibernate 的 JPA。这里 JPA 就是这个 API, 而 Hibernate 就是实现
Reactive Streams API 是 Java 9 的官方版本的一部分。在 Java 8 中, 你会需要专门引入依赖来使 用 Reactive Streams API。
Spring Framework 5.0 对于流式处理的支持依赖于 Project Reactor 来构建, 其专门实现了 Reactive Streams API。
Spring Framework 5.0 拥有一个新的 spring-webflux 模块,支持响应式 HTTP 和 WebSocket 客 户端。Spring Framework 5.0 还提供了对于运行于服务器之上,包含了 REST, HTML, 以及 WebSocket 风 格交互的响应式网页应用程序的支持。
在 spring-webflux 中包含了两种独立的服务端编程模型
基于注解:使用到了@Controller 以及 Spring MVC 的其它一些注解;
使用 Java 8 lambda 表达式的函数式风格的路由和处理。
有了 Spring Webflux, 你现在可以创建出 WebClient, 它是响应式且非阻塞的,可以作为 RestTemplate 的一个替代方案
这里有一个使用 Spring 5.0 的 REST 端点的 WebClient 实现:
WebClient webClient = WebClient.create();
Mono person = webClient.get()
.uri("http://localhost:8080/movie/42")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.then(response -> response.bodyToMono(Movie.class));
4.6.8.Junit5 支持
完全支持 JUnit 5 Jupiter,所以可以使用 JUnit 5 来编写测试以及扩展。此外还提供了一个编程以及 扩展模型,Jupiter 子项目提供了一个测试引擎来在 Spring 上运行基于 Jupiter 的测试。 另
另外,Spring Framework 5 还提供了在 Spring TestContext Framework 中进行并行测试的扩展
针对响应式编程模型, spring-test 现在还引入了支持 Spring WebFlux 的 WebTestClient 集成测 试的支持,类似于 MockMvc,并不需要一个运行着的服务端。使用一个模拟的请求或者响应, WebTestClient 就可以直接绑定到 WebFlux 服务端设施。
你可以在这里找到这个激动人心的 TestContext 框架所带来的增强功能的完整列表。
当然, Spring Framework 5.0 仍然支持我们的老朋友 JUnit! 在我写这篇文章的时候, JUnit 5 还 只是发展到了 GA 版本。对于 JUnit4, Spring Framework 在未来还是要支持一段时间的。
4.6.9.依赖类库的更新
终止支持的类库
Portlet.
Velocity.
JasperReports.
XMLBeans.
JDO.
Guava.
支持的类库
Jackson 2.6+
EhCache 2.10+ / 3.0 GA
Hibernate 5.0+
JDBC 4.0+
XmlUnit 2.x+
OkHttp 3.x+
Netty 4.1+