目录
5.6 返回后通知(After returning advice)... 28
5.7 抛出异常后通知(After throwing advice)... 28
5.8 后置通知(After(finally)advice)... 28
5.9 环绕通知(Adround Advice)... 28
9.4 Spring- Logic.xml(事务的配置文件) 36
Spring
1.2 Web应用架构
1.3 Web应用架构与Java EE规范
1.4 Web应用架构与Java EE规范高级
1.5 Web应用架构与民间解决方案
Spring 是一个开源框架,由 Rod Johnson 创建,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。
2.1 Spring框架的7个模块
- 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
- Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
2.2 Spring的包
- Spring有自己的包。
- Spring有依赖的包。
- Spring进行数据事务操作依赖CGLib包。
Spring基本包:(
aopalliance-*.jar,
aspectjrt-*.jar,
aspectjweaver-*.jar,
commons-logging-*.jar,
spring-aop-*.RELEASE.jar,
spring-aspects-*.RELEASE.jar,
spring-beans-*.RELEASE.jar,
spring-context-*.RELEASE.jar,
spring-core-*.RELEASE.jar,
spring-expression-*.RELEASE.jar,
spring-jdbc-*.RELEASE.jar,
spring-tx-*.RELEASE.jar,
spring-web-*.RELEASE.jar
)
3.1 ioc定义
- IoC:全称是Inversion of Control,反转控制。
- IoC模式:又称为DI,即Dependency Injection,叫做依赖注入。
使用springIoc不需要手动提供实现,通过spring配置,用构造方法或者set方法实现,这样客户端看不到是谁实现的接口,更改实现类的时候,只需要修改配置就行。
3.2 Ioc-控制反转
public class UserAction{
private UserService userService = new UserService();
}
UserService 是在应用内部创建及维护的。所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转
3.3 IoC-依赖注入
- 当我们把依赖对象交给外部容器负责创建,那么UserAction类可以改成如下
public class UserAction{
private UserService userService;
//通过构造器参数,让容器把创建好的依赖对象注入进UserAction
public UserAction(UserService userService){
this.userService = userService;
}
public UserService getUserService(){
return userService;
}
//通过set方法注入依赖对象
public void setUserService(UserService userService){
this.userService = userService;
}
}
- 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中
3.4 ioc的意义
- IoC设计模式,重点关注组件的依赖性,配置以及生命周期.
- 它主要是为了解决程序设计中松散耦合的问题,依赖关系不出现在任何代码中,而只出现在XML配置文件里.
3.5 Ioc-构造器注入
意义:
通过构造函数,来初始化javaBean中各属性的值。
要注意:构造方法先被调用,set方法后调用,set方法注入的属性会覆盖构造方法注入的属性
构造方法:事先调用,进行属性初始化,不允许后面调整,比如通过set方法。
好处就是不被其它类影响本类的属性值
set方法: 比构造方法灵活,因为任意调整set方法对应的属性值。
-
-
- 封装
-
封装的工具类:
package com.gist.util;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gist.ioc.action.StudentAction;
public class BeanUtil {
public static <T>T BeanXml(String string){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
return (T)classPathXmlApplicationContext.getBean(string);
}
}
//使用
Student student = BeanUtil.BeanXml("studentDemo");
System.out.println(student);
3.5.2 Bean
package com.gist.ioc.entity;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
private String id; //id
private String name; //名字
private int age; //年龄
public Student(){
}
//ioc构造器注入
public Student(String name,int age){
this.name=name;
this.age=age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3.5.3 Spring.student.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.gist.ioc.entity.Student" id=" studentDemo ">
<!-- 为构造方法赋值 -->
<constructor-arg index="0" value="kevin"></constructor-arg>
<constructor-arg index="1" value="20"></constructor-arg>
</bean>
</beans>
3.5.4 业务逻辑层编写
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
StudentAction studentAction1 = (StudentAction)classPathXmlApplicationContext.getBean("studentAction");
Student student = BeanUtil.BeanXml("studentDemo");
System.out.println(student);
}
3.5.5 思考
- 如果没有这个构造器会如何?
- :会抛出异常
- 如果构造器中没有写任何代码又会如何?
- :会赋null值
3.5.6 构造器注入
-
public Student( int studentAge ) { this.studentAge = studentAge; } //配置 <constructor-arg index="0"> <value type="int">100</value> </constructor-arg>
3.6 Ioc-set方法注入
set方法: 比构造方法灵活,因为任意调整set方法对应的属性值。
3.6.1 Bean
public class Student {
private String id; //id
private String name; //名字
private int age; //年龄
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3.6.2 Spring.student.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.gist.ioc.entity.Student" id="studentDemo" >
<!-- 为属性赋值 -->
<property name="name" value="Kevin"></property>
<property name="age" value="20"></property>
</bean>
</beans>
3.6.3 业务逻辑层编写
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
StudentAction studentAction1 = (StudentAction)classPathXmlApplicationContext.getBean("studentAction");
Student student = BeanUtil.BeanXml("studentDemo");
System.out.println(student.getName()+student.getAge());
}
3.6.4 Set方法注入
public void setName(String name) {
this.name = name;
}
配置
<property name="name" value="Kevin"></property>
<property name="age" value="20"></property>
3.7 混合注入
3.7.1 Bean
package com.gist.ioc.entity;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
private String id; //id
private String name; //名字
private int age; //年龄
public Student(){
}
public Student(int age){
this.age=age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3.7.2 Spring.student.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.gist.ioc.entity.Student" id="studentDemo" >
<!-- 为构造方法赋值 -->
<constructor-arg index="0" value="20"></constructor-arg>
<!-- 为属性赋值 -->
<property name="name" value="Kevin"></property>
</bean>
</beans>
3.7.3 思考
-
- 如果构造器注入和setter方法注入都注入了同一个属性,并且值不一样,会怎样?
不报错,以setter方法为准
3.7.4 指定注入类型
- Setter注入
- 8种基本数据类型,自动转换
- 构造器注入
- 默认为String类型
- 或者指定注入类型
- <value type="int">100</value>
3.8 List集合注入(set注入)
3.8.1 封装的工具类
package com.gist.util;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gist.ioc.action.StudentAction;
public class BeanUtil {
public static <T>T BeanXml(String string){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
return (T)classPathXmlApplicationContext.getBean(string);
}
}
3.8.2 Bean
package com.gist.ioc.entity;
import java.util.List;
public class Student {
private List<String> hobbyList; //爱好
public List<String> getHobbyList() {
return hobbyList;
}
public void setHobbyList(List<String> hobbyList) {
this.hobbyList = hobbyList;
}
}
3.8.3 Spring.collection.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义LocalUserServiceImpl -->
<bean id="hobbyListId" class="com.gist.ioc.entity.Student" >
<property name="hobbyList">
<list>
<value>书法</value>
<value>游戏</value>
<value>编程</value>
</list>
</property>
</bean>
</beans>
3.8.4 业务逻辑层
public static void mian(String [] args){
Student student = BeanUtil.BeanXml("hobbyListId");
System.out.println("-------爱好-------");
for(String str : student.getHobbyList()){
System.out.println(str);
}
}
3.9 List集合注入(构造器注入)
3.9.1 Bean
package com.gist.ioc.entity;
import java.util.List;
public class Student {
private List<String> hobbyList; //爱好
public Student( List hobbyList){
this. HobbyList= hobbyList;
}
public List<String> getHobbyList() {
return hobbyList;
}
public void setHobbyList(List<String> hobbyList) {
this.hobbyList = hobbyList;
}
}
3.9.2 Spring.collection.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义LocalUserServiceImpl -->
<bean id="hobbyListId" class="com.gist.ioc.entity.Student" >
<constructor index=“0”>
<list>
<value>书法</value>
<value>游戏</value>
<value>编程</value>
</list>
</constructor>
</bean>
</beans>
3.9.3 业务逻辑层
public static void mian(String [] args){
Student student = BeanUtil.BeanXml("hobbyListId");
System.out.println("-------爱好-------");
for(String str : student.getHobbyList()){
System.out.println(str);
}
}
3.10 Map集合注入(set注入)
3.10.1 Bean
package com.gist.ioc.entity;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
private Map<String ,String> CardMap; //卡号与密码
public Map<String, String> getCardMap() {
return CardMap;
}
public void setCardMap(Map<String, String> cardMap) {
CardMap = cardMap;
}
}
3.10.2 Spring.collection.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="CardMapId" class="com.gist.ioc.entity.Student">
<property name="CardMap">
<map>
<entry key="4408821996" value="1996"></entry>
<entry key="4408821997" value="1997"></entry>
<entry key="4408821998" value="1998"></entry>
</map>
</property>
</bean>
</beans>
3.10.2 业务逻辑层
package com.gist.ioc.test;
import java.util.Map;
public class StudentTest {
public static void main(String[] args) {
Student student = BeanUtil.BeanXml("CardMapId");
System.out.println("------银行卡账号与密码-------");
for(Map.Entry<String, String> map: student.getCardMap().entrySet()){
System.out.println("Key:"+map.getKey()+"---value:"+map.getValue());
}
}
}
3.11 Map集合注入(构造器注入)
3.11.1 Bean
public class Company {
private String companyName;
private Map department = new HashMap();
public Company() {
}
public Company( Map department ) {
this.department = department;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Map getDepartment() {
return department;
}
public void setDepartment(Map department) {
this.department = department;
}
}
3.11.2 Spring.collection.xml(配置文件)
<bean id="companyBean2" class="ioc.common.beans.Company">
<constructor-arg index="0">
<map>
<entry key="WJ">
<value>外交部</value>
</entry>
<entry key="NW">
<value>内务部</value>
</entry>
</map>
</constructor-arg>
</bean>
3.11.3 业务逻辑层
package com.gist.ioc.test;
import java.util.Map;
public class StudentTest {
public static void main(String[] args) {
Student student = BeanUtil.BeanXml("companyBean2");
System.out.println("------银行卡账号与密码-------");
for(Map.Entry<String, String> map: student.getCardMap().entrySet()){
System.out.println("Key:"+map.getKey()+"---value:"+map.getValue());
}
}
}
3.12 Set集合注入(set注入)
3.12.1 Bean
package com.gist.ioc.entity;
import java.util.Set;
public class Student {
private Set<String> CardSet; //卡号
public Set<String> getCardSet() {
return CardSet;
}
public void setCardSet(Set<String> cardSet) {
CardSet = cardSet;
}
}
3.12.2 Spring.collection.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="CardSetId" class="com.gist.ioc.entity.Student">
<property name="CardSet" >
<set>
<value>4408821996</value>
<value>4408821997</value>
<value>4408821998</value>
</set>
</property>
</bean>
</beans>
3.12.3 业务逻辑层
//测试Set集合
@Test
public void TestCardSet(){
Student student = BeanUtil.BeanXml("CardSetId");
System.out.println("-----银行卡账号------");
for(String str : student.getCardSet()){
System.out.println(str);
}
}
3.13 Set集合注入(构造器注入)
3.13.1 Bean
Public class Student{
private Set<String> CardSet; //卡号
public Set<String> getCardSet() {
return CardSet;
}
public void setCardSet(Set<String> cardSet) {
CardSet = cardSet;
}
}
3.13.2 Spring.collection.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- set使用构造器注入 -->
<bean id="constructorSet" class="com.gist.ioc.entity.Student">
<constructor-arg index="0">
<set>
<value>440882123</value>
<value>440882126</value>
</set>
</constructor-arg>
</bean>
</beans>
3.13.3 业务逻辑层
public class StudentTest {
public static void main(String[] args) {
Student student = BeanUtil.BeanXml("constructorSet");
System.out.println("-----银行卡账号------");
for(String str : student.getCardSet()){
System.out.println(str);
}
}
}
3.14 集合属性注入
<property>标签代表属性,集合的值有多个,value是只能代表一个值
如果属性是集合,value不能满足,
spring提供集合的标签进行代表集合的值:
比如<list>标签代表List集合 ,通过<value>代表list的一个元素
<set>标签代表set集合,通过<value>代表set的一个元素
<map>标签代表map集合,map有key,value。:<entry>标签代表每个key,value键值对,
<entry>的key属性值代表map的key,<entry>的value属性值代表map的value
java可以通过entry来遍历map的key,value值
3.15 自定义对象属性注入
比如Student,UserService这些类,是用户手动创建的类或JDK自带的类
<property>标签,提供一个ref属性,来引用对象的值,ref的值代表引用对象的bean id
例如:
<!-- 注入userAction -->
<bean id="studentService" class="com.gist.spring.ioc.service.StudentService">
</bean>
<bean id="userAction" class="com.gist.spring.ioc.UserAction">
<!--ref 的值必须等于bean id-->
<property name="studentService" ref="studentService"></property>
</bean>
4.1 bean的创建机制
bean的创建机制(scope范围):
singleton(单例模式,默认):bean只被创建一次,也就是类只能被 new 一次
prototype(原型模式):每次获取bean的时候,就新创建一个bean (不常用)
request:每个http 请求创建一次bean(不常用)
session:每个http session(会话)创建一次bean(不常用)
global session:每个全局 http session(会话)创建一次bean,在做门户项目portlet (不常用)
application:在启动tomcat的时候,创建一次bean(不常用)
通过bean 的scope属性设置bean的创建范围,比如scope="prototype":
<bean id="studentAction" class="com.gist.spring.ioc.service.StudentAction"
scope="prototype">
<property name="studentService" ref="studentService"></property>
</bean>
4.2 单例模式设置
<bean id="userBean" class="bean_manager.beans.User" scope="singleton">
- 不配置时,默认为singleton。
- singleton时,创建该类的实例有且只有一个
4.3 null值设置
-
<property name="hobby"> <null/> </property>
给hobby属性赋null值
4.4 其他类型数据的设置
<property name="brithday">
<value type=”int”>500</value>
</property>
- :type=“int”可以省去
4.5 其他Bean的引用设置
<property name="brithday">
<ref bean="date"/>
</property>
<ref bean=“date”/> 引用id=date的值
5.1 什么是面向切面编程
- AOP :全称是 Aspect-Oriented Programming,中文翻译是面向方面的编程或者面向切面的编程。
- 你熟悉的是面向过程的编程,面向对象的编程。
5.2 切面(Aspect)
- 即:当前关注的一个代码的流程,其中可能调用了多个类的多个方法。可以认为是通知、引入和切入点的组合。
5.3 连接点(Joinpoint)
- 即:一个代码流程中的某个步骤,也就是这个代码流程中对某个方法的调用。
5.4 通知(Advice)
- 即:对于某个方法(连接点),进行拦截的拦截规则。包含前置通知(before advice)、后置通知(after advice)、环绕通知(around advice)
5.5 切入点(Poinrcut)
- 即:对于某个方法(连接点),是否被拦截的判断(过滤)规则。可以认为是连接点的集合
5.5 前置通知(Before advice)
前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点钱的执行(除非它抛出一个异常)
5.6 返回后通知(After returning advice)
返回后通知(After returning advice):在某连接点正常完成后执行的通知,例如:一个方法没有抛出任何异常,正常返回。
5.7 抛出异常后通知(After throwing advice)
抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
5.8 后置通知(After(finally)advice)
后置通知(After(finally)advice):当某连接点退出的时候执行的通知,(不论是正常返回还是异常退出)
5.9 环绕通知(Adround Advice)
环绕通知(Adround Advice):包围一个连接点的通知,如方法调用,这是最强大的一种通知类型,环绕通知可以在方法调用前后完成的自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
6. Aop思维
7. Aop意义
- 现在的系统往往强调减小模块之间的耦合度,AOP 技术就是用来帮助实现这一目标的。
- 从某种角度上来讲“切面”是一个非常形象的描述,它好像在系统的功能之上横切一刀,要想让系统的功能继续,就必须先过了这个切面。
- 这些切面监视并拦截系统的行为,在某些(被指定的)行为执行之前或之后执行一些附加的任务(比如记录日志)。
- 而系统的功能流程(比如 Greeting)并不知道这些切面的存在,更不依赖于这些切面,这样就降低了系统模块之间的耦合度。
7.1 Aop主要作用
8. Spring的Aop方案
- AOP是很多直觉难以理解的术语的根源。幸运的是,你只要理解三个概念,就可以编写AOP模块。这三个概念是:advice,pointcut和advisor。
- advice是你想向别的程序内部不同的地方注入的代码。
- pointcut定义了需要注入advice的位置,通常是某个特定的类的一个public方法。
- advisor是pointcut和advice的装配器,是将advice注入主程序中预定义位置的代码。
8.1Spring的AOP的Advice(拦截规则)
-
- 前拦截
- 后拦截
- 围绕拦截
- 异常通知
<aop:pointcut id="logpointcut" expression="execution(* com.gist.aop.service.*.add*(..))" />
8.2 Before Advice(前拦截规则)
public void doBefore(JoinPoint joinpoint){
System.out.println("前置通知,调用:"+joinpoint.getSignature().getName()+"方法");
}
<!-- aop.xml -->
<aop:before method="doBefore" pointcut-ref="logpointcut" />
JoinPoint :业务逻辑方法。
8.3 After Advice(后拦截规则)
public void doAfter(JoinPoint joinpoint){
System.out.println("后置通知,调用:"+joinpoint.getSignature().getName()+"方法");
}
<!-- aop.xml -->
<aop:after method="doAfter" pointcut-ref="logpointcut" />
JoinPoint :业务逻辑方法。
8.4 Around Advice(围绕拦截规则)
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知前,调用:"+pjp.getSignature().getName()+"方法");
long time = System.currentTimeMillis();
Object retVal = pjp.proceed();
time = System.currentTimeMillis() - time;
System.out.println("process time: " + time + " ms");
System.out.println("后环绕通知,调用:"+pjp.getSignature().getName()+"方法");
return retVal;
}
<!-- aop.xml -->
<aop:around method="doAround" pointcut-ref="logpointcut" />
ProceedingJoinPoint :业务逻辑方法
8.5 Around Advice(异常通知)
public void doThrowing(JoinPoint jp, Throwable ex) {
System.out.println("method " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()
+ " throw exception");
System.out.println(ex.getMessage());
}
<!-- aop.xml -->
<aop:after-throwing method="doThrowing" pointcut-ref="logpointcut" />
- JoinPoint :业务逻辑方法
- Throwable :异常信息
8.6 Aop-切面定义
public class ShopLogAspect{
//后置通知,方法名可以随意定义,但必须是包含JoinPoint参数
public void doAfter(JoinPoint jp){
}
//环绕通知,方法名可以随意定义,但必须包含ProceedingJoinPoint参数
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
Object retval = pjp.proceed();
return retval;
}
//前置通知,方法名可以随意定义,但必须包含JoinPoint参数
public void doBefore(JoinPoint jp){
}
//异常通知:方法名可以随意定义,但是必须包含JoinPoint,Throwable参数
public void doThrowing(JoinPoint jp, Throwable ex){
}
}
8.7 Aop核心类介绍
- JoinPoint 代表被拦截的对象
- getTarget().getClass().getName():被拦截的方法所在的类
- getSignature().getName() :被拦截的具体方法名
- ProceedingJoinPoint:环绕通知中被拦截的对象
- proceed()执行被拦截的方法
8.8 Aop-配置文件定义
<?xml version="1.0" encoding="UTF-8"?>
<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 -->
<bean id="logAdvice" class="com.gist.aop.logAdvice.LogAdvice"></bean>
<!—Aop配置 -->
<aop:config>
<aop:aspect id="logAspect" ref="logAdvice">
<!-- 定义切点 -->
<aop:pointcut id="logpointcut" expression="execution(* com.gist.aop.service.*.add*(..))" />
<!-- 定义通知方法,前置通知 -->
<aop:before method="doBefore" pointcut-ref="logpointcut" />
<!-- 定义通知方法,后置通知 -->
<aop:after method="doAfter" pointcut-ref="logpointcut" />
<!-- 定义通知方法,环绕通知 -->
<aop:around method="doAround" pointcut-ref="logpointcut" />
<!-- 定义通知方法,异常通知-->
<aop:after-throwing method="doThrowing" pointcut-ref="logpointcut" />
</aop:aspect>
</aop:config>
</beans>
8.9 Spring提供了六种类型切点
·静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut是静态方法切点的抽象基类,默认情况下它匹配所有的类。 StaticMethodMatcherPointcut包括 两个主要的子类,分别是NameMatchMethodPointcut和AbstractRegexpMethodPointcut,前者提供简单字符串匹配方法签名,而后者使用正则表达式匹配方法签名;
·动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut是动态方法切点的抽象基类,默认情况下它匹配所有的类。 DynamicMethodMatcherPointcut类已经过时,可以使用DefaultPointcutAdvisor和DynamicMethodMatcherPointcut动态方法匹配器替代之;
·注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut实现类表示注解切点。使用AnnotationMatchingPointcut支持在Bean中直接通过JDK5.0注解标签定义的切点;
·表达式切点:
org.springframework.aop.support.ExpressionPointcut接口主要是为了支持AspectJ切点表达式语法而定义的接口;
·流程切点:
org.springframework.aop.support.ControlFlowPointcut实现类表示控制流程切点。ControlFlowPointcut是一种特殊的切点,它根据程序执行堆栈的信息查看目标方法是否由某一个方法直接或间接发起调用,以此判断是否为匹配的连接点;
·复合切点:
org.springframework.aop.support.ComposablePointcut实现类是为创建多个切点而提供的方便操作类。它所有的方法都返回ComposablePointcut类,这样,我们就可以使用链接表达式对其进行操作,形如:
Pointcut pc=new ComposablePointcut().union(classFilter).intersection(methodMatcher).intersection(pointcut)
9. Spring事务分类
9.1 编程式事务
-
- 需要编写代码控制事务在哪里开始,哪里提交,哪里回滚。
- 申明式事务
- 由Spring自动控制,事务在业务逻辑方法执行前开始,在业务逻辑方法正常结束后提交,在业务逻辑方法抛出异常时回滚。
9.2 Spring申明式事务
- 效果:
- 业务方法执行前自动开启事务
- 业务方法执行顺利结束后自动提交事务
- 业务方法执行抛出异常时自动回滚事务
9.3 Bean
- 业务逻辑:
public class Logic {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
}
public void update(String name, int age) {
this.jdbcTemplate.update("INSERT INTO user_list ( user_name, user_age ) VALUES ( '"+ name + "', " + age + " )");
}
}
- 注意:没有事务控制的代码。
9.4 Spring- Logic.xml(事务的配置文件)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource ">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/spring_base</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<bean id="transactionManager " class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="logic" class="transaction.declare_patten.Logic">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="logicDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<ref bean="logic"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
9.5 Spring事务传播行为
PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
PROPAGATION_NESTED: 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
10. Spring Dao
10.1 Spring数据库操作接口
Datasource类
JdbcTemplate类
不需要任何配置文件,直接使用接口
//数据源:
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName( "com.mysql.jdbc.Driver" );
ds.setUrl( "jdbc:mysql://localhost:3306/spring_base" );
ds.setUsername( "root" );
ds.setPassword( "root" );
//连接数据库:
JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSource);
10.2 封装的工具类
package com.gist.util;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gist.ioc.action.StudentAction;
public class BeanUtil {
public static <T>T BeanXml(String string){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
return (T)classPathXmlApplicationContext.getBean(string);
}
}
使用
Student student = BeanUtil.BeanXml("studentDemo");
System.out.println(student);
10.3 结合IOC简化代码
10.3.1 Spring-jdbc.xml(配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/springjdbc?useUnicode=true&characterEncoding=UTF8"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
<!—连接数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg index="0" ref="dataSource"></constructor-arg>
</bean>
</beans>
10.3.2 测试类
public class jdbcTest {
public static void main(String [] args){
//将数据库连接信息绑定在一个JdbcTemplate类上
JdbcTemplate jdbcTemplate = BeanUtil.BeanXml("jdbcTemplate");
}
}
10.3.3 添加数据
//添加一条字段
@Test
public void insertStudent(){
String sql="insert into student_list(id,name,age,sex)value('1234','东莞一哥','18','男')";
jdbcTemplate.execute(sql);
}
10.3.4 更新数据
//更新一条字段
@Test
public void updateStudent(){
String sql="insert into student_list(id,name,age,sex)value('1234','东莞一哥','18','男')";
jdbcTemplate.update(sql);
}
10.3.5 查询数据
//查询一个字段
@Test
public void queryStudent(){
String sql="select *from student_list where id='123'";
for(Map.Entry entry : jdbcTemplate.queryForMap(sql).entrySet()){
System.out.println("字段:"+entry.getKey()+"---值:"+entry.getValue());
}
}
10.3.6 查询多个数据
//查询多个字段
@Test
public void queryStudents(){
String sql="select *from student_list ";
List<Map<String,Object>> list = jdbcTemplate.queryForList(sql);
for(Map<String,Object> map:list){
for(Map.Entry entry : map.entrySet()){
System.out.println("字段:"+entry.getKey()+"---值:"+entry.getValue());
}
}
}
10.3.7 查询单列字段与类型
//查询单列字段与类型
@Test
public void queryStudents2(){
String sql="select name from student_list ";
List<String> list = jdbcTemplate.queryForList(sql,String.class);
for(String name : list){
System.out.println("name:"+name);
}
}
10.3.8 查询多列字段与类型
//查询多列字段与类型
@Test
public void queryStudent3(){
String sql="select * from student_list ";
RowMapper<Student> rowMapper = new RowMapper<Student>(){
@Override
public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
Student student = new Student();
student.setAge(rs.getInt("age"));
student.setId(rs.getString("id"));
student.setName(rs.getString("name"));
student.setSex(rs.getString("sex"));
return student;
}
};
List<Student> studentList = jdbcTemplate.query(sql, rowMapper);
for(Student student : studentList){
System.out.println("name:"+student.getName()+"---age:"+student.getAge());
}
}
11. 注解@Transactional
11.1 @Transactional属性
11.2 用法
注解就是为了说明java中的某个部分的作用。
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
12.事务概述
事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。
Spring Framework对事务管理提供了一致的抽象,其特点如下:
- 为不同的事务API提供一致的编程模型,比如JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)
- 支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用
- 提供比其他事务API如JTA更简单的编程式事务管理API
- 与spring数据访问抽象的完美集成
12.1 事务管理方式
spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。
编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
自动提交(AutoCommit)与连接关闭时的是否自动提交
12.2 自动提交
默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。不过,这个我们不用担心,spring会将底层连接的自动提交特性设置为false。
org/springframework/jdbc/datasource/DataSourceTransactionManager.java
// switch to manual commit if necessary. this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getautocommit()) {
txobject.setmustrestoreautocommit(true);
if (logger.isdebugenabled()) {
logger.debug("switching jdbc connection [" + con + "] to manual commit");
}
con.setautocommit(false);
}
有些数据连接池提供了关闭事务自动提交的设置,最好在设置连接池时就将其关闭。但C3P0没有提供这一特性,只能依靠spring来设置。
因为JDBC规范规定,当连接对象建立时应该处于自动提交模式,这是跨DBMS的缺省值,如果需要,必须显式的关闭自动提交。C3P0遵守这一规范,让客户代码来显式的设置需要的提交模式。
12.3 连接关闭时的是否自动提交
当一个连接关闭时,如果有未提交的事务应该如何处理?JDBC规范没有提及,C3P0默认的策略是回滚任何未提交的事务。这是一个正确的策略,但JDBC驱动提供商之间对此问题并没有达成一致。
C3P0的autoCommitOnClose属性默认是false,没有十分必要不要动它。或者可以显式的设置此属性为false,这样会更明确。
基于注解的声明式事务管理配置
spring-servlet.xml
<!-- transaction support-->
<!-- PlatformTransactionMnager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="txManager" />
还要在spring-servlet.xml中添加tx名字空间
...
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
...
MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。
另外需要下载依赖包aopalliance.jar放置到WEB-INF/lib目录下。否则spring初始化时会报异常
java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor
12.5 spring事务特性
spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
其中TransactionDefinition接口定义以下特性:
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
- TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
事务只读属性
只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。
spring事务回滚规则
指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。
还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。
@Transactional注解
@Transactional属性
| ||
属性 | 类型 | 描述 |
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
用法
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
//方法上注解属性会覆盖类注解上的相同属性
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}