Spring初始
以下内容如有侵权请联系删除
Spring第一天
第一章 JavaEE三层架构
第二章 IOC思想推导(难点)
之前的代码逻辑
- servlet中需要调用service : UserService userService = new UserServicelmpl()
耦合关系 代码片段中的依赖关系(如上,UserServicelmpl与UserService耦合关系太强,如何验证代码片段的耦合关系的强弱(依赖关系),例如将UserServicelmpl.java文件删除,看是否会报编译异常,如果会报编译异常,说明耦合关系强;如果不报编译异常,说明没有耦合关系;如何解决这种耦合关系,通过反射或者配置文件)解耦:
反射,配置文件,面向接口编程(创建工厂类的实质就是利用反射和配置文件)
2.1 简化版本
(1)创建工程导入依赖
<dependencies><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6</version></dependency></dependencies>
(2)配置dao接口
(3)创建BeanFactory工具类
package cn.itcast.utils;public class BeanFactory {/*** 根据传入的参数名,反射创建对象* @param name* @return* @throws Exception*/public static Object getBean(String name) throws Exception{Class<?> aClass = Class.forName(name);return aClass.newInstance();}}
2.2 升级版本
(1)创建配置文件
(2)在BeanFactory解析配置文件
package cn.itcast.utils;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 单例工厂:多次调用getBean方法,获取的对象是同一个* 1、当程序第一次访问BeanFactory,解析Bean.xml配置文件,创建对象* 2、当调用getBean方法的时候,从map集合中直接获取目标对象*/public class BeanFactory {/*** map存放:* userDao = UserDaoImpl对象*/private static Map<String,Object> map=new HashMap<String,Object>();static{try {//1.获取配置文件的输入流InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");//2.创建SAXReader对象SAXReader reader = new SAXReader();//3.获取Document对象Document document = reader.read(is);//4.获取所有的bean节点Element root = document.getRootElement(); //获取跟节点List<Element> bean = root.elements("bean");//5.解析bean节点,获取id和class属性for (Element element : bean) {String id = element.attributeValue("id"); //userDaoString cls = element.attributeValue("class"); //全限定类名Class<?> aClass = Class.forName(cls);Object obj = aClass.newInstance();//6.存入map集合map.put(id, obj);}} catch (Exception e) {}}/*** 根据传入的参数名,反射创建对象* @param key* @return* @throws Exception*/public static Object getBean(String key) throws Exception {return map.get(key);}}
2.3 总结
通过两种方式完成了对象的对象(userDaolmpl)
- 最简单的版本:通过手动反射创建对象(手动new对象)
- 第二个版本:所需的目标对象,从map集合(容器)中获取(控制反转:IOC)
ioc:对象的获取方式,由传统的主动实例化对象〈new对象),变为从容器(map)集合中获取
第三章 Spring概述和控制反转
3.1 什么是Spring
- Spring是于2003年兴起的一个full-stack轻量级的Java开源框架,由Rod Johnson创建
- Spring以IOC(控制反转)和AOP(面向切面编程)为核心
- Spring提供了展现层Spring MVC、持久层Spring JDBC、业务层事务管理等众多的企业级应用技术
- Spring还能整合开源世界众多的第三方框架和类库,逐渐成为使用最多的ava EE企业应用开源框架
3.2 认识IOC
IOC(控制反转)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。
- 控制:指的是控制权,在java中可以简单理解为对象的控制权限(比如对象的创建、销毁等权限)
- 反转:指的是将对象的控制权由原来的程序员在类中主动控制反转到由Spring容器来控制。
- 对象的创建交由Spring容器管理,需要对象从容器中获取即可
- 主要功能解耦
- IOC的底层原理:反射
举个例子:找对象
- 传统方式:自己找,想要啥样的自己去大街上找(new) ,主动出击
- l0C方式:婚介所,首先人们将自己的信息注册到婚介所。然后,等你想要对象的时候,婚介所就会帮你找到,然后给你送来。
第四章 SpringIOC的入门案例(重点)
4.1 入门案例
创建工程导入依赖
<dependencies><!--spring的坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency><!--单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
创建Dao接口和实现类
创建Spring配置文件
- spring配置文件约定俗称::applicationContext.xml
- spring配置文件,需要引入名称空间(约束)
- 在spring的配置文件中通过标签,定义对象id和实现类全类名
在resource目录下创建applicationContext.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"><!--创建对象id:容器中对象的唯—标识(唯—别名)class:实现类的全限定类名,在springIoc容器中,会通过全类名,反射创建对象--><bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl"></bean></beans>
测试
4.2 执行过程分析
4.3 Spring的API介绍
第五章 对象的生命周期
5.1 对象作用域
<!--创建对象的作用域scope :作用singleton :单例对象容器创建的时候,反射创建对象存入map集合中prototype:多例对象容器创建的时候,不会反射创建此对象当调用容器的getBean方法的时候,反射创建一个新的对象--><bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl" scope="prototype"></bean>
5.2 生命周期
<!--对象的生命周期(对象和创建和销毁过程),和对象的作用域息息相关init-method : 对象实例化之后,自动执行的对象方法destroy-method:容器关闭,对象被销毁之前,自动执行的对象方法singleton(单例对象)1、容器创建(加载配置文件app1icationContext.xm1文件时,容器创建),自动的反射创建UserDaoImp1对象存入容器2、自动的执行init-method中配置的对象方法3、容器关闭时,对象销毁之前自动的执行destroy-method配置的对象方法prototype(多例对象)1、当调用一次getBean方法,创建一次对象2、当对象创建,调用init-method中配置的对象方法3、多例对象(没有存在容器中),自动的进行垃圾回收--><bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl" scope="prototype"init-method="initMethod" destroy-method="destoryMethod"></bean>
第六章 依赖注入(重点)
依赖注入:Dependency lnjection (Dl)。它是spring框架核心ioc的具体实现。我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。比如我们的Book中可能引入一个Publish类,在使用了Spring之后,它会为我们解决这些依赖对象的注入。
本质:向对象中的私有属性赋值
构造方法
set万法调用
6.1 构造方法注入
向对象添加有参构造方法
package cn.itcast.dao.impl;import cn.itcast.dao.UserDao;public class UserDaoImpl implements UserDao {private String username;private Integer age;public UserDaoImpl(){}//1.配置有参构造,在new对象的时候,直接对属性赋值public UserDaoImpl(String username,Integer age) {this.username = username;this.age = age;}public void save() {System.out.println("调用dao保存用户");}
}
在spring的配置文件中,通过bean标签配置对象创建(需要添加构造方法参数)
<!--相当于调用无参构造方法,实例化 UserDaoImplnew UserDaoImpl();构造方法注入参数:在bean标签中,通过constructor-arg配置构造方法参数new UserDaoImpl("王者荣耀",12);--><bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl"><!--配置构造参数index:指定参数的索引定位,从0开始type:通过参数的类型定位,Integername:通过参数的名称定位,username以上三个属性,适用于定位构造参数(三选一就可以)value:对基本数据类型的数据赋值(8个基本数据类型+String)ref:对java实体类对象赋值以上两个属性,适用于对参数赋值--><constructor-arg name="username" value="王者荣耀" ></constructor-arg><constructor-arg name="age" value="12" ></constructor-arg></bean>
6.2 set方法注入
提供属性的set方法
package cn.itcast.dao.impl;import cn.itcast.dao.UserDao;public class UserDaoImpl implements UserDao {private String username;private Integer age;public UserDaoImpl(){}//1.配置有参构造,在new对象的时候,直接对属性赋值public UserDaoImpl(String username,Integer age) {this.username = username;this.age = age;}public void setUsername(String username) {this.username = username;}public void setAge(Integer age) {this.age = age;}public void save() {System.out.println("调用dao保存用户");}
}
在spring配置文件中,通过bean结合property配置set方法调用
<!--set方法注入通过bean标签结合property标签配置对象创建之后,自动执行的set方法UserDao userDao = new UserDaoImpl();userDao.setUsername();userDao.setAge();property标签name :定位待执行的set方法value:对基本数据类型的属性赋值ref :对java实体类对象赋值--><bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl"><property name="username" value="LOL"></property><property name="age" value="15"></property></bean>
6.3 注入复杂类型(集合)
(1)注入数组类型
配置set方法
spring配置
(2)注入kv数据
java对象
配置文件
6.4 注入对象(重点)
第七章 配置文件的模块化(了解)
我们现在的配置都集中配在了一个applicationContext.xml文件中,当开发人员过多时,如果所有bean都配置到同一个配置文件中,会使这个文件巨大,而且也不方便维护。针对这个问题,Spring提供了多配置文件的方式,也就是所谓的配置文件模块化。
1、并列的多个配置文件
直接编写多个配置文件,比如说beans1.xml,beans2.xm…然后在创建ApplicationContext的时候,直接传入多个配置文件。
ApplicationContext act=new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
2、主从配置文件
先陪一个主配置文件,然后在里面导入其它的配置文件。
<import resource="beans1.xml"/><import resource="beans2.xml"/>
注意事项:
- 同一个xml文件中不能出现相同名称的bean,如果出现会报错
- 多个xml文件如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽量保证bean的名称是唯一的。
Spring第二天
第一章 DbUtils(会用)
1.1 DbUtils介绍
DbUtils是Apache的一款用于简化Dao代码的工具类,它底层封装了JDBC技术。
核心类:
- QueryRunner用于执行增删改查的SQL语句
- ResultSetHandler这是一个接口,主要作用是将数据库返回的记录封装进实体对象-------查询数据封装,结果集处理器
核心方法:
- update()用来执行增、删、改语句executeUpate
- query()用来执行查询语句executeQuery
//1.创建datasourcexxX
//2.创建QueryRunner
QueryRunner queryRunner = new QueryRunner(datasource); //update方法,用于执行增删改语句
//第一个参数:sq1语句后面的参数:sq1语句中的所需要的的值
queryRunner.update("insert into account value(nu71,?,?)",1,2);
//query方法,用于执行查询语句
//第一个参数:sq1语句―第一个参数:封装返回值―后面的参数:sq1语句中的所需要的的值
//BeanHandler用于将一条返回数据封装成一个JavaBean,类似的子类还有BeanListHandler等
queryRunner. query("select * from account where aid = ?", new BeanHandler<Account>(Account.class),1);
1.2 DbUtils基本使用
准备数据库环境
create table account(id int primary key auto_increment,name varchar(100) not null unique,money float(10,2)
)
创建工程导入依赖
<dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.15</version></dependency><!--dbutils--><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
创建实体类
package cn.itcast.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {private Integer id;private String name;private Double money;
}
保存
/*** 保存账户数据* @throws Exception*/@Testpublic void test() throws Exception {//1.创建DataSourceDruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///heima31");//2.创建QueryRunner对象(通过构造参数,传入datasource)QueryRunner queryRunner = new QueryRunner(dataSource);//3.调用QueryRunner方法完成数据库操作(update, query)queryRunner.update("insert into account(name , money) values (?,?)","小张",2);}
根据名称查询
/*** 根据名称查询账户*/
@Test
public void testQueryByName() throws Exception {//1、创建DataSourceDruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///heima31");//2、创建QueryRunner对象 (通过构造参数,传入datasource)QueryRunner queryRunner = new QueryRunner(dataSource);//3、调用QueryRunner方法完成数据库操作(update,query)/*** 结果集处理器:* BeanHandler :处理返回值仅有一条数据,将查询结果封装为唯一的java对象* BeanListHandler:处理返回值有多条记录,将查询结果封装为List<>对象* 语法:* new BeanHandler<封装的对象类型>(返回对象.class)*/Account account = queryRunner.query("select * from account where name=?",new BeanHandler<Account>(Account.class), "小张");//sql语句,结果集处理器,sql语句的参数(不是必须)System.out.println(account);
}
查询所有
/*** 查询列表记录*/
@Test
public void testQueryAll() throws Exception {//1、创建DataSourceDruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///heima31");//2、创建QueryRunner对象 (通过构造参数,传入datasource)QueryRunner queryRunner = new QueryRunner(dataSource);//3、调用QueryRunner方法完成数据库操作(update,query)/*** 结果集处理器:* BeanHandler :处理返回值仅有一条数据,将查询结果封装为唯一的java对象* BeanListHandler:处理返回值有多条记录,将查询结果封装为List<>对象* 语法:* new BeanHandler<封装的对象类型>(返回对象.class)*/List<Account> list = queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));for (Account account : list) {System.out.println(account);}
}
第二章 基于XML配置数据库操作(重点)
2.1 环境搭建
创建工程导入坐标
<dependencies><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.15</version></dependency><!--dbutils--><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency><!--spring-junit--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.6.RELEASE</version></dependency></dependencies>
创建实体
package cn.itcast.domain;public class Account {private Integer id;/*** 账户名称*/private String name;/*** 账户金额*/private Float 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 Float getMoney() {return money;}public void setMoney(Float money) {this.money = money;}
}
创建dao接口
package cn.itcast.dao;import cn.itcast.domain.Account;import java.util.List;public interface AccountDao {/*** 1、保存账户* @param account*/public void saveAccount(Account account);/*** 2、更新账户* @param account*/public void updateAccount(Account account);/*** 3、根据名称查询账户*/public Account findByName(String name);/*** 4、查询所有账户* @return*/public List<Account> findA1l();/*** 5、根据i d删除账户* @param id*/public void de1eteById(Integer id) ;}
创建dao接口实现类
package cn.itcast.dao.impl;import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;import java.sql.SQLException;
import java.util.List;/*** 在dao层中,使用QueryRunner完成数据库操作*/
public class AccountDaoImpl implements AccountDao {/*** 依赖属性*/private QueryRunner queryRunner;public void setQueryRunner(QueryRunner queryRunner) {this.queryRunner = queryRunner;}/*** 保存* @param account*/public void saveAccount(Account account) {try {queryRunner.update("insert into account(name,money) values(?,?)", account.getName(),account.getMoney());} catch (SQLException e) {e.printStackTrace();}}/*** 根据用户名称更新金额* @param account*/public void updateAccount(Account account) {try {queryRunner.update("update account set money=? where name=?", account.getMoney(),account.getName());} catch (SQLException e) {e.printStackTrace();}}/*** 根据名称查询* @param name* @return*/public Account findByName(String name) {try {return queryRunner.query("select * from account where name=?", new BeanHandler<Account>(Account.class),name);} catch (SQLException e) {e.printStackTrace();}return null;}/*** 查询所有* @return*/public List<Account> findAll() {try {return queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));} catch (SQLException e) {e.printStackTrace();}return null;}/*** 删除* @param id*/public void deleteById(Integer id) {try {queryRunner.update("delete from account where id=?",id);} catch (SQLException e) {e.printStackTrace();}}
}
创建service接口
package cn.itcast.service;import cn.itcast.domain.Account;import java.util.List;public interface AccountService {/*** 1、保存账户* @param account*/public void saveAccount (Account account);/*** 2、更新账户* @param account*/public void updateAccount(Account account) ;/*** 3、根据名称查询账户* @param name* @return*/public Account findByName (String name) ;/*** 4、查询所有账户* @return*/public List<Account> findAll();/*** 5、根据id删除账户* @param id*/public void deleteById(Integer id);}
创建service接口实现类
package cn.itcast.service.impl;import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;import java.util.List;public class AccountServiceImpl implements AccountService {/*** 依赖属性(依赖注入)*/private AccountDao accountDao;/*** 用于property的set注入* @param accountDao*/public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/*** 保存* @param account*/public void saveAccount(Account account) {accountDao.saveAccount(account);}/*** 更新* @param account*/public void updateAccount(Account account) {accountDao.updateAccount(account);}/*** 根据名称查询* @param name* @return*/public Account findByName(String name) {return accountDao.findByName(name);}/*** 查询全部* @return*/public List<Account> findAll() {return accountDao.findAll();}/*** 根据id删除* @param id*/public void deleteById(Integer id) {accountDao.deleteById(id);}
}
2.2 Spring配置(重点)
2.3 测试
/*** 调用Userservice完成数据库CRUD操作* @throws Exception*/@Testpublic void testOne() throws Exception {//1、创建spring容器ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//2、从容器中获取对象AccountService accountService = (AccountService)ac.getBean( "accountService");List<Account> list = accountService.findAll();for (Account account : list) {System.out.println(account);}}
第三章 Spring中的常见注解
3.1 环境搭建
(1)案例拷贝
(2)xml配置文件删除bean配置
使用注解替换bean标签的配置,我们将applicationContext.xml中的bean标签删除
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>
(3)xml中加入包扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--包扫描:扫描指定包下的所有java类,扫描类中的Spring注解,帮助我们自动的创建对象存入容器--><!--扫描路径:当前包及子包--><context:component-scan base-package="cn.itcast"></context:component-scan></beans>
包扫描:扫描指定包下的所有java类,扫描类中的Spring注解,帮助我们自动的创建对象存入容器
3.2 对象创建的注解
语法规则
创建对象交给spring容器管理,语义话的形式代码分层<bean id="userDao" class="cn.itcast.dao.impl. userDaoImpl"></bean>@Component(组件):@Controller:在web层使用@Service:在service层@Repository:在dao层使用当spring容器启动时,根据包扫描配置自动的扫描到@Component注解,反射创建注解标注的对象,存入容器默认存入容器的id(唯一标识)=当前类名首字母小写(userDaoImpl)value属性:自定义容器中对象的唯一标识
java代码如下
@Repository(value="userDao")
public class UserDaoImpl implements UserDao{public void save(){System.out.println("调用dao11111完成保存");}
}
3.3 生命周期的注解
语法规则
对象的生命周期的注解@Scope:配置到类上,生命对象的生命周期singleton:单例(默认)prototype:多例@PostConstruct:相当于xml中的init-method对象初始化方法:配置到对象的方法上@PreDestory:相当于xml中的destory-method对象的销毁方法:配置到对象的方法上
java代码如下
@Repository(value = "userDao1")
@Scope("prototype")
public class UserDaoImpl implements UserDao {public void save() {System.out.println("调用dao11111保存用户");}/*** 初始化方法*/@PostConstructpublic void initMethod(){System.out.println("对象执行了init方法");}/*** 销毁方法*/@PreDestroypublic void destoryMethod(){System.out.println("对象执行了destory方法");}
}
3.4 依赖注入的注解
@Autowired
@Reource
@value
3.5 注解总结
使用注解编程之前,必须执行包扫描
第四章 注解结合XML完成案例(重点)
注解结合XML:
自定义的java对象通过注解配置(service, dao)—service,repository,autowired
第三方对象通过XML配置(QueryRunner,DataSource)
4.1 环境搭建
4.2 持久层代码
4.3 业务层代码
4.4 XML配置
第五章 第五章 Spring案例之纯注解版(了解)
注解版:使用注解配置第三方对象交给spring容器管理
Bean
5.1 开发步骤
环境搭建
删除XML配置文件
新建配置类
package cn.itcast.config;import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;/*** 通过SpringConfig替换之前的xml配置项* 1、声明配置类,在类上配置一个注解* @Configuration* 2、开启包扫描* @ComponentScan(basePackages = "cn.itcast")* basePackages: 指定需要扫描的包* 3、将第三方jar包对象,创建并交给容器管理* @Bean*/
@Configuration
@ComponentScan(basePackages = "cn.itcast")
public class SpringConfig {@Beanpublic DataSource getDataSource() {DruidDataSource dataSource = new DruidDataSource();//四元素dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///heima31");return dataSource;}//创建QueryRunner@Beanpublic QueryRunner getQueryRunner(DataSource dataSource) {//dataSource : 从容器中获取的对象QueryRunner queryRunner = new QueryRunner(dataSource);return queryRunner;}}
新的注解
@Configuration—声明配置
@ComponentScan—指定包扫描
@Bean—创建第三方jar包中的对象
测试
5.2 案例优化
从外部加载配置文件
(1)准备properties文件
jdbc.username=root
jdbc.password=root
jdbc.driver=com.mysql.jdbc. Driver
jdbc.url=jdbc :mysql: ///heima31
(2)通过注解将此文件交给spring容器管理
@Propertysource(value="jdbc.properties ")
(3)通过@Value,获取文件中的属性,赋值到变量中
package cn.itcast.config;import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;import javax.sql.DataSource;/*** 通过SpringConfig替换之前的xml配置项* 1、声明配置类,在类上配置一个注解* @Configuration* 2、开启包扫描* @ComponentScan(basePackages = "cn.itcast")* basePackages: 指定需要扫描的包* 3、将第三方jar包对象,创建并交给容器管理* @Bean* 4、将properties配置文件,交给spring容器管理* @PropertySource("jdbc.properties")* value : properties路径*/
@Configuration
@ComponentScan(basePackages = "cn.itcast")
@PropertySource(value="jdbc.properties")
public class SpringConfig {@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;/*** @Bean : 创建第三方jar包对象,交给容器管理* 语法:* 1、@Bean注解需要配置到方法上* 2、方法需要返回值* 3、在Spring容器启动的时候,自动的扫描所有配置了@Bean的方法* 4、自动执行被@Bean扫描的方法,将返回值存入Spring容器* 5、如果方法需要参数,Spring会从容器中根据类型获取对象,再调用* 在@Bean标注的方法中,可以配置依赖的属性参数* spring会从容器中获取到依赖的对象,自动调用方法*/@Beanpublic DataSource getDataSource() {DruidDataSource dataSource = new DruidDataSource();//四元素dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setDriverClassName(driver);dataSource.setUrl(url);return dataSource;}//创建QueryRunner@Beanpublic QueryRunner getQueryRunner(DataSource dataSource) {//dataSource : 从容器中获取的对象QueryRunner queryRunner = new QueryRunner(dataSource);return queryRunner;}
}
模块化
配置类臃肿,Spring支持多配置类(配置类的模块)
拆分配置类SpringConfig,添加一个新的子配置类JdbcConfig
package cn.itcast.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;public class JdbcConfig {@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;/*** @Bean : 创建第三方jar包对象,交给容器管理* 语法:* 1、@Bean注解需要配置到方法上* 2、方法需要返回值* 3、在Spring容器启动的时候,自动的扫描所有配置了@Bean的方法* 4、自动执行被@Bean扫描的方法,将返回值存入Spring容器* 5、如果方法需要参数,Spring会从容器中根据类型获取对象,再调用* 在@Bean标注的方法中,可以配置依赖的属性参数* spring会从容器中获取到依赖的对象,自动调用方法*/@Beanpublic DataSource getDataSource() {DruidDataSource dataSource = new DruidDataSource();//四元素dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setDriverClassName(driver);dataSource.setUrl(url);return dataSource;}
}
在主配置类中,通过Import引入其他的配置类
package cn.itcast.config;import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;import javax.sql.DataSource;@Configuration
@ComponentScan(basePackages = "cn.itcast")
@PropertySource(value="jdbc.properties")
//引入其他的配置类
@Import(value=JdbcConfig.class)
public class SpringConfig {//创建QueryRunner@Beanpublic QueryRunner getQueryRunner(DataSource dataSource) {//dataSource : 从容器中获取的对象QueryRunner queryRunner = new QueryRunner(dataSource);return queryRunner;}
}
5.3 新注解总结
第六章 Spring整合单元测试(会用)
当在单元测试中,点击run的时候,底层工作的其实是一个运行器,默认是ParentRunner.这个运行器是junit提供的,它是不认识Spring的环境.这也就意味着,它无法从spring的容器中获取bean.
如果想要从Spring的容器中获取对象,那就必须先认识Spring环境.这时候,Spring提供了一个运行器,这个运行器就认识Spring环境,也就可以获取对象了
6.1 导入坐标
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency><!--spring-junit 整合单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.6.RELEASE</version>
</dependency>
6.2 配置注解
package cn.itcast.test;import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** 1、指定单元测试环境:指定spring提供的单元测试环境* @RunWith(SpringJUnit4ClassRunner.class)* 2、指定spring的容器配置信息* @ContextConfiguration* locations : 配置文件路径* classes : 配置类*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountJunitTest {@Autowiredprivate AccountService accountService;//测试保存@Testpublic void testInsert() {//3、调用方法保存Account account = new Account();account.setMoney(100.0);account.setName("小李1");accountService.saveAccount(account);}
}
Spring第三天
第一章 转账案例(练习)
介绍:通过spring结合serivce,dao,dbuitils完成转账工程
转入账户:加款
转出账户:扣款
配置:采用流行的xml+注解的方式
效果:张三,向李四转账100元
1.1 代码开发
准备数据环境(略)
创建工程导入坐标
编写domain
编写dao接口
编写dao实现
编写service接口
编写service实现
加入spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启包扫描--><context:component-scan base-package="cn.itcast"></context:component-scan><!--配置queryrunner--><bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"><constructor-arg name="ds" ref="dataSource"></constructor-arg></bean><!--配置datasource--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="root"></property><property name="password" value="123456"></property><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql:///spring"></property></bean>
</beans>
测试
import cn.itcast.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {accountService.transfer("小张","小李",1f);}}
1.2 问题分析
1.3 解决思路
1想办法让同一个业务中的所有sql使用同一个connection
2想办法禁止自动提交,然后手动控制事务的提交和回滚
1.4 传统解决方案
service层代码
package cn.itcast.service.impl;import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;/*** 已经配置好了数据源 datasource,可以通过datasource获取一个连接*/@Autowiredprivate DataSource dataSource;//转账:转出账户名,转入账户名,转账金额public void transfer(String sourceName, String targetName, Double money) throws SQLException {//获取数据库连接Connection connection = dataSource.getConnection();//开启事务connection.setAutoCommit(false);try {//1、根据账户名称,查询两个账户Account sourceAccount = accountDao.findByName(connection,sourceName); //转出Account targetAccount = accountDao.findByName(connection,targetName); //转入//2、对于转出账户,扣款sourceAccount.setMoney(sourceAccount.getMoney() - money);//3、对于转入账户,加款targetAccount.setMoney(targetAccount.getMoney() + money);//4、更新转出账户和转入账户到数据库中accountDao.update(connection,sourceAccount);int i = 1/0;accountDao.update(connection,targetAccount);//提交事务connection.commit();}catch (Exception e) {e.printStackTrace();//回滚事务connection.rollback();}finally {//释放资源connection.close();}}
}
dao层代码
传统的解决方法,随着业务代码的不断增多,参数会越来越多且传递复杂
1.5 Threadlocal
Threadlocal:将数据绑定到当前线程上,同一个线程中。进过的不同方法都可以从Threadlocal获取数据,并且获取的数据是同一个对象
Threadlocal使用方法很简单
大致意思就是ThreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。
ThreadLocal<T> sThreadLocal=new ThreadLocal<T>();
//将数据绑定到当前线程
sThreadLocal.set();
//从当前线程中获取数据
sThreadLocal.get();
第二章 开发事务管理器
2.1 代码开发
复制一个工程
编写事务管理器(copy)
package cn.itcast.utils;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;//事务管理器
@Component
public class TxManager {@Autowiredprivate DataSource dataSource;//准备好本地存储Connection对象的ThreadLocalprivate ThreadLocal<Connection> th = new ThreadLocal<Connection>();//获取Connection对象public Connection getConnection() throws SQLException {Connection connection = th.get();if (connection == null){connection = dataSource.getConnection();th.set(connection);}return connection;}//开启public void begin(){try {getConnection().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}//提交public void commit(){try {getConnection().commit();} catch (SQLException e) {e.printStackTrace();}}//回滚public void rollback(){try {getConnection().rollback();} catch (SQLException e) {e.printStackTrace();}}//关闭public void close(){try {getConnection().close();th.remove();} catch (SQLException e) {e.printStackTrace();}}
}
修改dao
修改service
2.2 问题分析
现在的事务代码和业务代码严重耦合在一起了,我们希望的是这样
在不改动原来业务代码的前提下,给代码添加事务管理功能
即:在不修改源代码的情况下,给代码增强功能
2.3 解决思路
动态代理
第三章 动态代理优化转账
动态代理:在不改变源代码的前提下,对功能进行增强(对指定类中方法进行业务增强)
- java代码中只需要重点关注业务逻辑即可
- 增强部分内容,通过动态代理添加
动态代理在目前两种实现方式
- jdk动态代理
- cglib动态代理
3.1 jdk动态代理
复制工程
制作被代理对象
制作增强功能
产生代理对象(JDK)
package cn.itcast.test;import cn.itcast.service.AccountService;
import cn.itcast.utils.TxManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Autowiredprivate TxManager txManager;@Testpublic void testTransfer() {// 产生目标对象(注入)// 编写代理逻辑InvocationHandler invocationHandler = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object obj = null;try {//开启事务txManager.begin();//调用目标对象的方法obj = method.invoke(accountService, args);//提交事务txManager.commit();} catch (Exception e) {e.printStackTrace();//回滚事务txManager.rollback();} finally {txManager.close();}return obj;}};// 创建代理对象AccountService instance = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),accountService.getClass().getInterfaces(),invocationHandler);//让代理对象去工作instance.transfer("B01", "B02", 10.0);}
}
3.2 cglib动态代理(了解)
复制工程
去掉接口相关所有代码
只需要删除AccountService接口即可(注意修改实现类)
使用cglib的方式创建代理对象
package cn.itcast.test;import cn.itcast.service.impl.AccountServiceImpl;
import cn.itcast.utils.TxManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.lang.reflect.Method;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {@Autowiredprivate AccountServiceImpl accountService;@Autowiredprivate TxManager txManager;@Testpublic void testTransfer() {// 产生目标对象(注入)// 编写代理逻辑InvocationHandler invocationHandler = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object obj = null;try {//开启事务txManager.begin();//调用目标对象的方法obj = method.invoke(accountService, args);//提交事务txManager.commit();} catch (Exception e) {e.printStackTrace();//回滚事务txManager.rollback();} finally {txManager.close();}return obj;}};// 使用cglib的方式创建代理对象// 1 创建增强器Enhancer enhancer = new Enhancer();// 2 设置父类enhancer.setSuperclass(AccountServiceImpl.class);// 3 设置代理逻辑enhancer.setCallback(invocationHandler);// 4 产生代理对象AccountServiceImpl instance = (AccountServiceImpl) enhancer.create();//5 让代理对象去工作instance.transfer("B01", "B02", 10.0);}
}
3.3 jdk和cglib两种代理方式的选择(面试)
首先明确在创建代理实现类时, jdk的速度要高于cglib,所以选择的时候:
当被代理类有接口的时候,使用jdk动态代理,因为它的效率高
当被代理类没有接口的时候,使用cglib动态代理,因为没办法
3.4 总结
当核心业务(转账)和增强业务(事务)同时出现时,我们可以在开发时对他们分别开发,运行时再组装在一起(使用动态代理的方式)。这样做的好处是:
1.逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
2.代码复用性高:增强代码不用重复书写
这就是一种AOP的思想。
我的总结:
开发阶段分离开发,运行阶段组装运行
第四章 AOP概述
4.1 概念
AOP(面向切面编程)是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强.
Spring AOP是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理.
Spring会根据被代理的类是否有接口自动选择代理方式:
如果有接口,就采用jdk动态代理(当然,也可以强制使用cglib)
没有接口就采用cglib的方式
4.2 术语(难点)
目标对象–target:被代理的对象
连接点–jointPoint:被代理对象中的所有方法
切入点–pointCut:被增强的方法
增强(通知)
advice: 一个具体的增强功能
通知分为5种类型:
前置通知,后置通知,异常通知,最终通知,环绕通知
代理对象–proxy :生成的动态代理对象
切面
aspect
切面是一种描述,描述了一件事:一个什么样的功能添加到了哪个切入点的什么位置上
切面=切点+增强
第五章 SpringAOP的入门案例(重点)
在AccountDaolmpl类中的方法上打印日志
5.1 思路分析
目标对象(target) ----AccountDaolmpl类
被增强方法(pointcut) — AccountDaolmpl类中的方法
增强功能(advice) ----打印日志
切面配置(aspect)-切点+增强日志在目标对象中的哪个方法的哪个位置上执行
5.2 代码开发
创建工程,引入坐标
<dependencies><!--spring aop--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency><!--切点表达式的解析坐标--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency><!--test--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
创建AccountDao的接口和实现类
开发增强功能
基于XML配置切面
切面类需要交给Spring容器管理
通过aop:config开启AOP配置
(1)配置切入点(被增强的类和方法)
(2)配置切面
第六章 SpringAOP配置详解
6.1 切入点表达式
6.2 四大通知
四大通知描述的就是增强方法在切点方法的什么位置上执行
前置通知(before):在切点运行之前执行
后置通知(after-returning):在切点正常运行结束之后执行
异常通知(after-throwing):在切点发生异常的时候执行
最终通知(after):在切点的最终执行
try{前置通知(before) :在切点运行之前执行//切点执行,被代理对象方法调用后置通知(after-returning):在切点正常运行结束之后执行}catch(Exception e){异常通知(after-th rowing):在切点发生异常的时候执行}finally{最终通知(after):在切点的最终执行}
方法
配置
由于多个通知类的配置顺序不同,导致不一样样的执行效果!!!
6.3 环绕通知
它是一种特殊的通知,他允许你以编码的形式实现四大通知(和手动定义动态代理类似)
方法
配置
6.4 AOP工作原理
开发阶段分别开发运行阶段组装运行
开发阶段
开发共性功能,制作成增强
开发非共性功能,制作成切点
在配置文件中,声明切点与增强间的关系,即切面
容器启动阶段
Spring读取配置文件中的切面信息,根据切面中的描述,
将增强功能增加在目标对象的切点方法上,动态创建代理对象
最后将经过代理之后对象放入容器中(存入容器的是动态代理对象!!!!!)
Spring第四天
第一章 SpringAOP注解版(重点)
AOP注解版
基于XML结合注解的配置方式(重点)
基于纯注解的配置方式(了解)
1.1 基于XML结合注解的配置
环境准备
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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--基于XML结合注解配置IOC的注解:开启包扫描自定义的java对象,通过注解配置第三方jar包对象,通过XML配置AOP的注解:开启AOP注解的支持在切面类方法上,通过注解的形式配置AOP* 开启IOC的包扫描,开启AOP的自动代理* 在切面类中完成* 声明切面类* 配置AOP通知类型--><!--包扫描--><context:component-scan base-package="cn.itcast"></context:component-scan><!--开启AOP注解的支持,开启自动代理--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
修改dao
修改Logger切面类
package cn.itcast.dao.utils;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** Logger : 切面类,增强类* 基于注解的形式配置AOP* 1、声明切面类 (Spring知道此类中具有增强通知方法)* @Aspect* 2、在增强通知方法上,通过注解配置通知类型* @before:前置通知* @afterReturning:后置通知* @afterThrowing:异常通知* @after:最终通知* @around:环绕通知*/
@Component
@Aspect
public class Logger {/*** 前置通知:在被代理对象方法之前,增强日志*/@Before(value="execution(* cn.itcast.dao.impl.*.*(..))")public void before() {System.out.println("执行前置通知:before方法");}/*** 后置通知:被代理对象方法正常执行,获取返回值之后*/@AfterReturning(value="execution(* cn.itcast.dao.impl.*.*(..))")public void afterReturining() {System.out.println("执行后置通知:afterReturining");}/*** 异常通知:调用过程中抛出异常时执行*/@AfterThrowing(value="execution(* cn.itcast.dao.impl.*.*(..))")public void afterThrowing() {System.out.println("执行异常通知:afterThrowing");}/*** 最终通知:在最终代码块中需要执行的逻辑*/@After(value="execution(* cn.itcast.dao.impl.*.*(..))")public void after() {System.out.println("执行最终通知:after");}//环绕通知public Object around(ProceedingJoinPoint pjp) throws Throwable {Object obj = null;try {System.out.println("执行前置通知");obj = pjp.proceed(); //执行被代理对象的方法System.out.println("执行后置通知");}catch (Exception e) {System.out.println("执行异常通知");}finally {System.out.println("执行最终通知");}return obj;}
}
四大通知类型的问题
通过注解配置四大通知类型,存在小BUG。执行顺序和XML配置不一致。在正常企业开发中不用
环绕通知(重点)
1.2 基于纯注解的配置(了解)
环境准备
编写配置类
第二章 SpringAOP实现事务管理
SpringAOP实现事务管理:基于AOP完成转账案例,通过AOP配置事务增强
2.1 基于XML的AOP事务配置
四个通知类型配置
(1)工程准备,导入依赖
<dependencies><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.15</version></dependency><!--dbutils--><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--spring ioc依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency><!--spring-junit 整合单元测试--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.6.RELEASE</version></dependency><!--切点表达式的解析坐标--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency></dependencies>
(2)修改AccountServiceImpl
(3)修改AccountDaoImpl
(4)配置切面类
(5)配置AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--对象创建IOC--><bean id="accountService" class="cn.itcast.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean><bean id="accountDao" class="cn.itcast.dao.impl.AccountDaoImpl"><property name="queryRunner" ref="queryRunner"></property><property name="txManager" ref="txManager"></property></bean><bean id="txManager" class="cn.itcast.utils.TxManager"><property name="dataSource" ref="dataSource"></property></bean><bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"><constructor-arg name="ds" ref="dataSource"></constructor-arg></bean><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="root"></property><property name="password" value="root"></property><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql:///heima31"></property></bean><!--AOP配置--><aop:config><!--切入点表达式 : 查找需要增强的方法--><aop:pointcut id="pt" expression="execution(* cn.itcast.service.impl.*.*(..))"/><!--配置切面--><aop:aspect ref="txManager"><!--前置通知--><aop:before method="begin" pointcut-ref="pt"></aop:before><!--后置通知--><aop:after-returning method="commit" pointcut-ref="pt"></aop:after-returning><!--异常通知--><aop:after-throwing method="rollback" pointcut-ref="pt"></aop:after-throwing><!--最终通知--><aop:after method="close" pointcut-ref="pt"></aop:after></aop:aspect></aop:config>
</beans>
借助环绕通知配置(重点)
(1)切面类添加环绕通知方法
(2)XML中配置环绕通知
2.2 基于XML结合注解的AOP事务配置
搭建环境
(1)Dao层代码
(2)Service层代码
配置
(1)XML配置文件
(2)切面类配置
第三章 JdbcTemplate(会用)
3.1 JdbcTemplate介绍
方法介绍
package cn.itcast.test;import cn.itcast.domain.Account;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;public class JdbcTemplateTest {/*** 练习JdbcTemplate的基本使用*/public static void main(String[] args) {//1、创建DataSourceDruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///heima31");//2、创建JdbcTemplate对象JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);//3、调用方法完成数据库操作//3.1 保存,删除(update)//jdbcTemplate.update("insert into account (name,money) values (?,?)" ,"小王",100f);//3.2 查询数据列表(query)//BeanPropertyRowMapper : 结果集处理器 (查询列表和唯一的时候,都使用同一个)
// List<Account> list = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
// for (Account account : list) {
// System.out.println(account);
// }//3.3 根据id查询唯一的对象( queryForObject)Account account = jdbcTemplate.queryForObject("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), 1);System.out.println(account);}
}
3.2 JdbcTemplate案例
使用dbcTemplate完成一个crud和转账的案例,使用xml结合注解的形式配置
(1)准备数据环境
和之前的数据库一模一样,账户操作
(2)创建工程,导入坐标
<dependencies><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.15</version></dependency><!--spring-jdbc--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.6.RELEASE</version></dependency><!--spring-context--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--spring-test--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.6.RELEASE</version></dependency><!--添加切点表达式--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency></dependencies>
(3)创建实体类
(4)创建dao接口
(5)创建dao实现
(6)创建service接口
(7)创建service实现
(8)加入Spring的配置文件
(9)测试
package cn.itcast.test;import cn.itcast.domain.Account;
import cn.itcast.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testFindAll() {List<Account> list = accountService.findAll();for (Account account : list) {System.out.println(account);}}@Testpublic void testTransfer() throws Exception {accountService.transfer("小张", "小李",1f);}}
第四章 Spring中的事务管理(了解)
Spring中的事务管理:根据SpringAOP拓展出来的事务控制功能
4.1 事务管理方式
Spring支持两种事务管理方式:编程式事务和声明式事务
编程式事务就是将业务代码和事务代码放在一起书写,它的耦合性太高,开发中不使用
声明式事务通过一段配置让程序组装运行,最后达到事务控制的目的
声明式事务就是通过AOP原理实现的
4.2 Spring事务管理相关的API
PlateformTransactionManager
TransactionDefinition
TransactionDefinition这个API是用来做事务定义的
面向配置(事务隔离级别,传播行为,是否只读事务,超时时间)
隔离级别(*)
传播行为(理解)
是否只读事务
超时时长
配置项总结
第五章 声明式事务(重点)
5.1 思路
目标对象[业务类]service
增强[事务管理器]DataSourceTransactionManager
事务配置:[事务隔离级别事务传播行为事务是否只读事务超时时长]
5.2 xml版(重点)
5.3 注解结合XML(重点)
(1)复制工程
(2)删除xml中的tx:advice和aop:config
(3)添加一个事务注解驱动
(4)在方法上添加声明式事务的注解
5.4 纯注解版(了解)
package cn.itcast.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;/*** 1、声明配置类* 2、开启包扫描* 3、开始事务注解管理* @EnableTransactionManagement* 4、第三方对象*/
@Configuration
@ComponentScan(basePackages = "cn.itcast")
@EnableTransactionManagement
public class SpringConfig {/*** @Bean:配置到方法上,表明此方法的返回值交给Spring容器管理* 在Spring容器启动时,自动的扫描所有@Bean的方法* 自动执行并获取返回值,存入Spring容器* 在方法上配置参数,Spring会自动的从容器中获取对象,自动调用方法*//*** DataSource*/@Beanpublic DataSource getDataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("root");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///heima31");return dataSource;}/*** JdbcTemplate*/@Beanpublic JdbcTemplate getJdbcTemplate(DataSource dataSource) {JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}/*** 事务管理器*/@Beanpublic PlatformTransactionManager getTransactionManager(DataSource dataSource) {DataSourceTransactionManager tm = new DataSourceTransactionManager();tm.setDataSource(dataSource);return tm;}
}
第六章 事务补充
6.1 准备
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`age` smallint(3) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;CREATE TABLE `role` (`id` int(11) NOT NULL AUTO_INCREMENT,`role_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
6.2 测试
6.3 try catch影响
6.4 事务嵌套影响
上面的代码可以改造成如下代码:
@Transactional
public void out() throws Exception{Role role=new Role();Role.setRoleName(“roleName:”+new Random().nextInt(100));roleService.save(role);int age=random.nextInt(100);User user=new User().setAge(age).setName(“name:”+age);userService.save(user);throw new Exception();
}
这个是inner的方法没有抛出异常,out的代码抛出了异常,所以代码可以改造成如上样子。所以结合结论一,事务回滚都失败。