目录
一、Spring概述
(一)、Spring是什么?
(二)、Spring框架发展历程
(三)、Spring框架的优势
(四)、Spring的体系结构
二、程序耦合与解耦合
(一)什么是程序的耦合
(二)解决程序耦合的思路
1、编译不依赖,运行时才依赖
2、使用工厂模式解耦合
三、SpringIOC机制的详解
(一)、IOC概述及作用
1、IOC的简介,设计思想
2、IOC作用
(二)SpringIOC入门案例前期准备
1、构建maven工程,添加Spring框架的依赖
2、创建持久层接口和实现类
3、创建业务层接口和实现类
4、在resources文件夹中创建一个任意名称的xml文件
5、让Spring管理资源,在配置文件中配置service和dao
6、测试IOC配置是否成功
(三)Spring基于XML的IOC细节
1、IOC配置文件详解:配置标签书写规范,配置标签的属性
2、SpringIOC机制源码解析
(1) IOC容器解析
(2)IOC容器底层bean初始化过程
(四)手动实现自己的IOC容器
1、分析IOC实现思路
2、IOC原理实现—环境搭建:构建maven工程,引入依赖
3、设计接口和类以及编写配置文件
4、使用xml技术解析配置文件
5、编写测试文件,展示测试结果
(五)Spring管理bean细节
1、bean实例化方式
(1)构造方法的方式
1)创建User类
2)配置Spring容器管理user类型对象
3)测试容器实例化User对象是否成功)
(2)静态工厂方式
1)创建静态工厂ItemFactory
2)配置Spring容器管理User类型对象
3)测试容器实例化User对象是否成功
(3)实例工厂方式
1)创建实例工厂NewItemFactory
2)配置Spring容器管理NewItemFactory类型对象
3)测试容器实例化User对象是否成功
2、bean作用域
(1)bean作用域介绍
(2)bean作用域的解析
1)当scope的取值为singleton时
2)当scope的取值为prototype时
3)当Scope的取值为其他值
四、Spring依赖注入(DI)
(一)介绍
定义
(二)注入方式
1、构造函数注入
(1)构建Account类,提供所有属性的构造方法
(2)配置Account类(使用构造方法注入依赖数据)
(3)构造函数注入测试方法
2、setter注入
(1)修改Account类,添加属性的setter方法
(2)配置Account类(利用setter方法注入依赖数据)
(3)setter注入测试方法
3、注入集合数据
(1)修改Account类,添加集合属性
(2)配置Account类(利用setter注入依赖集合数据)
(3)集合注入测试方法
五、Spring配置文件模块化
(一)Spring模块化的介绍
(二)Spring模块化的配置
1、Spring模块化配置方式一
2、Spring模块化配置方式二
六、模块设计模式解析
(一)模块化设计模式介绍
(二)模板设计模式的应用场景
(三)模板设计模式实现
1、需求:用模块方法模式实现出国留学手续设计程序
2、代码实现
七、Spring整合JDBC实现用户的CRUD
(一)整合思路分析
(二)构建maven工程,添加技术依赖
(三)构建数据库表并编写实体类Account
(四)编写持久层代码AccountDao以及实现类AccountDaoImpl
(五)编写业务层代码Accountservice以及实现类AccountServiceImpl
(六)创建并编写配置文件:配置容器管理对象
(七)测试代码
八、常用注解
(一)创建对象的注解
(二)用于注入数据的注解
(三)用于改变作用范围的注解
九、基于注解的IOC案例
(一)构建工程,添加依赖
(二)使用注解配置
(三)创建配置文件并开启对注解的支持
(四)编写测试代码
十、纯注解配置
(一)新注解说明
(二)通过注解获取容器
十一、Spring整合Junit
(一)IOC测试类中的问题和解决思路
(二)整合Junit配置步骤
1、添加技术依赖
2、使用@RunWith注解替换原有运行器
3、使用@ContextConfiguration 指定 Spring 配置文件的位置
4、使用@Autowired 给测试类中的变量注入数据
十二、Spring整合DButils实现转账业务
(一)添加转账方法,并演示事务问题
1、转账Account表以及对应的实体bean
2、引入DBUtils依赖坐标
3、创建Account接口和对应实现类
4、创建AccountService接口和实现类
5、测试转账业务
(二)编写ConnectionUtils工具类管理数据库连接
(三)编写事务管理工具类
(四)编写业务层和持久层控制代码并配置Spring的IOC
(五)测试转账
(六)转账业务案例中存在的问题
(七)使用动态代理实现事务控制
1、创建代理工具类
2、配置并测试动态代理转账业务
十三、SpringAOP机制详解
(一)AOP概述
1、什么是AOP
2、AOP编程思想
3、Spring中的常用术语
4、AOP编程底层的实现机制
(1)JDK动态代理
(2)CGLB动态代理
5、AOP的作用及优势
(二)Spring基于XML的AOP配置
1、环境搭建
(1)添加依赖
(2)添加代码
(3)创建配置文件并导入约束
(4)配置Spring的IOC
(5)抽取公共代码制作成通知
2、AOP配置步骤
(1)配置bean标签
(2)使用aop:config 声明AOP配置
(3)使用aop:aspect 配置切面
(4)使用aop:pointcut配置切入点表达式
(5)使用aop:xxx配置对应的通知类型
3、切入点表达式说明
4、环绕通知配置事务管理
(三)Spring基于注解的AOP配置
1、环境搭建
(1)构建maven工程添加AOP注解的相关依赖
(2)导入代码
(3)在配置文件中导入context的名称空间且配置
(4)资源使用注解配置
(5)在配置文件中指定spring要扫描的包
2、配置步骤
(1)通知类使用注解配置
(2)通知类使用@Aspect注解声明为切面
(3)增强方法上使用注解配置通知
(4)开启Spring对注解AOP的支持
(5)环绕通知注解配置
(6)切入点表达式注解
十四、Spring事务详解
(一)Spring基于XML的事务配置
1、环境搭建
(1)构建工程,添加依赖
(2)创建Spring的配置文件,导入约束
(3)沿用转账业务
2、事务管理配置步骤
(1)配置事务管理器
(2)配置事务的通知引用事务管理器
(3)配置事务的属性
(4)配置AOP切入点表达式
(5)配置切入点表达式和事务通知的对应关系
(二)Spring基于注解的事务配置
1、环境搭建
(1)构建maven工程,添加相关依赖
(2)创建Spring配置文件
(3)沿用转账业务的代码:dao实现类和service实现类采用注解的形式,添加到容器中管理
2、事务管理配置步骤
(1)配置事务管理器并注入数据源
(2)在业务层使用@Transactional注解
(3)配置文件中开启Spring对注解事务的支持
十五、Spring整合Mybatis实现用户的CRUD
(一)整理思路分析
(二)构建maven工程,添加依赖
(三)构建数据库表并创建实体类
(四)编写dao层的接口UserMapper
(五)构建mapper接口对应的sql配置文件
(六)构建服务层接口UserService
(七)构建服务层实现类UserServiceImpl
(八)构建配置文件
(九)测试代码
Spring是一个分层的java SE/EE full-stack(一站式)轻量级开源框架,以IOC(控制反转)和AOP(面向切面编程)为内核。
在java三层架构当中分别提供了相应的技术:
表现层(web层):SpringMVC框架
业务层(service层):Bean管理(IOC容器)
持久层(dao层):jdbcTemplate模板对象以及提供了ORM模块整合其他优秀的持久层技术。
1997年,IBM提出了EJB的思想。1998年,SUN制定开发标准规范EJB1.0.。199年EJB1.1发布。2001年,EJB2.0发布。2006年EJB3.0发布。
方便解耦,简化开发:Spring就是一个工厂,可以管理所有对象的创建和依赖关系维护,交给Spring管理。
AOP编程的支持:可以方便的实现对程序进行权限拦截,日志记录,运行的监控。
声明式事务的支持:通过配置的方式完成对事务的管理,无需手动编程。
方便程序的测试:对Junit支持,可以通过注解方式方便的对Spring程序进行测试。
整合外部优秀技术:Spring内部提供了对各种优秀框架(Hibernate,Mybatis)的直接支持。
javaEE技术的封装;Spring对javaEE开发当中复杂难用的API进行封装,降低了这些API的使用难度。
程序的耦合是程序之间的关联性,也就是多个类的联系是否紧密,多个对象之间的关系是否密切。
当我们讲解jdbc时,是通过反射来注册驱动的,代码如下:
Class.forName("com.mysql.jdbc.Driver");//使用的驱动类是指定了一个字符串
我们的类中在编译阶段不再需要具体的驱动类,就算删除mysql的驱动jar包,依然可以通过编译。在运行阶段才会依赖驱动包。实际开发当中,我们应该做到编译不依赖,运行时才依赖。
上述代码产生的新问题,mysql驱动类的全限定类名作为一个字符串java类中是写死的,一旦发生改变,还需要修改源码。
使用配置文件结合反射就可以解决上述问题。
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。
那么这个读取配置文件,创建和获取三层对象的类就是工厂。
SpringIOC:IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。
IOC这个概念简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部透明的,从而降低了解决问题的复杂度,而且可以灵活的被重用和扩展。
IOC理论提出的观点大体是这样的:借助于第三方实现具有依赖关系的对象之间的解耦。
由于引进了中间位置“第三方”,也就是IOC容器,使得A、B、C、D这四个对象没有了耦合关系,齿轮之间的传动全部依靠IOC容器,全部对象的控制权上交给IOC容器,所以IOC容器成了整个系统的关键核心,它起到一个“粘合剂”的作用,把系统中所有对象粘合在一起发挥作用。
当把图中的IOC容器拿掉,可以发现A、B、C、D这四个 对象之间没有耦合关系。这样的话,当我们去实现A的时候,根本无需去考虑其他几个对象,对象之间的依赖关系已经讲到了最低程度。
IOc本质上就是一个大工程,大容器。主要作用就是创建和管理对象的依赖关系,削减计算机程序的耦合(解除我们代码间的依赖关系),提高程序的可扩展性和可维护性。
5.2.5.RELEASE
org.springframework
spring-context
${spring.version}
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userDao save method running...... ");
}
}
public interface UserService {
public void saveService();
}
public class UserServiceImpl implements UserService {
@Override
public void saveService() {
System.out.println("userSerivce save method running......");
}
}
@Test
public void test1(){
//1:获得spring IOC容器对象:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//2:从容器当中根据id获得UserDaoImpl 对象,执行userDao对象的save方法
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
//3:从容器当中根据id获得UserServiceImpl 对象,执行userService对象的saveService
方法
UserService userService = (UserService)applicationContext.getBean("userService");
userService.saveService();
}
运行结果:
bean标签:用于配置对象交给Spring来创建
默认情况下他会调用类中无参数的构造器,如果没有无参数构造器则不能创建成功。
基本属性:
id:Bean实例对象在Spring容器中的唯一标识
class:Bean的全限定类名
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂,Spring中工厂的类结构图如下;
1)BeanFactory:IOC容器的基本实现,是Spring内部使用的接口,不提供开发人员使用,加载配置文件时,不会创建对象,在获得(使用)对象时才采取创建对象。
2)HierarchicalBeanFactory:这个工厂接口非常简单,实现了Bean工厂的分层。工厂接口也是继承自BeanFactory,也是一个二级接口,相对于父接口,他只是扩展了一个重要的功能———工厂分层
3)AutowireCapableBeanFactory:该接口有自动配置能力,需要注意的是,ApplicationContext接口并实现此接口,因为应用代码很少用到此功能,如果确实需要的话,可以调用ApplicationContext的getAutowireCapableBeanFactory方法,来获取此接口的实例
4)ListableBeanFactory:获取bean时,Spring鼓励使用这个接口定义的api,如查看bean
的个数、获取某一类型Bean的配置名、查看容器中是否包括某一Bean等方法。
5)ApplicationContext:BeanFactory接口的子接口,提供更多强大的功能,一般由开发人员使用。接口提供了bean基础性操作同时,扩展了国际化等功能。ApplicationContext接口在加载配置文件时候就会配置文件当中的对象进行创建,存放IOC容器当中
6)AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建spring容器。他用来读取注解。
7)ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件
8)FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
1)BeanFactionPostProcessor
作用:定义了在bean工厂对象创建后,bean对象创建前执行的动作,用于对工厂进行创建后业务处理
运行时机:操作用于对工厂进行处理,仅运行一次
2) BeanPostProcessor
作用:定义了所有bean初始化前后进行的统一动作,用于对bean进行创建前业务处理与创建后业务处理
运行时机:当前操作伴随着每个bean的创建过程,每次创建bean均运行该操作。
3)InitializingBean
作用:定义了每个bean的初始化前进行的动作,属于非统一性动作,用于对bean进行创建前业务处理。
运行时机:当前操作伴随着任意一个bean的创建过程,保障其个性化业务处理
5.2.5.RELEASE
org.springframework
spring-context
${spring.version}
junit
junit
4.12
test
//设定UserDao接口
public interface UserDao {
public void save();
}
//设定UserDao接口实现类
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userDao save method running...... ");
}
}
配置文件:
dom4j
dom4j
1.6.1
/**
* 创建自己的工厂类
*/
public class MyBeanFactory {
//创建一个map集合,模拟IOC容器
private static Map map = new HashMap<>();
static{
try {
//使用dom4J 解析xml文件:
//第一步:获得一个解析器:
SAXReader reader = new SAXReader();
//第二: 读取外部的配置文件:
String path = "src/main/resources/applicationContext.xml";
//第三: 读取了整个文档对象
Document document = reader.read(path);
//第四: 获得根节点:
Element rootElement = document.getRootElement();//beans
//第五: 获得根节点下的所有的bean 标签对应的节点:
List bean = rootElement.elements("bean");// bean
for (Element element : bean) {
//获得id属性对应的值:
String id1 = element.attributeValue("id");
//获得class属性对应的值:【全限定类名】
String aClass = element.attributeValue("class");//获得class对应的值: 全限定类名。
//通过反射创建对象:
Class clz = Class.forName(aClass);
Object object = clz.newInstance();
//存容器 id做key,创建出来的对象value
map.put(id1,object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据id从容器当中获得对象
* @param id id的名称
* @return 返回Object类型对象
*/
public static Object getBean(String id){
Object o = map.get(id);
return o;
}
}
@Test
public void testMyFactory(){
//1:创建工厂对象
MyBeanFactory factory =new MyBeanFactory();
//2:从容器当中根据id获得对象
UserDao userDao = (UserDao) factory.getBean("userDao");
System.out.println(userDao);
userDao.save();
}
public class User implements Serializable {
public User(){
System.out.println("user created...");
}
}
@Test
public void testUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
public class ItemFactory {
//静态方法返回实例bean
public static User createUser(){
System.out.println("static method running create bean ......");
return new User();
}
}
@Test
public void testItemFactory(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
public class NewItemFactory {
//工厂的非静态方法返回bean实例
public User createUser(){
System.out.println("Dynamic method running create bean ......");
return new User();
}
}
@Test
public void testNewItemFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
所谓Bean的作用域其实就是指Spring给我们创建出的对象的存活范围,在配置文件中通过bean的scope属性指定
scope:指对象的作用范围,取值如下:
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
scope指定为其他值,需要在特定的环境下使用。
它是SpringBoot框架核心IOC的具体实现。组件之间的依赖关系由容器在应用系统运行期来决定,也就是由动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。
public class Account {
private String name;
private Integer age;
private Date birthday;
public Account(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
@Test
public void testDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Account account = (Account) applicationContext.getBean("account");
System.out.println("name:"+account.getName()+" age:"+account.getAge()+"birthday:"+account.getBirthday());
//打印结果: name:王达 age:20 birthday:Thu Feb 04 13:53:45 CST 2021
}
public class Account {
private String name;
private Integer age;
private Date birthday;
public Account() {
}
public Account(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
@Test
public void testDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Account account = (Account) applicationContext.getBean("account");
System.out.println("name:"+account.getName()+" age:"+account.getAge()+"birthday:"+account.getBirthday());
//测试结果: name:张三丰 age:31 birthday:Thu Feb 04 14:05:19 CST 2021
}
public class Account {
//注入数组,List集合,Set集合,Map集合,Properties集合属性
private String[] myStrs;
private List myList;
private Set mySet;
private Map myMap;
private Properties myProps;
public Account() {
}
public String[] getMyStrs() {
return myStrs;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public List getMyList() {
return myList;
}
public void setMyList(List myList) {
this.myList = myList;
}
public Set getMySet() {
return mySet;
}
public void setMySet(Set mySet) {
this.mySet = mySet;
}
public Map getMyMap() {
return myMap;
}
public void setMyMap(Map myMap) {
this.myMap = myMap;
}
public Properties getMyProps() {
return myProps;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
}
array-AAA
array-BBB
array-CCC
list-AAA
list-BBB
list-CCC
set-AAA
set-BBB
set-CCC
AAA
BBB
@Test
public void testDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Account account = (Account) applicationContext.getBean("account");
System.out.println("array:"+ Arrays.toString(account.getMyStrs()));
System.out.println("list:"+account.getMyList());
System.out.println("set:"+account.getMySet());
System.out.println("map:"+account.getMyMap());
System.out.println("props:"+account.getMyProps());
/*
测试结果:
array:[array-AAA, array-BBB, array-CCC]
list:[list-AAA, list-BBB, list-CCC]
set:[set-AAA, set-BBB, set-CCC]
map:{map-a=AAA, map-b=BBB}
props:{pro-b=BBB, pro-a=AAA}
*/
}
并列的多个配置文件直接编写多个配置文件,比如beans1.xml,beans2.xml,...,然后在创建ApplicationContext的时候,直接传入多个配置文件。
ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
主从配置文件,先配置一个主配置文件,然后在里面导入其他的配置文件。
注意:
同一个xml文件中不能出现相同名称的bean,如果出现会报错。‘’
多个xml文件如果出现相同的名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽量保证bean的名称是唯一的。
模块方法(Template Method)模式的定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
模块方法模式的静态结构图:
在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来发到一个模块抽象类中程序主框架相同,细节不同的情况下,也可以使用模块方法。
举例说明:
分析:出国留学手续一般经过以下流程:索取学校资料,提出入学申请,办理因私出国护照、出境卡和公证,申请签证,体检、订机票、准备行装,抵达目标学校等,其中有些业务对各个学校是一样的,但有些业务因学校不同而不同,所以比较适合用模板方法模式来实现。
在本实例中,我们先定义一个出国留学的抽象类 StudyAbroad,里面包含了一个模板方法 TemplateMethod(),该方法中包含了办理出国留学手续流程中的各个基本方法,其中有些方法的处理由于各国都一样,所以在抽象类中就可以实现,但有些方法的处理各国是不同的,必须在其具体子类(如美国留学类 StudyInAmerica)中实现。如果再增加一个国家,只要增加一个子类就可以了。
抽象类:
//抽象类: 出国留学
public abstract class StudyAbroad {
//定义抽象方法,索取学校资料
public abstract void LookingForSchool();
//定义抽象方法,定义入学申请
public abstract void ApplyForEnrol();
//定义入学申请方法:
public void ApplyForPassport() {
System.out.println("三.办理因私出国护照、出境卡和公证:");
System.out.println(" 1)持录取通知书、本人户口簿或身份证向户口所在地公安机关申请办理因私出国护照和出境卡。");
System.out.println(" 2)办理出生公证书,学历、学位和成绩公证,经历证书,亲属关系公证,经济担保公证。");
}
//定义申请签证方法
public void ApplyForVisa() {
System.out.println("四.申请签证:");
System.out.println(" 1)准备申请国外境签证所需的各种资料,包括个人学历、成绩单、工作经历的证明;个人及家庭收入、资金和财产证明;家庭成员的关系证明等;");
System.out.println(" 2)向拟留学国家驻华使(领)馆申请入境签证。申请时需按要求填写有关表格,递交必需的证明材料,缴纳签证。有的国家(比如美国、英国、加拿大等)在申请签证时会要求申请人前往使(领)馆进行面试。");
}
//体检、订机票、准备行装 方法
public void ReadyGoAbroad() {
System.out.println("五.体检、订机票、准备行装:");
System.out.println(" 1)进行身体检查、免疫检查和接种传染病疫苗;");
System.out.println(" 2)确定机票时间、航班和转机地点。");
}
//定义抵达抽象方法
public abstract void Arriving();
}
子类
//定义具体的子类,美国留学
public class StudyInAmerica extends StudyAbroad {
//索取资料的具体实现
@Override
public void LookingForSchool() {
System.out.println("一.索取学校以下资料:");
System.out.println(" 1)对留学意向国家的政治、经济、文化背景和教育体制、学术水平进行较为全面的了解;");
System.out.println(" 2)全面了解和掌握国外学校的情况,包括历史、学费、学制、专业、师资配备、教学设施、学术地位、学生人数等;");
System.out.println(" 3)了解该学校的住宿、交通、医疗保险情况如何;");
System.out.println(" 4)该学校在中国是否有授权代理招生的留学中介公司?");
System.out.println(" 5)掌握留学签证情况;");
System.out.println(" 6)该国政府是否允许留学生合法打工?");
System.out.println(" 8)毕业之后可否移民?");
System.out.println(" 9)文凭是否受到我国认可?");
}
//入学申请的具体实现
@Override
public void ApplyForEnrol() {
System.out.println("二.入学申请:");
System.out.println(" 1)填写报名表;");
System.out.println(" 2)将报名表、个人学历证明、最近的学习成绩单、推荐信、个人简历、托福或雅思语言考试成绩单等资料寄往所申请的学校;");
System.out.println(" 3)为了给签证办理留有充裕的时间,建议越早申请越好,一般提前1年就比较从容。");
}
//抵达的具体实现
@Override
public void Arriving() {
System.out.println("六.抵达目标学校:");
System.out.println(" 1)安排住宿;");
System.out.println(" 2)了解校园及周边环境。");
}
}
public class StudyAbroadProcess {
public static void main(String[] args) {
StudyAbroad tm = new StudyInAmerica();
tm.TemplateMethod();
}
}
Spring提供了ioc容器,管理jdbc操作数据库的过程中需要的数据库连接对象,同时Spring提供了整合jdbc操作数据库的工具类JdbcDaoSupport 和模板工具 JdbcTemplate,在JdbcTemplate中提供了大量的操作数据库的方式供用户使用。所以我们只需要获取模板工具类然后调用方法就可以完成Jdbc的操作了。
org.springframework
spring-context
${spring.version}
org.springframework
spring-jdbc
${spring.version}
mysql
mysql-connector-java
5.1.47
com.mchange
c3p0
0.9.5.2
junit
junit
4.12
test
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
public interface AccountDao {
public void save(Account account);
public void delete(Integer id);
public void update(Account account);
public Account findById(Integer id);
public Integer getTotalRecords();
public List findAll();
}
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void save(Account account) {
String sql ="insert into account(name,money) values(?,?)";
jdbcTemplate.update(sql,account.getName(),account.getMoney());
}
@Override
public void delete(Integer id) {
String sql ="delete from account where id = ? ";
jdbcTemplate.update(sql,id);
}
@Override
public void update(Account account) {
String sql ="update account set money = ? , name=? where id= ?";
jdbcTemplate.update(sql,account.getMoney(),account.getName(),account.getId());
}
@Override
public Account findById(Integer id) {
String sql ="select * from account where id = ? ";
Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class),id);
return account;
}
@Override
public Long getTotalRecords() {
Long count = jdbcTemplate.queryForObject("select count(*) from account",Long.class);
System.out.println(count);
return count;
}
@Override
public List findAll() {
String sql ="select * from account";
List accountList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Account.class));
return accountList;
}
}
public interface AccountService {
public void save(Account account);
public void delete(Integer id);
public void update(Account account);
public Account findById(Integer id);
public Long getTotalRecords();
public List findAll();
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void save(Account account) {
accountDao.save(account);
}
@Override
public void delete(Integer id) {
accountDao.delete(id);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public Account findById(Integer id) {
return accountDao.findById(id);
}
@Override
public Long getTotalRecords() {
return accountDao.getTotalRecords();
}
@Override
public List findAll() {
return accountDao.findAll();
}
}
将数据库的连接信息抽取到外部配置文件中,和spring的配置文件分离开,有利于后期维护
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
//测试save方法
@Test
public void testJdbcTemplateSave(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Account account = new Account();
account.setName("jack");
account.setMoney(1001D);
service.save(account);
}
//测试update方法
@Test
public void testJdbcTemplateUpdate(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Account account = new Account();
account.setName("jack2");
account.setMoney(999D);
account.setId(1008);
service.update(account);
}
//测试delete方法
@Test
public void testJdbcTemplateDelete(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
service.delete(1001);
}
//测试唯一性查询findById
@Test
public void testJdbcTemplateFindById(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Account account = service.findById(1001);
System.out.println(account);
}
//测试总记录数
@Test
public void testJdbcTemplateGetTotalRecords(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Long totalRecords = service.getTotalRecords();
System.out.println("表当中的总记录数为:"+totalRecords);
}
//测试账户列表
@Test
public void testJdbcTemplateGetAll(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
List accountList = service.findAll();
accountList.forEach((account -> {
System.out.println(account);
}));
}
注解 | 说明 |
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
注意:
//@Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("save running... ...");
}
}
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("save running... ...");
}
}
注解 | 说明 |
@Value | 注入普通属性 |
@Autowired | 自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。 |
@Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,按照名称进行注入 |
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Value("注入普通数据")
private String str;
@Value("${jdbc.driver}")
private String driver;
@Override
public void save() {
System.out.println(str);
System.out.println(driver);
System.out.println("save running... ...");
}
}
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
/*@Autowired
@Qualifier("userDao")*/
@Resource(name="userDao")
private UserDao userDao;
@Override
public void save() {
userDao.save();
}
}
注解 | 说明 |
@Scope | 标注Bean的作用范围,scope取值singleton prototype request session globalsession |
使用@Scope标注Bean的范围
//@Scope("prototype")
@Scope("singleton")
public class UserDaoImpl implements UserDao {
//此处省略代码
}
org.springframework
spring-context
${spring.version}
org.springframework
spring-jdbc
${spring.version}
mysql
mysql-connector-java
5.1.47
com.mchange
c3p0
0.9.5.2
junit
junit
4.12
test
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save(User user) {
String sql ="insert into user (name,birthday) values(?,?)";
jdbcTemplate.update(sql,user.getName(),user.getBirthday());
}
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao ;
@Override
public void saveService(User user) {
userDao.save(user);
}
}
//IOC 注解版本的案例:
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
User user = new User();
user.setName("admin");
user.setBirthday(new Date());
userService.saveService(user);
}
注解 | 说明 |
@Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定Spring在初始化容器时要扫描的包。作用和在Springdexml配置文件中的 |
@Bean | 使用在方法上,标注将该方法的返回值存储到Spring容器中 |
@PropertySource | 用于加载xxx.properties文件中的配置 |
@Import | 用于导入其他配置类 |
@Configuration //指定当前类是一个配置类,取代applicationContext.xml配置文件
@ComponentScan("com.offcn") //指定Spring在初始化容器时要扫描的包
@Import({xxx.class}) //导入其他的配置类
public class SpringConfiguration {
...
}
@PropertySource("classpath:dbConfig.properties") //当前类中引入dbConfig.properties文件
public class DataSourceConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
}
@Bean(name="dataSource") //将方法的返回值存入到IOC容器当中
public DataSource getDataSource() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
public void AnnotationTest (){
//使用AnnotationConfigApplicationContext获得ioc容器对象
ApplicationContext context= new AnnotationConfigApplicationContext(SpringConfig.class);
Object obj = context.getBean("...");
}
在测试类中,每个测试方法都有一下两行代码:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
5.2.5.RELEASE
junit
junit
4.12
test
org.springframework
spring-test
${spring.version}
org.springframework
spring-context
${spring.version}
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunitTest {
}
@RunWith(SpringJUnit4ClassRunner.class)
//加载spring核心配置文件
//@ContextConfiguration(value = {"classpath:applicationContext.xml"})
//加载spring核心配置类
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private ApplicationContext context;
@Test
public void test(){
...
}
}
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
commons-dbutils
commons-dbutils
1.7
public interface AccountDao {
public Account findByName(String name);
public void update(Account account);
}
public class AccountDaoImpl implements AccountDao {
private QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
@Override
public Account findByName(String name) {
try {
String sql ="select *from account where name =? ";
Account account = queryRunner.query(sql, new BeanHandler<> (Account.class), name);
return account;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public void update(Account account) {
try {
String sql ="update account set money =? where name =? ";
queryRunner.update(sql, account.getMoney(), account.getName());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public interface AccountService {
/**
* 定义业务方法, 实现转账
* @param sourceAccountName 来源账户
* @param targetAccountName 目标账户
* @param money 转账金额
*/
public void transfer(String sourceAccountName,String targetAccountName,double money);
}
public class AccountServiceImpl implements AccountService {
//依赖dao层
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String sourceAccountName, String targetAccountName, double money) {
//查询来源账户和目标账户
Account sAccount = accountDao.findByName(sourceAccountName);
Account tAccount = accountDao.findByName(targetAccountName);
//来源账户减钱,目标账户加钱
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
//持久化到数据库
accountDao.update(sAccount);
//模拟异常发生
int i=1/0;
accountDao.update(tAccount);
}
}
@Test
public void test1(){
//获得业务层对象:
AccountService service = new AccountServiceImpl();
service.transfer("aaa","bbb",100D);
}
public class ConnectionUtils {
private ThreadLocal tl = new ThreadLocal();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程上的连接
* @return
*/
public Connection getThreadConnection() {
try{
//1.先从ThreadLocal上获取
Connection connection = tl.get();
//2.判断当前线程上是否有连接
if (connection == null) {
//3.从数据源中获取一个连接,并且存入ThreadLocal中
connection = dataSource.getConnection();
tl.set(connection);
}
//4.返回当前线程上的连接
return connection;
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 把连接和线程解绑
*/
public void removeConnection(){
tl.remove();
}
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
public void release(){
try {
connectionUtils.removeConnection();
connectionUtils.getThreadConnection().close();//还回连接池中
}catch (Exception e){
e.printStackTrace();
}
}
}
public class AccountDaoImpl implements AccountDao {
private QueryRunner queryRunner ;
private ConnectionUtils connectionUtils;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
@Override
public Account findByName(String name) {
try {
String sql ="select * from account where name =? ";
Account account = queryRunner.query(connectionUtils.getThreadConnection(), sql,new BeanHandler(Account.class), name);
return account;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public void update(Account account) {
try {
String sql ="update account set money =? where name =? ";
queryRunner.update(connectionUtils.getThreadConnection(),sql,account.getMoney(),account.getName());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public class AccountServiceImpl implements AccountService {
//依赖dao层
private AccountDao accountDao ;
private TransactionManager txManager;
public AccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public TransactionManager getTxManager() {
return txManager;
}
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
@Override
public void transfer(String sourceAccountName, String targetAccountName,double money) {
try {
//开启事务:
txManager.beginTransaction();
//查询来源账户和目标账户
Account sAccount = accountDao.findByName(sourceAccountName);
Account tAccount = accountDao.findByName(targetAccountName);
//来源账户减钱,目标账户加钱
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
//持久化到数据库
accountDao.update(sAccount);
//模拟异常发生
int i=1/0;
accountDao.update(tAccount);
} catch (Exception exception){
//事务回滚
txManager.rollback();
exception.printStackTrace();
} finally {
//释放资源
txManager.release();
}
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestAccountTransfer {
@Autowired
private AccountService accountService;
@Test
public void test1(){
accountService.transfer("aaa","bbb",100);
}
}
/**
* 用于创建客户业务层对象工厂(当然也可以创建其他业务层对象,只不过我们此处不做那么繁琐)
*/
public class BeanFactory {
private AccountService accountService;
private TransactionManager txManager;
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
/**
* 获取Service代理对象
* @return
*/
public AccountService getAccountService() {
return (AccountService)
Proxy.newProxyInstance(accountService.getClass().getClassLoader(),accountService.getClass().getInterfaces(),new InvocationHandler() {
/**
* 添加事务的支持
* @param proxy
* @param method 执行目标方法被封装到Method当中
* @param args 执行目标方法的参数
* @return 执行目标方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returntValue = null;
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
returntValue = method.invoke(accountService, args);
//3.提交事务
txManager.commit();
//4.返回结果
return returntValue;
} catch (Exception e) {
//5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
} finally {
//6.释放连接
txManager.release();
}
}
});
}
}
配置静态工厂,生产实例bean
业务层代码的修改:把和事务相关的代码去掉
@Override
public void transfer(String sourceAccountName, String targetAccountName, double money) {
Account sAccount = accountDao.findByName(sourceAccountName);
Account tAccount = accountDao.findByName(targetAccountName);
//来源账户减钱,目标账户加钱
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
//持久化到数据库
accountDao.update(sAccount);
//模拟异常发生
//int i=1/0;
accountDao.update(tAccount);
}
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestAccountTransfer {
@Autowired
@Qualifier("proxyAccountService")
private AccountService accountProxyService;
@Test
public void testAccountProxy(){
accountProxyService.transfer("aaa","bbb",100D);
}
}
总结:使用动态代理改造之后,业务层代码已经和事务相关代码进行了分离,并且保证了事务的一致性。
AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP面向切面编程是一种编程思想,是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
只提供接口的代理,不支持类的代理。JDK动态代理通过反射来接受被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是invocationHandler接口和Proxy类
通过继承的方式来实现动态代理,因此如果某个类被标记为final,那么他是无法使用CGLB做动态代理。
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强。
优势:减少重复代码,提高开发效率,并且便于维护。
5.2.5.RELEASE
org.springframework
spring-context
${spring.version}
org.aspectj
aspectjweaver
1.8.7
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
public interface AccountDao {
public Account findByName(String name);
public void update(Account account);
}
public class AccountDaoImpl implements AccountDao {
private QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
@Override
public Account findByName(String name) {
try {
String sql ="select *from account where name =? ";
Account account = queryRunner.query(sql, new BeanHandler<>(Account.class), name);
return account;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public void update(Account account) {
try {
String sql ="update account set money =? where name =? ";
queryRunner.update(sql, account.getMoney(), account.getName());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public interface AccountService {
/**
* 定义业务方法, 实现转账
* @param sourceAccountName 来源账户
* @param targetAccountName 目标账户
* @param money 转账金额
*/
public void transfer(String sourceAccountName,String targetAccountName,double money);
}
public class AccountServiceImpl implements AccountService {
//依赖dao层
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String sourceAccountName, String targetAccountName,double money) {
//查询来源账户和目标账户
Account sAccount = accountDao.findByName(sourceAccountName);
Account tAccount = accountDao.findByName(targetAccountName);
//来源账户减钱,目标账户加钱
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
//持久化到数据库
accountDao.update(sAccount);
//模拟异常发生
int i=1/0;
accountDao.update(tAccount);
}
}
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
public void release(){
try {
connectionUtils.removeConnection();
connectionUtils.getThreadConnection().close();//还回连接池中
}catch (Exception e){
e.printStackTrace();
}
}
}
aop:config:
作用: 开始声明aop配置
aop:aspect
作用: 用于配置切面
属性:id :给切面提供一个唯一标识。
ref:引用配置好的通知类 bean 的 id。
aop:pointcut
作用: 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。
属性: expression:用于定义切入点表达式。
id:用于给切入点表达式提供一个唯一标识
作用:用于指定通知类中的增强方法名称
属性:method:用于指定通知类中的增强方法名称
ponitcut-ref:用于指定切入点的表达式的引用
poinitcut:用于指定切入点表达式
执行时间点:切入点方法执行之前执行
作用:用于配置后置通知
属性:method:指定通知中方法的名称
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:切入点方法正常执行之后。它和异常通知只能有一个执行。
作用:用于配置异常通知
属性:method:指定通知中方法的名称
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:切入点方法执行产生异常后执行。它和后置通知只能执行一个。
作用:用于配置最终通知
属性:method:指定通知中方法的名称
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:无论切入点方法执行时是否有异常,它都会在其后面执行。
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
在TransactionManager类当中添加方法
/**
* 环绕通知:
* spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参
数。
* 在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。
* @param pjp
* @return
*/
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object returnValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
returnValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return returnValue;
}
aop:around:
作用:用于配置环绕通知
属性:method:指定通知中方法的名称
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
说明:它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
注意:通常情况下,环绕通知都是独立使用的。
5.2.5.RELEASE
org.springframework
spring-context
${spring.version}
org.aspectj
aspectjweaver
1.8.7
沿用上一章节资源
copy Account AccountDao AccountDaoImpl AccountService AccountServiceImpl
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
@Component("txManager")
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
}
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
@Component("txManager")
@Aspect //表明当前类是一个切面类
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
}
作用:把当前方法看成是前置通知
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
//开启事务
@Before("execution(* com.offcn.service.impl.*.*(..)))")
public void beginTransaction(){
try {
System.out.println("before..........................");
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
作用:把当前方法看成是后置通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
// 提交事务
@AfterReturning("execution(* com.offcn.service.impl.*.*(..)))")
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
@AfterThrowing
作用:把当前方法看成是异常通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
//回滚事务
@AfterThrowing("execution(* com.offcn.service.impl.*.*(..)))")
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
@After
作用:把当前方法看成是最终通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用 。
//释放连接
@After("execution(* com.offcn.service.impl.*.*(..)))")
public void release(){
try {
connectionUtils.removeConnection();
connectionUtils.getThreadConnection().close();//还回连接池中
}catch (Exception e){
e.printStackTrace();
}
}
@Around
作用:把当前方法看成是环绕通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
// 环绕通知:
@Around("execution(*com.offcn.service.impl.*.*(..))")
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object returnValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
returnValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return returnValue;
}
@Pointcut("execution(* com.offcn.service.impl.*.*(..))")
private void point1() {}
// 环绕通知:
@Around("point1()")///注意:千万别忘了写括号
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object returnValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
returnValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return returnValue;
}
5.2.5.RELEASE
junit
junit
4.12
test
org.springframework
spring-context
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
mysql
mysql-connector-java
5.1.47
com.mchange
c3p0
0.9.5.2
org.aspectj
aspectjweaver
1.8.7
copy Account AccountDao AccountDaoImpl AccontService AccountServiceImpl 代码:
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findByName(String name) {
String sql ="select * from account where name =? ";
Account account = this.jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
return account;
}
@Override
public void update(Account account) {
String sql ="update account set money =? where name =? ";
this.jdbcTemplate.update(sql, account.getMoney(), account.getName());
}
}
5.2.5.RELEASE
junit
junit
4.12
test
org.springframework
spring-context
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
mysql
mysql-connector-java
5.1.47
com.mchange
c3p0
0.9.5.2
org.aspectj
aspectjweaver
1.8.7
copy Account AccountDao AccountImpl AccountService AccountServiceImpl 到工程当中复用
/**
该注解的属性和 xml 中的属性含义一致。该注解可以出现在接口上,类上和方法上。
出现接口上,表示该接口的所有实现类都有事务支持。
出现在类上,表示类中所有方法有事务支持
出现在方法上,表示方法有事务支持。
以上三个位置的优先级:方法>类>接口
*/
@Service("accountService")
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {
//依赖dao层
@Autowired
private AccountDao accountDao ;
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
@Override
public void transfer(String sourceAccountName, String targetAccountName,Double money) {
Account sAccount = accountDao.findByName(sourceAccountName);
Account tAccount = accountDao.findByName(targetAccountName);
//来源账户减钱,目标账户加钱
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
//持久化到数据库
accountDao.update(sAccount);
//模拟异常发生
//int i=1/0;
accountDao.update(tAccount);
}
}
Mybatis框架是一个持久层ORM框架,而Spring则是一个综合性一站式框架。所以整合是Mybatis往Spring上整合。就是让Spring框架接管Mybatis的组件。
5.2.5.RELEASE
junit
junit
4.12
test
org.springframework
spring-context
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
mysql
mysql-connector-java
5.1.47
com.mchange
c3p0
0.9.5.2
org.aspectj
aspectjweaver
1.8.7
org.mybatis
mybatis-spring
2.0.0
org.mybatis
mybatis
3.4.6
create table User(
id int primary key auto_increment,
name varchar(32) not null,
address varchar(32) not null,
birthday date
);
public class User implements Serializable {
private Integer id;
private String name;
private String address;
private Date birthday;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", address='" + address + '\'' +
", birthday=" + birthday +
'}';
}
}
public interface UserMapper {
int insert(User user);
int update(User user);
int delete(Integer id);
User findById(Integer id);
List findAll();
}
insert into user (name, birthday, address)
values (#{name}, #{birthday},#{address})
update user
set
name= #{name},
birthday=#{birthday},
address = #{address}
where id=#{id}
delete from user
where id =#{id}
public interface UserService {
int insert(User user);
int update(User user);
int delete(Integer id);
User findById(Integer id);
List findAll();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int insert(User user) {
int num = userMapper.insert(user);
return num;
}
@Override
public int update(User user) {
int num = userMapper.update(user);
return num;
}
@Override
public int delete(Integer id) {
int num = userMapper.delete(id);
return num;
}
@Override
public User findById(Integer id) {
User user = userMapper.findById(id);
return user;
}
@Override
public List findAll() {
List userList = userMapper.findAll();
return userList;
}
}
helperDialect=MySQL
reasonable=true
supportMethodsArguments=true
params=count=countSql
autoRuntimeDialect=true
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestAccountTransfer {
@Autowired
private UserService userService;
//save
@Test
public void testInsert(){
User user = new User();
user.setName("admin");
user.setAddress("china");
user.setBirthday(new Date());
int num = userService.insert(user);
System.out.println("num:"+num);
}
//update
@Test
public void testUpdate(){
User user = new User();
user.setName("marry");
user.setAddress("America");
user.setBirthday(new Date());
user.setId(1);
int num = userService.update(user);
System.out.println("num:"+num);
}
//delete:
@Test
public void testDelete(){
int num = userService.delete(1);
System.out.println("num:"+num);
}
//findById
@Test
public void testFindById(){
User user = userService.findById(2);
System.out.println("user:"+user);
}
//findAll
@Test
public void testFindByAll(){
List userList = userService.findAll();
System.out.println("userList:"+userList);
}
}