spring learning code
This is my learning note about how Spring core features like IoC, AOP works, with Code Examples .
Spring Code Learning Notes with Code Examples
Table of Contents
- Spring V1.0 DispatcherServlet
- from Servlet to ApplicatoinContext
- IoC
- Screenshots
- Setup
- Usage
- Project Status
- Room for Improvement
- Acknowledgements
- Contact
spring introduce
download

spring

spring

spring
inport jar
-
Beans
-
Core
-
Context
-
Expression

spring

spring jar
create class and mothod
public class User {
public void add() {
System.out.println("add.............");
}
}
create an object with Spring properties
1. xml file in Spring properties

spring jar
test
1. load spring properties file
2. get object from properties (bean id)

spring jar
Spring IOC
1. spring ioc basic concepts
-
xml encoding, factory-based design pattern, reflect
-
original: tightly coupled
class UserService{
execute();
UserDao dao = new UserDao();
dao.add();
}
class UserDao{
add() {
...
}
}
- factory-based design pattern
class UserService{
execute();
UserDao dao = UserFactory.getDao();
dao.add();
}
class UserDao{
add() {
...
}
}
class UserFactory{
public static UserDao getDao() {
return new UserDao();
}
}
- IOC
<bean id="user" class="com.learn.User"></bean>
class UserFactory{
public static UserDao getDao() {
// 1. xml encoding
String classValue = class属性;
// 2. use reflect create an object
Class clazz = Class.for(Name);
return (UserDao)clazz.newInstance();
}
}
2. IOC interface(BeanFactory)
-
BeanFactory: for String, not for developer
- won’t creates object when loading properties file
-
ApplicationContext
- creating objects when loading properties file
3. IOC opertation Bean management(xml)
- creating objects in xml(bean)
-
bean tag properties
- id, class…
- create objects by No-argument constructor
-
set properties in xml
- DI: set properties
-
DI: set method
- 1). create class, and set method
public void setBname(String bname) {
this.bname = bname;
}
- 2). spring xml file, set properties
- DI: use annotation
- 1). create class, and set method, and argument constructor
public class Orders{
private String oname;
private String address;
public Order(String oname, String address) {
this.oname = oname;
this.address = address;
}
}
- 2). spring xml file, use argument constructor
4. Bean management(xml) external beans
- 2 classes: service and dao
- call methods in dao from service class
// service and dao objects: 在service层创建UserDAO类,生成set方法
<bean id="userService" class="com.learn.service.UserService">
// inject userDao object
// name attribute: class attribute name private UserDao userDao;
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.learn.dao.UserDaoImpl"></bean>
5. Bean management(xml) inner beans and Cascade assignment
- 1 对多关系: 部门和员工
- 在实体类之间表示 1 对多关系
department
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
employ
public class Emp {
private String ename;
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
}
xml
// inner bean
<bean id="emp" class="com.learn.bean.Emp">
// inject userDao object
<property name="ename" value="lucy"></property>
// method 1
// <property name="dept" ref="dame"></property>
// method 2
<property name="dept">
<bean id="dept" class="com.learn.bean.Dept">
<property name="dname" value="IT"></property>
</property>
</bean>
6. Bean management(xml) array, list, map
6.1 class and set method
public class Stu {
// array
private String[] courses;
// list
private List<String> list;
// map
private Map<String, String> maps;
// set
private Set<String> sets;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList( List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> maps) {
this.maps = maps;
}
public void setSet(Set<String> sets) {
this.sets = sets;
}
}
6.2 xml
<bean id="stu" class="com.learn.conllectiontype.Stu">
// array
<property name="courses">
<array>
<value>java class </value>
<value>sql class </value>
</array>
</property>
// list
<property name="list">
<list>
<value>AAA </value>
<value>BBB class </value>
</list>
</property>
// MAP
<property name="maps">
<map>
<entry>key="JAVA" value="jva"</entry>
<entry>key="PHP" value="php"</entry>
</map>
</property>
// set
<property name="sets">
<set>
<value>MySQL </value>
<value>Redis </value>
</set>
</property>
</bean>
7. Bean managementset object type in collection
7.1 class and set method
public class Course {
private String cname;
public void setCname(String cname) {
this.cname = cname;
}
public String toString() {
}
}
public class Stu {
// array
private String[] courses;
// list
private List<String> list;
// map
private Map<String, String> maps;
// set
private Set<String> sets;
// ===============coursed students take===============
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
// ===============coursed students take===============
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList( List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> maps) {
this.maps = maps;
}
public void setSet(Set<String> sets) {
this.sets = sets;
}
}
7.2 xml
<bean id="stu" class="com.learn.conllectiontype.Stu">
// array
<property name="courses">
<array>
<value>java class </value>
<value>sql class </value>
</array>
</property>
// list
<property name="list">
<list>
<value>AAA </value>
<value>BBB class </value>
</list>
</property>
// MAP
<property name="maps">
<map>
<entry>key="JAVA" value="jva"</entry>
<entry>key="PHP" value="php"</entry>
</map>
</property>
// set
<property name="sets">
<set>
<value>MySQL </value>
<value>Redis </value>
</set>
</property>
</bean>
// ===============inject list collection type, value is object===============
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
// ===============create multiple course objects===============
<bean id="course1" class="com.learn.collectiontype.Course">
<property name="cname" value="Spring5"></property>
</bean>
<bean id="course2" class="com.learn.collectiontype.Course">
<property name="cname" value="MyBatis"></property>
</bean>
8. extract injection in collection
8.1 Book class
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
public String toString() {
}
public void test() {
System.out.println(list);
}
}
8.2 in xml import name space util
<bean xmlns=""
xmlns:p=""
// copy xmlns:p
xmlns:util="http://www.springframework.org/schema/util"
// change xsi:schemaLocation beans to util
// xsi:schemaLocation="http://www.springframework.org/schema/beans" "http://www.springframework.org/schema/beans/spring-beans.xsd"
xsi:schemaLocation="http://www.springframework.org/schema/util" "http://www.springframework.org/schema/util/spring-util.xsd">
8.3 util tag
// 1. list=====================
<util:list id="bookList">
<value>AAA </value>
<value>BBB</value>
<value>CCC</value>
</util:list>
8.4 test
@Test
public void testCollection() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Book book = context.getBean("book", Book.class);
book.test();
}
9. IOC manage Bean (factory beans)
9.1 ordinary beans 定义 bean 类型就是返回类型
9.2 factory beans 配置文件定义 bean 类型和返回类型不一样
-
9.2.1 创建类,作为工厂 bean, 实现接口 FactoryBean
-
9.2.2 实现接口里面的方法,在方法中定义返回的 bean 类型

spring factorybean

spring factorybean
10. Spring Bean Scopes
10.1 single or instances will be created 单实例多实例 10.2 default model is single instance 默认 spring bean 是单实例对象 10.3 scope is used to set single or multiple: singleton or protype 10.4 singleton vs protype
- singleton: create instance when load spring xml file
- prototype: create instances when getBean method
11. Spring Bean Lifecycle
11.1 create instance by constructor(no-argument constructor)

Spring Bean Lifecycle

Spring Bean Lifecycle
11.2 set bean property and ref(set method)

Spring Bean Lifecycle
11.3 call the init() method(init-method in the xml based configuration file)

Spring Bean Lifecycle

Spring Bean Lifecycle
11.4 use bean

Spring Bean Lifecycle
11.5 destroy Spring Bean (destroy-method in the xml based configuration file)

Spring Bean Lifecycle

Spring Bean Lifecycle

Spring Bean Lifecycle
12.bean post processor (7 steps of life cycle)
12.1 create instance by constructor(no-argument constructor)
- create class, implement interface BeanPostProcessor

Spring Bean Lifecycle
12.2 set bean property and ref(set method)

Spring Bean Lifecycle
12.3 pass bean instance to post processor
12.4 call the init() method(init-method in the xml based configuration file)
12.5 pass bean instance to post processor
12.6 use bean
12.7 destroy Spring Bean (destroy-method in the xml based configuration file)

Spring Bean Lifecycle
13.ioc 管理 bean (xml autowire)
13.1 autowire: byName: 注入值 bean 的 id 值和类里面属性名称要一样
13.2 autowire: byName, byType
14.ioc 管理 bean (外部属性)
14.1 直接配置数据库信息(not recommend)

Spring ioc
14.2 引入外部属性文件配置数据库连接池
create properties file

Spring ioc
import context name space

Spring ioc

Spring ioc
spring IOC annotation
- 1. @Compoment
- 2. @Service
- 3. @Controller
- 4. @Repository
dependency
- spring-aop
start component scan

annotation
<context:component-scan base-package="com.learn"></context:component-scan>
<context:component-scan base-package="com.learn" use-default-filter="false">
<context:include-filter type="anotation" expreesion="org.springframework.steretype.Controller">
</context:component-scan>
set properties (annotation)
1.1 @AotuWired: 根据属性类型进行自动装配
1.2 @Qualifier:根据属性名称进行注入
1.3 @Resource: 可以根据类型注入,可以根据名称注入
1.4 @Value: 注入普通类型属性
steps (annotation)
1.1 dao and service annotation
- @Resource (UserDaoImpl)
- @Service (UserService)

annotation
Field injection is not recommended(不再推荐使用字段注入)但是国内的使用场景有时候比国外复杂很多,实际情况,就一个业务单元,国内的公司几轮需求加上来,注入的服务就会牵扯到十几个,用构造器注入,奇丑无比;用传统的分层结构,光数据库 dao 的注入,都是好几个
1.2 dao and service annotation
- @Resource(name = “userDaoImpl1”)
- @AutoWired + @Qualifier(value = “userDaoImpl1”)

annotation
完全注解开发
2.1 comfig class

annotation
2.2 test

annotation
spring AOP
spring AOP
1.0 面向切面 1.1 登录功能
AOP 底层原理:底层使用动态代理
2.0 有接口,使用 JDK 动态代理
interface UserDao {
public void login();
}
class UserDao implements UserDao{
public void login() {
// 登录实现过程
}
}
-
JDK 动态代理
-
- 创建 UserDao 接口实现类代理对象
-
-
2.1 没有接口,使用 CGLIB 动态代理
class User {
public void add() {
。。。。
}
}
class Person extends User{
public void add() {
super.add();
}
}
-
CGLIB 动态代理
-
- 创建当前子类代理对象
-
-
2.3 AOP JDK 使用 Proxy 代理对象
-
2.4 使用 newProxyInstance 方法代理对象
- 3 个参数
- ClassLoader 类加载器
- 类<?>[] interface 增加方法所在的类,这个类实现的接口,支持多个接口
- InvocationHandler 实现这个接口 InvocationHandler, 创建代理对象,写增加的方法
-
2.5 使用 JDK 动态代理代码
- 创建接口, 定义方法
public interface Userdao {
int add(int a, int b);
String update(String id);
}
- 创建接口实现类, 实现方法
public class UserDaoImpl implements Userdao{
@Override
public int add(int a, int b) {
System.out.println("add方法之前执行le");
return a + b;
}
@Override
public String update(String id) {
System.out.println("update方法之前执行le");
return id;
}
}
public class UserDaoImpl implements Userdao{
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String update(String id) {
return id;
}
}
- 使用 Proxy 类创建接口代理对象
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
}
or
- 创建代理对象代理
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
...
}
}
class UserDaoProxy implements InvocationHandler {
// 1. 把创建的是谁的代理对象,把谁传递过来
// 有参数构造传递
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
// 增加的逻辑
// 增加的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法之前
System.out.println("方法之前执行.............." + method.getName() + " : 传递的参数" + Arrays.toString(args));
Object res = method.invoke(obj, args);
System.out.println("方法之后执行..........." + obj);
return res;
}
}
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System.out.println(result);
}
}
3.0 AOP 操作
-
3.1 Spring 框架一般基于 AspectJ 实现 AOP 操作
-
3.2 AOP 相关依赖
-
3.3 切入点表达式
execution([权限修饰符][返回类型][类全路径]方法名称)
execution([public/private][返回类型/ 忽略][类全路径]方法名称)
ex1: com.learn.dao.BookDao 里面 add 进行增强 execution(*com.learn.dao.BookDao.add(..))
ex2: com.learn.dao.BookDao 里面所有的方法进行增强 execution(com.learn.dao.BookDao.(..))
ex3: 对 com.learn.dao 包里面所有类,类里面所有的方法进行增强 execution(com.learn.dao..*(..))
4.0 AOP AspectJ 注解
- 4.1 创建类, 在类里面定义方法
被增强类
public class User {
public void add() {
System.out.println("add...........");
}
}
- 4.2 创建增强类(编写增强逻辑)
- 在增强类里面,创建方法,让不同方法代表不同通知类型
// 增强类
public class UserProxy {
// 前置通知
public void before() {
System.out.println("before......................");
}
}
- 4.3 进行通知的配置
- 在 spring 配置文件中,开启注解扫描
- 使用注解创建 User 和 UserProxy 对象

Spring aop

Spring aop

Spring aop
- 4.4 在增强类上面添加注解 @Aspect
// 增强类
@Component
@Aspect // 生成代理对象
public class UserProxy {
// 前置通知
public void before() {
System.out.println("before......................");
}
}
- 4.5 在 spring 配置文件中开启生成代理对象

Spring aop
-
配置不同类型的通知
- 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
// 增强类
@Component
@Aspect // 生成代理对象
public class UserProxy {
// 前置通知
// @Before 注解表示作为配置通知
@Before("execution(*com.learn.aop.User.add(..)")
public void before() {
System.out.println("before......................");
}
}

Spring aop

Spring aop

Spring aop
- 4.6 公共切入点抽取

Spring aop
- 4.7 有多个增强类多同一个方法进行增强, 设置增强类优先
- 在增强类上面添加注解 @Order(数字类型值), 小优先级越高

Spring aop

Spring aop
5.0 AOP AspectJ 配置文件
-
5.1 两个类,增强类和被增强类, 创建方法
-
5.2 在 spring 配置文件中创建两个类对象
<bean id="book" class="com.learn.aopxml.Book"></bean>
<bean id="bookProxy" class="com.learn.aopxml.BookProxy"></bean>
- 5.3 在 spring 配置文件中配置切入点

Spring aop
spring data access: JDBC
JdbcTemplate
1.0 dependency jar
-
mysql-connector-java (Java 连接 MySQL 需要驱动包,否则 JDBC 无法访问数据库)
-
druid (在 JDBC 访问数据库的时候是通过连接来完成的,但是连接的打开及关闭非常耗时,这时就是数据库连接池的作用了,他来管理这个数据库连接,不会用完立即关闭,可以缓存起来,供下次使用,为 JDBC 连接数据库提供锦上添花的作用,Druid 就是一个数据库连接池,用来管理数据库连接池的)
-
spring-jdbc (Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接)
-
spring-tx:为 JDBC、Hibernate、JDO、JPA 等提供的一致的声明式和编程式事务管理
-
spring-orm (ORM 的全称是 Object Relational Mapping,即对象关系映射。它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的操作转化为对这些对象的操作。因此它的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作)
2.0 在 spring 配置文件配置数据库连接池
3.0 配置 JdbcTemplate 对象, 注入 DataSource
<!--JdbcTemplate 对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--set方法注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
4.0 创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象
- 4.1 开启组件扫描
<!--4.1 开启组件扫描-->
<context:component-scan base-package="com.learn"/>
- 4.2 Service 类
@Service
public class BookService {
// 注入dao
@Autowired
private BookDao bookDao ;
}
- 4.3 Dao 类
@Repository
public class BookDaoImpl implements BookDao{
// 注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
JdbcTemplate 操作数据库,添加功能
1.0 对应数据库实体类
2.0 编写 service 和 dao
@Repository
public class BookDaoImpl implements BookDao{
// 注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
// 添加的方法
@Override
public void add(Book book) {
// 1. 创建 sql 语句
String sql = "insert into t_book values(?, ?, ?)";
// 2. 调用方法实现
int update = jdbcTemplate.update(sql, book.getUserId(), book.getUsername(), book.getUstatus());
System.out.println(update);
}
// 删除的方法
@Override
public void del(String id) {
// 1. 创建 sql 语句
String sql = "delete from t_book where user_id=?";
// 2. 调用方法实现
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
// 更新的方法
@Override
public void update(Book book) {
// 1. 创建 sql 语句
String sql = "update t_book set username=?, ustatus=? where user_id=?";
// 2. 调用方法实现
Object[] args = {book.getUserId(), book.getUsername(), book.getUstatus()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
}
@Test
public void testAdd() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
Book book = new Book();
book.setUserId("2");
book.setUsername("bb");
book.setUstatus("b");
bookService.addBook(book);
}
JdbcTemplate 操作数据库,查询
查询表有多少条记录
- SELECT COUNT(*) FROM t_book
查询返回某个值
- 1. 查询表有多少条记录, 返回某个值
- 2. 使用 JdbcTemplate 查询表有多少条记录, 返回某个值
- queryForObject(String sql, Class
requireType) - 第一个参数 sql 语句
- 第二个参数, 返回类性 Class(Integer.class, String.class)
- queryForObject(String sql, Class
- 3. daoImpl
@Override
public int selectCount() {
String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
查询返回对象
- 1. 场景: 查询图书详情
- 2. 使用 JdbcTemplate 查询返回对象
- queryForObject(String sql, RowMappper
rowMapper, Object… args) - 第一个参数 sql(select * from t_book where user_id=?)语句
- 第二个参数: RowMapper, 是接口, 返回不同类型数据,使用这个接口里面实现类完成数据封装 new BeanPropertyRowMapper
(Book.class) - 第三个参数: sql 语句值(问号里面的值 id)
- queryForObject(String sql, RowMappper
- 3. daoImpl
public Book findBookInfo(String id) {
String sql = "select * from t_book where user_id=?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
查询返回集合
- 1. 场景: 查询图书列表分页
- 2. 使用 JdbcTemplate 查询返回集合
- query(String sql, RowMappper
rowMapper, Object… args) - 第一个参数 sql 语句
- 第二个参数: RowMapper, 是接口, 返回不同类型数据,使用这个接口里面实现类完成数据封装 new BeanPropertyRowMapper
(Book.class) - 第三个参数: 可以省略
- query(String sql, RowMappper
- 3. daoImpl
@Override
public List<Book> findAll() {
String sql = "select * from t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
批量增加操作
- 1. 场景: 批量操作,比如操作表里面多条记录
- 2. 使用 JdbcTemplate 查询返回集合
- batchUpdate(String sql, List<Object[]> batchArgs)
- 第一个参数 sql 语句
- 第二个参数: List 集合
- 3. daoImpl
@Override
public void batchAddBook(List<Object[]> batchArgs) {
String sql = "insert into t_book values(?, ?, ?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(ints);
System.out.println(Arrays.toString(ints));
}
- 4. test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"6", "hava", "w"};
Object[] o2 = {"7", "gwe", "we"};
Object[] o3 = {"8", "cge", "h"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
bookService.batchAdd(batchArgs);
批量修改操作
- 1. 场景: 批量操作,比如操作表里面多条记录
- 2. 使用 JdbcTemplate 查询返回集合
- batchUpdate(String sql, List<Object[]> batchArgs)
- 第一个参数 sql 语句
- 第二个参数: List 集合
- 3. daoImpl
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
String sql = "update t_book set user_name=?, ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
- 4. test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
List<Object[]> batchArgs = new ArrayList<>();
// Object[] o1 = {"6", "hava", "w"};
// Object[] o2 = {"7", "gwe", "we"};
// Object[] o3 = {"8", "cge", "h"};
// update t_book set user_name=?, ustatus=? where user_id=? id放最后
Object[] o1 = { "hava001", "w","6",};
Object[] o2 = { "hava002", "w","7",};
Object[] o3 = { "hava003", "w","8",};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
bookService.batchAdd(batchArgs);
批量删除操作
- 1. 场景: 批量操作,比如操作表里面多条记录
- 2. 使用 JdbcTemplate 查询返回集合
- batchUpdate(String sql, List<Object[]> batchArgs)
- 第一个参数 sql 语句
- 第二个参数: List 集合
- 3. daoImpl
public void batchDeleteBook(List<Object[]> batchArgs) {
String sql = "delete from t_book where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
- 4. test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = { "6"};
Object[] o2 = { "7"};
batchArgs.add(o1);
batchArgs.add(o2);
bookService.batchDelate(batchArgs);
spring Transaction(ACID)
- Atomicity 原子性(
- Consistency 一致性
- Isolateion 隔离性
- Durability 持久性
转账环境
经典场景: 银行转账
-
lucy 转账 100 给 mary
-
lucy 少 100, mary 多 100
-
Dao: 数据库操作,不写业务
- 多钱方法
- 少钱方法
-
Service: 业务操作
- 创建转账方法
- 调用 dao 两个方法
-
project step:
-
- 创建数据库表, 添加记录

spring Transaction
-
- 创建 Service, 搭建 dao, 完成对象创建和注入关系
- service 注入 dao, 在 dao 注入 jdbcTemplate, 在 JdbcTemplate 注入 DataSource
service
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
bean.xml
<!-- 引入外部属性文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--4.1 开启组件扫描-->
<context:component-scan base-package="com.learn"/>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="driverClassName" value="${prop.driverClass}"/>
</bean>
<!--JdbcTemplate 对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--set方法注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
-
- 在 dao 创建两个方法,多钱少钱, 在 service 创建方法 (转账的方法)
-
- 发生错误, 比如转账到一半断电,产生 lucy 钱少了, mary 没到账
事务解决问题
-
- 开启事务: 业务层(javaEE 三层结构)
-
- Sping 进行事务管理操作
- 2.1 编程式管理: 一般开发中不用

spring Transaction
-
2.2 声明式事务管理 (开发中使用)
- 注解方式 (开发中)
- xml 配置方式
-
3.spring 进行声明式事务管理,底层使用 AOP 原理
-
4.spring 事务管理 API
-
4.1 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实体类
PlatformTransactionManager -> DataSourceTransactionManager

spring Transaction

spring Transaction
-
4.2 开启事务注解

spring Transaction
-
5.service 类上面 (获取 service 类里面方法上面) 添加事务注解
-
5.1 @Transactional 可以加到类或者某个方法上面

spring Transaction
事务操作 (声明式事务管理参数配置)
-
- 事务方法 : 对数据库数据进行变化的操作
-
- service 类上面添加注解 @Transactional
-
- propagation : 事务传播行为:有事务的方法调用没事务的方法,或者反过来 或者两个方法都有事务 就是事务传播行为
@Transactional(propogation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public void add() {
// call update method
update();
}
public void update() {
}
-
- isolation: 事务隔离级别 : 多事务操作直接不会产生影响,不考虑隔离性产生很多问题 解决三个读问题: 脏读,不可重复读,虚读
-
3.1 脏读: 一个未提交事务读取到另一个未提交事务的数据
-
3.2 不可重复读: 一个未提交事务读取到另一个提交事务修改数据
-
3.3 虚读: 一个未提交事务读取到另一个提交事务添加数据
-
3.4 READ UNCOMMITTED(读未提交) :无法解决 3 问题
-
3.5 READ COMMITTED(读已提交) :解决 脏读问题
-
3.6 REPEATABLE READ(可重复度) :解决 脏读 不可重复读 问题
-
3.7 SERIALIZABLE(串行化) :解决脏读 不可重复读 虚读问题
-
- timeout: 超时
-
- readOnly : 是否只读
-
- rollbackFor : 回滚
-
- norollbackFor
事务操作 (完全注解声明式事务管理)
-
- 创建配置类, 使用配置类替代 xml 配置文件

spring Transaction
- 创建配置类, 使用配置类替代 xml 配置文件
spring5 新功能
自带通用的日志封装
-
- 整合 Log4j2
-
- 引入 jar 包
-
2.1 log4j-api
-
2.2 log4j-core
-
2.3 log4j-slf4j-impl
-
2.4 slf4j-api
-
- 创建 log4j2.xml 配置

spring Transaction
- 创建 log4j2.xml 配置
JUnit5
-
- test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean1.xml")
// ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
}
}
-
- 整合 JUnit5
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
// ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
}
}
+ 2. JUnit5 复合注解
```java
@SpringJunitConfig(lacation="classpath:bean1.xml")
// ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
}
}
Webflux

spring Transaction
-
- 响应式编程: Reacter 实现
-
1.1 Reacter 两个核心类, Mono 和 Flux, 实现接口 Publisher。 Flux 对象实现发布者,返回 N 个元素; Mono 实现发布者,返回 0 或者 1 个元素
-
1.2 Mono 和 Flux 都是数据流的发布者,都可以发出三种数据信号: 元素值, 错误信号, 完成信号, 错误信号, 完成信号 都代表终止信号
-
1.3 引入依赖: Reactor-core
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.13</version>
</dependency>
-
1.3 调用 just 或 者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流

spring Transaction
-
1.4 操作符: map 元素映射为新元素; flatMap 元素映射为流
-
- SpringWebflux 执行流程和核心 api SpringWebflux 基于 Reactor, 默认容器是 Netty, Netty 是高性能 NIO 框架,异步非阻塞的框架
-
2.1 Netty 阻塞状态 Socket -》 InputStream -》 read() -》 Thread
非阻塞 NIO 状态

spring Transaction
-
2.2 SpringWebflux 核心控制器 DispatchHandler, 实现接口 WebHandler, 负责请求的处理
- HanddlerMapping : 请求查询处理的方法
- HandlerAdapter: 真正负责请求处理
- HandlerResultHandler: 响应结果处理
-
2.3 函数式编程,两个接口: RouterFunction 和 HandlerFunction
-
- SpringWebflux (基于注解)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.4.1</version>
</dependency>
- 3.1 配置启动端口号
resources -> application.properties -> server.port = 8081
- 3.2 创建包和相关类

spring Transaction

spring Transaction

spring Transaction
-
- SpringWebflux (基于函数)
Spring V1.0 DispatcherServlet
Spring IoC, DI, MVC 的工作原理
遵循单一职责原则
MIN 版本 Spring 基本实现思路
- 配置阶段
- 配置 web.xml | DispatcherServlet
- 设定 init-param | contextConfigLocation = classpath:application.xml
- 设定 url-pattern | /*
- 配置 Annotation | @Controller @Service @Autowrited @RequestMapping
- 初始化阶段
- 调用init()方法 | 加载配置文件
- IOC容器初始化 | *Map<String, Object>
- 扫描相关的类 | scan-package=“com.gupaoedu” IOC + 创建实例化并保存至容器 | 通过反射机制将类实例化放入IOC容器中 DI + 进行DI操作 | 扫描IOC容器中的实例,给没有赋值的属性自动赋值 MVC + 初始化HandlerMapping | 将一个URL和一个Method进行一对一的关联映射Map<String, Method>
-
运行阶段
- 调用doPost()/doGet() | web容器调用doPost/doGet方法,获得request/response对象
- 匹配HandlerMapping | 从request对象中获得用户输入的url,找到对应的Method
- 反射调用method.invoker() | 利用反射调用方法并返回结果
- response.getWrite().write() | 将返回结果输出到游览器
新建一个类,实现三个方法
代码折叠
public class GPDispatchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
<!-- 6.根据URL委派给具体的调用的方法 -->
doDispatch();
}
@Override
protected void init(ServletConfig config) throws ServletException {
<!-- 1.初始化方法init -->
<!--1. 加载配置文件 -->
doLoadConfig();
<!-- 2.扫描相关的类 -->
doScanner();
<!-- Ioc功能 -->
<!-- 3.初始化Ioc容器,将扫描的类进行实例化,缓存到Ioc容器中 -->
doInstance();
<!-- DI功能 -->
<!--4.完成依赖注入 -->
doAutowired();
<!-- MVC功能 -->
<!-- 5.初始化HandlerMapping -->
doInitHandlerMapping();
System.out.printLn("GP sTRING Framework is init.")
}
//根据contextConfigLocation的classpath找到对应的配置文件
private void doLoadConfig(String contextConfigLocatoin)
InputString is = this.getClass.
}
Spring IoC
工厂模式,原型,单例(工厂怎么把对象创建出来,交给用户)
from Servlet to ApplicatoinContext

1. Spring IOC
without IOC

when we need a new Impl, problem occurs

use IOC, we save lots of time for adjusting code

here is a case
Transfer Funds to Other Bank’s Account: A sent money to B account
without IOC
table
name varcher 255 username
money int 255 balance
cardNo varcher 255 bank account
call flow
Transfer page // page transfer botton, send ajax requirement to TransferServlet
⇕ ⇕
TransferServlet // servlet instance is initialized, invokes its Service layer method: TransferService transferService = newTransferServiceImpl();
⇕ ⇕
TransferService // service class create Dao instance as singleton, invokes its Dao layer method: AccountDao accountDao = new JdbcAccountDaoImpl();
⇕ ⇕
AccountDao // Dao provides multiple Impl
↓………………………………… ↓
JdbcAccountDaoImpl MybatisAccountDaoImpl
core code
@WebServlet(name = "transerServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// create service instance
private TransferService transferService = new TransferServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOE {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOE {
req.setCharacterEncoding("UTF-8");
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
// invoke service method
transferService.transfer(fromCardNo,toCardNo,money);
result.setStatus("200");
} catch (Exception e) {
e.printStackTrace();
result.setStatus("201");
result.setMessage(e.toString());
}
// response
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}
}
public interface TransferService {
void transfer(String fromCardNo, String toCardNo, int money) throws Exception;
}
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao = new JdbcAccountDaoImpl();
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
accountDao.updateAccountByCardNo(from);
}
}
public interface AccountDao {
/**
* get account by query accountNo
* @param cardNo
* @return
* @throws Exception
*/
Account queryAccountByCardNo(String cardNo) throws Exception;
/**
* up aacount information
* @param account
* @return
* @throws Exception
*/
int updateAccountByCardNo(Account account) throws Exception;
}
public class JdbcAccountDaoImpl implements AccountDao {
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
Connection con = DruidUtils.getInstance().getConnection();
String sql = "select * from account where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1,cardNo);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while(resultSet.next()) {
account.setCardNo(resultSet.getString("cardNo"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getInt("money"));
}
resultSet.close();
preparedStatement.close();
con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
Connection con = DruidUtils.getInstance().getConnection();
String sql = "update account set money=? where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setInt(1,account.getMoney());
preparedStatement.setString(2,account.getCardNo());
int i = preparedStatement.executeUpdate();
preparedStatement.close();
con.close();
return i;
}
}
analysis:
Transfer page
⇕ ⇕
TransferServlet // public class TransferServlet extends HttpServlet { TransferService transferService = newTransferServiceImpl(); }
⇕ ⇕
interface: TransferService // public class TransferServiceImpl implements TransferService {
impl: TransferServiceImpl // private AccountDao accountDao = new JdbcAccountDaoImpl();
⇕ ⇕
interface:AccountDao
impl: JdbcAccountDaoImpl
P1:in service level New define the references to DAO to the concrete implementations as that will make it tightly coupled, not easy in different implementation scenarios, which will lead to modify service code.
P2:serivice layer miss TransactionStatus. And data error may occur in transferring process, cause serious damage.
solution
P1: new tight coupled problem solution –Using Reflection to instantiate the object in factory pattern
public class TransferServiceImpl implements TransferService {
// private AccountDao accountDao = new JdbcAccountDaoImpl();
private AccountDao accountDao;
// @Override
// public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
// Account from = accountDao.queryAccountByCardNo(fromCardNo);
// Account to = accountDao.queryAccountByCardNo(toCardNo);
// from.setMoney(from.getMoney()-money);
// to.setMoney(to.getMoney()+money);
// accountDao.updateAccountByCardNo(to);
// accountDao.updateAccountByCardNo(from);
// }
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
Method injection, allows to inject a dependency right at the point of use when you have mutiple implementation, Method injection is a good choice
before:
TransferServiceImpl
↓
new
↓
JdbcAccountDaoImpl
now:
TransferServiceImpl
↓
factory read xml file,
use reflection to instantiate the object,
defines an interface for creating objects
↑
xml:
com.test.dao.JdbcAccountDaoImpl
com.test.service.TransferServiceImpl
code
define bean.xml file
<?xml version="1.0" encoding="UTF-8" ?>
<!--each bean represent a class-->
<beans>
<bean id="accountDao" class="com.test.dao.impl.JdbcAccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<bean id="transferService" class="com.test.service.impl.TransferServiceImpl">
<!--set+ name, Method injectione -->
<property name="AccountDao" ref="accountDao"></property>
</bean>
</beans>
define BeanFactory
/*
* factory class
* task1: read xml, use reflection to instantiate the object, store in the map
* task2: provide interfaces for creating objects (id)
*/
public class BeanFactory {
private static Map<String, Object> map = new HashMap<>(); // store object
/*
* read xml, use reflection to instantiate the object, store in the map
*/
static {
// load xml
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml")
// read xml
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.getRootElement();
Element rootElement = document.getRootElement();
List<Element> beanList = rootElement.selectNodes("//bean");
for (int i = 0; i < beanList.size(); i++) {
Element element = beanList.get(i);
// get bean id 和 class property
String id = element.attributeValue("id"); // accountDao
String clazz = element.attributeValue("class"); // com.test.dao.impl.JdbcAccountDaoImpl
// use reflection to instantiate the object
Class<?> aClass = Class.forName(clazz);
Object o = aClass.newInstance();
// store in the map
map.put(id, o);
}
// pass property of the bean into Oject
List<Element> propertyList = rootElement.selectNodes("//property");
// read property, get super element
for (int i = 0; i < propertyList.size(); i++) {
Element element = propertyList.get(i); //<property name="AccountDao" ref="accountDao"></property>
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
// find parent
Element parent = element.getParent();
// call parent element reflection
String parentId = parent.attributeValue("id");
Object parentObject = map.get(parentId);
// search all the methods
Method[] methods = parentObject.getClass().getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
if (method.getName().equalsIgnoreCase("set" + name)) { // setAccountDao(AccountDao accountDao)
method.invoke(parentObject, map.get(ref));
}
}
// put parentObject back into map
map.put(parentId, parentObject);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/*
* provide interfaces for creating objects (id)
* @param id
* @return
*/
public static Object getBean(String id) {
return map.get(id);
}
}
@WebServlet(name = "transerServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// create service instance
// private TransferService transferService = new TransferServiceImpl();
// get service object through BeanFactory
private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
public class TransferServiceImpl implements TransferService {
// get Dao object through BeanFactory
// private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
// better way
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
P2 TransactionStatus make one wrong transaction
accountDao.updateAccountByCardNo(to);
int i = 1/0;
accountDao.updateAccountByCardNo(from);
run the application, click transfer button(A sent $100 to B), get an error, however, when checking the database,
B get $100, but A is still have the same amount of money!!!
solution
- executing multiple UPDATE statement in one connection
- put tranaction in service layer
TransactionStatus code add ConnectionUtils
/**
* get connection
*/
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // store current threadlocal
/**
* connection from thread
*/
public Connection getCurrentThreadConn() throws SQLException {
Connection connection = threadLocal.get();
if(connection == null) {
// get connection
connection = DruidUtils.getInstance().getConnection();
// put into threadlocal
threadLocal.set(connection);
}
return connection;
}
}
add TransactionManager
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
// begin transaction
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// commit transaction
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// rollback
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}
add ProxyFactory
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/*
Jdk Dynamic Proxy
@param obj
@return proxy object
*/
public Object getJdkProxy(Object obj) {
// get proxy object
return Proxy.newProxyInstance(obj.getClass.getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
transactionManager.beginTransaction();
result = method.invoke(obj,args);
//
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
//
transactionManager.rollback();
// throw e can be caught by servlet
throw e;
}
return result;
}
}
});
}
/*
* cglib get Dynamic Proxy
@param obj
@return proxy object
*/
public Object getCglibProxy(Object obj) {
// get proxy object
return Enhancer.create(obj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
transactionManager.beginTransaction();
result = method.invoke(obj,objects);
//
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
//
transactionManager.rollback();
// throw e can be caught by servlet
throw e;
}
return result;
}
}
});
}
}
modify beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--each bean represent a class-->
<beans>
<bean id="accountDao" class="com.test.dao.impl.JdbcAccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<bean id="transferService" class="com.test.service.impl.TransferServiceImpl">
<!--set+ name, Method injectione -->
<property name="AccountDao" ref="accountDao"></property>
</bean>
<!--add three new bean-->
<bean id="connectionUtils" class="com.test.utils.ConnectionUtils">
</bean>
<!--transactionManager bean-->
<bean id="transactionManager" class="com.test.utils.TransactionManager">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<!--ProxyFactory-->
<bean id="proxyFactory" class="com.test.factory.ProxyFactory">
<property name="TransactionManager" ref="transactionManager"/>
</bean>
</beans>
public class JdbcAccountDaoImpl implements AccountDao {
private ConnectionUtils connectionUtils;
public void setConnecionUtils(ConnectionUtils connecionUtils) {
this.connectionUtils = connectionUtils;
}
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
// Connection con = DruidUtils.getInstance().getConnection();
// get connection from threadlocal
Connection con = connectionUtils.getCurrentThreadConn();
String sql = "select * from account where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1,cardNo);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while(resultSet.next()) {
account.setCardNo(resultSet.getString("cardNo"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getInt("money"));
}
resultSet.close();
preparedStatement.close();
con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
// Connection con = DruidUtils.getInstance().getConnection();
Connection con = connectionUtils.getCurrentThreadConn();
String sql = "update account set money=? where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setInt(1,account.getMoney());
preparedStatement.setString(2,account.getCardNo());
int i = preparedStatement.executeUpdate();
preparedStatement.close();
// con.close();
return i;
}
}
modify TransferServlet
@WebServlet(name = "transerServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// create service instance
// private TransferService transferService = new TransferServiceImpl();
// get service object through BeanFactory
// private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
// get proxy object from factory
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
private TransferService transferService = (TransferService) proxyFactory.getProxy(BeanFactory.getBean("transferService"));
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
// 2. call service method
transferService.transfer(fromCardNo,toCardNo,money);
result.setStatus("200");
} catch (Exception e) {
e.printStackTrace();
result.setStatus("201");
result.setMessage(e.toString());
}
//
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}
p2 problem solved
Q: why use proxy for transferServiceImpl?
without proxy, all transtation relate code would be put in the TransferServiceImpl
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
try{
TransactionManager.getInstance().beginTransaction();*/
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
// make a error to test
int c = 1/0;
accountDao.updateAccountByCardNo(from);
TransactionManager.getInstance().commit();
}catch (Exception e) {
e.printStackTrace();
TransactionManager.getInstance().rollback();
throw e;
}
}
}
we can see the transaction and Impl have become a tight couple, if there are multiple Impl need transaction method, we need add the same codes on every single method. Using proxy way to realize transaction management is a AOP
from Servlet to ApplicatoinContext
IoC
-
Map 容器
-
BeanFactory 工厂
-
ApplicationContext 上下文:持有BeanFactory引用, 门面模式
-
BeanDefinitionReader解析器:负责解析所有的配置文件
-
BeanDefinition元信息,配置(保存各种配置信息) xml, yml, annotation, properties
-
Bean实例,反射实例化Object 原生Bean,代理Bean
-
BeanWrapper包装器模式:缓存到了Ioc容器,缓存 持有Bean引用
IOC 顶层设计, ListableBeanFactory为例
用户通过 => ApplicationContext => 调用 getBean()方法, 底层各种factory方法listable等,创建factory对象, 所以要调用BeanDefinitionReader, 读取bean配置文件,创建 BeanDefinition = > 换存到容器里就是BeanWrapper对象, 所以getBean()实际拿到的就是BeanWrapper对象
init() {
new ApplicationContext() {
reader = new BeanDefinitionReader();
reader.loadBeanDefinitions();
factory.doRegistryBeanDefinition() {
beanDefinitionMap.put()
}
doLoadInstance() {
getBean() //循环调用
}
}
}
getBean() {
BeanDefinition beanDefinition = registry.beanDefinitionMap.get(beanName);
Object instance = instantiateBean();
BeanWrapper beanWrapper = new BeanWrapper(instance);
factoryBeanInstanceCache.put(beanWrapper);
populateBean(); //依赖注入
}
1.1 the most basic bean container define a simple bean container: BeanFactory, contains a map to store bean, owns registry and get Bean two methods.
public class BeanFactory {
private Map<String, Object> beanMap = new HashMap<>();
public void registerBean(String name, Object bean) {
beanMap.put(name, bean);
}
public Object getBean(String name) {
return beanMap.get(name);
}
}
public class SimpleBeanContainerTest {
@Test
public void testGetBean() throws Exception {
BeanFactory beanFactory = new BeanFactory();
beanFactory.registerBean("helloService", new HelloService());
HelloService helloService = (HelloService) beanFactory.getBean("helloService");
assertThat(helloService).isNotNull();
assertThat(helloService.sayHello()).isEqualTo("hello");
}
class HelloService {
public String sayHello() {
System.out.println("hello");
return "hello";
}
}
}
BeanDefinition & BeanDefinitionRegistry

click here code download
…beans.factory.config.SingletonBeanRegistry.java
…beans.factory.config.BeanDefinition.java
…beans.factory.support.DefaultSingletonBeanRegistry.java
…beans.factory.BeanFactory.java
…beans.factory.support.AbstractAutowireCapableBeanFactory.java
…beans.factory.support.AbstractBeanFactory.java
…beans.factory.support.BeanDefinitionRegistry.java
…beans.factory.support.DefaultListableBeanFactory.java
package org.springframework.beans;
public class BeanDefinitionAndBeanDefinitionRegistryTest {
@Test
public void testBeanFactory() throws Exception {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
BeanDefinition beanDefinition = new BeanDefinition(HelloService.class);
beanFactory.registerBeanDefinition("helloService", beanDefinition);
HelloService helloService = (HelloService) beanFactory.getBean("helloService");
helloService.sayHello();
}
}
class HelloService {
public String sayHello() {
System.out.println("hello");
return "hello";
}
}
InstantiationStrategy
now we instantiat bean by method : beanClass.newInstance() in AbstractAutowireCapableBeanFactory.doCreateBean. And this only used in NoArgsConstructo
now create InstantiationStrategy interface with implemented methods
- SimpleInstantiationStrategy: use bean construction
- CglibSubclassingInstantiationStrategy,use CGLIB
基础java知识
1. servlet(模板模式)
- HttpServlet已经实现了service方法:HttpServlet是一个抽象类
一个类声明成抽象方法,一般有两个原因: 有抽象方法 OR 没有抽象方法,但是不希望被实例化
HttpServlet做成抽象类,仅仅是为了不让new
如何写一个Servet?
不用实现javax.servlet接口
不用继承GenericServlet抽象类
只需继承HttpServlet并重写doGet()/doPost()
父类把能写的逻辑都写完,把不确定的业务代码抽成一个方法,调用它。当子类重写该方法,整个业务代码就活了。这就是模板方法模式
父类: 子类
service() { service() {
doXxx(); //具体业务代码,但是父类无法知道子类具体业务逻辑, <= 继承 doXxx();
//所以后抽象惩罚让子类重写 }
}
protected void doXxx() { <= 覆盖 void doXxx() {
//空实现,或者默认实现 //具体实现
} }
1.2 ServletContext
map,服务器会为每个应用创建一个ServletContext对象
ServletContext对象的作用是在整个Web应用的动态资源(Servlet/JSP)之间共享数据
这种用来装载共享数据的对象,在JavaWeb中共有4个,而且更习惯被成为“域对象”:
ServletContext域(Servlet间共享数据)
Session域(一次会话间共享数据,也可以理解为多次请求间共享数据)
Request域(同一次请求共享数据)
Page域(JSP页面内共享数据)
它们都可以看做是map,都有getAttribute()/setAttribute()方法。
2.Java泛型Generics
- 泛型类
<T>Type Parameter
public class ArrayList<T> {
private T[] array;
private int size;
public void add(T e) {...}
public void remove(int index) {...}
public T get(int index) {...}
}
- 对变量类型进行抽取
public User getUser(T t){...}
- 获取构造函数的参数
获取到构造函数的对象之后,可以通过getParameterTypes()获取到构造函数的参数。
Constructor constructors = birdClass.getConstructor(new Class[]{String.class});
Class[] parameterTypes = constructors.getParameterTypes();
3.反射
- JVM是如何构建一个实例的 A a = new A();
Step1: ClassLoader加载.class文件到内存(jvm内存;执行静态代码块和静态初始化语句
Step2: 执行new,申请一个内存空间
Step3:调用构造器,创建一个空白对象
step4:子类调用父类构造器
step5:构造器执行: 执行构造代码块和初始化语句; 构造器内容
- Class
Class类对象就相当于B超的探头,将一个类的方法、变量、接口、类名、类修饰符等信息告诉运行的程序。
- 获取构造函数Constructor
获取构造函数的方法:
Class birdClass = Bird.class;
Constructor[] constructors = birdClass.getConstructors();
一个类会有多个构造函数,getConstructors()返回的是Constructor[]数组,包含了所有声明的用public修饰的构造函数。
如果你已经知道了某个构造的参数,可以通过下面的方法获取到回应的构造函数对象:
public class Alunbar {
public static void main(String arts[]){
Class birdClass = Bird.class;
try{
Constructor constructors = birdClass.getConstructor(new Class[]{String.class});
}catch(NoSuchMethodException e){
}
}
private class Bird {
public Bird(){
}
public Bird(String eat){
}
}
}
- 类加载器
loadClass(),告诉它需要加载的类名,它会帮你加载
// 子类应该重写该方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
加载.class文件大致可以分为3个步骤:
检查是否已经加载,有就直接返回,避免重复加载 当前缓存中确实没有该类,那么遵循父优先加载机制,加载.class文件 上面两步都失败了,调用findClass()方法加载 需要注意的是,ClassLoader类本身是抽象类,而抽象类是无法通过new创建对象的。所以它的findClass()方法写的很随意,直接抛了异常,反正你无法通过ClassLoader对象调用。也就是说,父类ClassLoader中的findClass()方法根本不会去加载.class文件。
正确的做法是,子类重写覆盖findClass(),在里面写自定义的加载逻辑。比如:
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
try {
/*自己另外写一个getClassData()
通过IO流从指定位置读取xxx.class文件得到字节数组*/
byte[] datas = getClassData(name);
if(datas == null) {
throw new ClassNotFoundException("类没有找到:" + name);
}
//调用类加载器本身的defineClass()方法,由字节码得到Class对象
return defineClass(name, datas, 0, datas.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException("类找不到:" + name);
}
}
defineClass()是ClassLoader定义的方法,目的是根据.class文件的字节数组byte[] b造出一个对应的Class对象。我们无法得知具体是如何实现的,因为最终它会调用一个native方法:
- 反射API
创建实例
clazz.newInstance()底层还是调用Contructor对象的newInstance()。所以,要想调用clazz.newInstance(),必须保证编写类的时候有个无参构造。
为什么根据Class对象获取Method时,需要传入方法名+参数的Class类型?
调用Class对象的getMethod()方法时,内部会循环遍历所有Method,然后根据方法名和参数类型匹配唯一的Method返回。
调用method.invoke(obj, args);时为什么要传入一个目标对象?
把Method理解为方法执行指令吧,它更像是一个方法执行器,必须告诉它要执行的对象(数据)。
反射调用方法
4.Java 动态代理
- 问题: 原有代码:
public class Calculator {
//加
public int add(int a, int b) {
int result = a + b;
return result;
}
//减
public int subtract(int a, int b) {
int result = a - b;
return result;
}
//乘法、除法...
}
现有一个需求:在每个方法执行前后打印日志.
直接修改
public class Calculator {
//加
public int add(int a, int b) {
System.out.println("add方法开始...");
int result = a + b;
System.out.println("add方法结束...");
return result;
}
//减
public int subtract(int a, int b) {
System.out.println("subtract方法开始...");
int result = a - b;
System.out.println("subtract方法结束...");
return result;
}
//乘法、除法...
}
上面的方案是有问题的:
直接修改源程序,不符合开闭原则。应该对扩展开放,对修改关闭 如果Calculator有几十个、上百个方法,修改量太大 存在重复代码(都是在核心代码前后打印日志) 日志打印硬编码在代理类中,不利于后期维护:比如你花了一上午终于写完了,组长告诉你这个功能取消,于是你又要打开Calculator花十分钟删除日志打印的代码!
- 静态代理,通过代理访问目标对象
静态代理的实现比较简单:编写一个代理类,实现与目标对象相同的接口,并在内部维护一个目标对象的引用。通过构造器塞入目标对象,在代理对象中调用目标对象的同名方法,并添加前拦截,后拦截等所需的业务功能。
- 将Calculator抽取为接口
/**
* 目标对象实现类,实现Calculator接口
*/
public class CalculatorImpl implements Calculator {
//加
public int add(int a, int b) {
int result = a + b;
return result;
}
//减
public int subtract(int a, int b) {
int result = a - b;
return result;
}
//乘法、除法...
}
- 创建代理类CalculatorProxy实现Calculator
/**
* 代理对象实现类,实现Calculator接口
*/
public class CalculatorProxy implements Calculator {
//代理对象内部维护一个目标对象引用
private Calculator target;
//构造方法,传入目标对象
public CalculatorProxy(Calculator target) {
this.target = target;
}
//调用目标对象的add,并在前后打印日志
@Override
public int add(int a, int b) {
System.out.println("add方法开始...");
int result = target.add(a, b);
System.out.println("add方法结束...");
return result;
}
//调用目标对象的subtract,并在前后打印日志
@Override
public int subtract(int a, int b) {
System.out.println("subtract方法开始...");
int result = target.subtract(a, b);
System.out.println("subtract方法结束...");
return result;
}
//乘法、除法...
}
- 使用代理对象完成加减乘除,并且打印日志
public class Test {
public static void main(String[] args) {
//把目标对象通过构造器塞入代理对象
Calculator calculator = new CalculatorProxy(new CalculatorImpl());
//代理对象调用目标对象方法完成计算,并在前后打印日志
calculator.add(1, 2);
calculator.subtract(2, 1);
}
}
没解决重复代码,如果有很多类,没解决修改量太大的问题, 所以我们要代理的不是代理类,而是代理对象,根据接口自动生成代理对象
- 动态代理
Proxy有个静态方法:getProxyClass(ClassLoader, interfaces),只要你给它传入类加载器和一组接口,它就给你返回代理Class对象。
- 使用代理对象完成加减乘除,并且打印日志
public class ProxyTest {
public static void main(String[] args) throws Throwable {
//Calculator的类加载器
Class calculatorProxyClazz = Proxy.getProxyClass(Calculator.class.getClassLoader(), Calculator.class);
//得到有参构造器
Constructor constructor = calculatorProxyClazz.getConstructor(InvocationHandler.class);
//反射创建代理实例
Calculator CalculatorProxyImpl = (Calculator)constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method, method, Object[] args) throws Exception {
//手动new一个目标对象
CalculatorImpl calculatorImpl = new CalculatorImpl();
//反射执行目标对象的方法
Object result = method.invoke(calculatorImpl, args);
//返回目标对象执行结果
return result;
}
})
CalculatorProxyImpl.add(1, 2);
}
}
代理对象改变,invoke方法要改,所以把目标对象当参数传进来
public class ProxyTest {
public static void main(String[] args) throws Throwable {
//传入目标对象
CalculatorImpl target = new CalculatorImpl();
//根据它实现的接口生成代理对象, 代理对象调用目标对象方法
Calculator calculatorProxy = (Calculator)getProxy(target);
calculatorProxy.add(1, 2);
calculatorProxy.substract(2, 1);
}
private static Object getProxy(final Object target) throws Exception {
Class ProxyClazz = Proxy.getProxyClass(target.getclass().getClassLoader(), target.getclass().getInterfaces());
//得到有参构造器
Constructor constructor = ProxyClazz.getConstructor(InvocationHandler.class);
//反射创建代理实例
Object proxy = constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method, method, Object[] args) throws Exception {
System.out.println(method.getName() + "方法开始执行。。");
Object result = method.invoke(target, args);
//反射执行目标对象的方法
return result;
}
});
return proxy;
}
}
无论现在系统有多少类,只要你把实例传进来,getProxy()都能给你返回对应的代理对象。就这样,我们完美地跳过了代理类,直接创建了代理对象!
不过实际编程中,一般不用getProxyClass(),而是使用Proxy类的另一个静态方法:Proxy.newProxyInstance(),直接返回代理实例,连中间得到代理Class对象的过程都帮你隐藏:
public class ProxyTest {
public static void main(String[] args) throws Throwable {
//传入目标对象
CalculatorImpl target = new CalculatorImpl();
//根据它实现的接口生成代理对象, 代理对象调用目标对象方法
Calculator calculatorProxy = (Calculator)getProxy(target);
calculatorProxy.add(1, 2);
calculatorProxy.substract(2, 1);
}
private static Object getProxy(final Object target) throws Exception {
Object proxy = Proxy.newProxyInstance(target.getclass().getClassLoader(), target.getclass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method, method, Object[] args) throws Exception {
System.out.println(method.getName() + "方法开始执行。。");
Object result = method.invoke(target, args);
//反射执行目标对象的方法
return result;
}
}
);
return proxy;
}
}
Spring DI
组合复合原则(怎么给对象自动赋值,循环依赖注入)
Spring MVC
委派,策略,解释器原则(用户输入URL怎么样和java代码关联)
Spring AOP
责任,动态代理
我们需要一个通知类(TransactionManager)执行事务,一个代理工厂帮助生成代理对象,然后利用动态代理将事务代码织入代理对象的各个方法中。
UserService {
public void test() {
//开启事务
userDao.add();
}
}
BrandService {
public void test() {
//开启事务
brandDao.add();
}
}
CategoryService {
public void test() {
//开启事务
categoryDao.add();
}
}
希望最终达到的效果是,我加了个@MyTransactional后,代理工厂给我返回一个代理对象:
UserService –》 入参 |代理工厂| 出参 –》 UserServiceProxy
细节分析:
UserServiceProxy.test() –> //+1 +8
|代理工厂|
代理对象 {
//1.开启事务
txManager.beginTransaction(); //+2
//2.执行事务
rtValue = method.invoke(target, args) --> 调用代理对象同名方法+3 +5
//3.提交事务
txManager.commit(); //+6
//3.返回结果
return rtValue; //+7
}
UserService {
public void test() { //+4
//开启事务
userDao.add();
}
}
代理对象方法 = 事务 + 目标对象方法。
事务操作,必须使用同一个Connection对象。如何保证?第一次从数据源获取Connection对象并开启事务后,将它存入当前线程的ThreadLocal中,等到了DAO层,还是从ThreadLocal中取,这样就能保证开启事务和操作数据库使用的Connection对象是同一个。
开启事务后,Controller并不是直接调用Service,而是Spring提供的代理对象
Service和Dao关系也是i如此
AOP事务具体代码实现
ConnectionUtils工具类
package com.demo.myaopframework.utils;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
/**
* 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
*/
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private static BasicDataSource dataSource = new BasicDataSource();
//静态代码块,设置连接数据库的参数
static{
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
}
/**
* 获取当前线程上的连接
* @return
*/
public Connection getThreadConnection() {
try{
//1.先从ThreadLocal上获取
Connection conn = tl.get();
//2.判断当前线程上是否有连接
if (conn == null) {
//3.从数据源中获取一个连接,并且存入ThreadLocal中
conn = dataSource.getConnection();
tl.set(conn);
}
//4.返回当前线程上的连接
return conn;
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 把连接和线程解绑
*/
public void removeConnection(){
tl.remove();
}
}
AOP通知(事务管理器)
package com.demo.myaopframework.utils;
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
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.getThreadConnection().close();//还回连接池中
connectionUtils.removeConnection();
}catch (Exception e){
e.printStackTrace();
}
}
}
自定义注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTransactional {
}
Service
public interface UserService {
void getUser();
}
public class UserServiceImpl implements UserService {
@Override
public void getUser() {
System.out.println("service执行...");
}
}
实例工厂
public class BeanFactory {
public Object getBean(String name) throws Exception {
//得到目标类的Class对象
Class<?> clazz = Class.forName(name);
//得到目标对象
Object bean = clazz.newInstance();
//得到目标类上的@MyTransactional注解
MyTransactional myTransactional = clazz.getAnnotation(MyTransactional.class);
//如果打了@MyTransactional注解,返回代理对象,否则返回目标对象
if (null != myTransactional) {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
TransactionManager txManager = new TransactionManager();
txManager.setConnectionUtils(new ConnectionUtils());
//装配通知和目标对象
proxyFactoryBean.setTxManager(txManager);
proxyFactoryBean.setTarget(bean);
Object proxyBean = proxyFactoryBean.getProxy();
//返回代理对象
return proxyBean;
}
//返回目标对象
return bean;
}
}
代理工厂
public class ProxyFactoryBean {
//通知
private TransactionManager txManager;
//目标对象
private Object target;
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public void setTarget(Object target) {
this.target = target;
}
//传入目标对象target,为它装配好通知,返回代理对象
public Object getProxy() {
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),/*1.类加载器*/
target.getClass().getInterfaces(), /*2.目标对象实现的接口*/
new InvocationHandler() {/*3.InvocationHandler*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
Object retVal = method.invoke(target, args);
//3.提交事务
txManager.commit();
//4.返回结果
return retVal;
} catch (Exception e) {
//5.回滚事务
txManager.rollback();
throw new RuntimeException(e);
} finally {
//6.释放连接
txManager.release();
}
}
}
);
return proxy;
}
}
AOPTest,
BeanFactory beanFactory = new BeanFactory();
try {
Object bean = beanFactory.getBean("com.demo.mya.service.UserServiceImpl");
Sout(bean.getClass().getName());
}
com.demo.mya.service.UserServiceImpl
给UserServiceImpl添加@MyTransactional注解,得到代理对象:
com.sun.proxy.$Proxy2
JDBC
JDBC的演化版本1.0
通过DriveManager得到Connection,得到PreparedStatement, PreparedStatement执行sql返回结果
public class Basic {
@Test
public void testJdbc() throws SQLException, ClassNotFoundException {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.建立练连接
String url = "jdbc:mysql://192.168.56.10:3306/test";
String user = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 创建sql模板
String sql = "select * from t_user where id = ?";
PreparedStatement preparedStatement = conn.preparedStatement(sql);
// 4. 设置模板参考
preparedStatement.setInt(1, 5);
// 5. 执行语句
ResultSet rs = preparedStatement.executeQuery();
// 6. 处理结果
while (rs.next()) {
System.out.println(rs.getObject(1) + "\t" + rs.getObject(2) + "\t" + rs.getObject(3) + "\t" + rs.getObject(4));
}
// 7. 释放资源
rs.close();
preparedStement.close();
conn.close();
}
}
获取Connection的步骤太复杂,需要封装; 资源释放太随意,不够规范,数据库的连接数是有限的,如果不及时释放,会导致其他请求无法访问。应该把释放资源的操作放在finally中,保证资源一定会被关闭。
public class Basic {
// 抛异常
@Test
public void testJdbc() throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1. 获得连接
conn = JdbcUtils.getConnection();
String sql = "select * from t_user where id = ?";
ps = conn.preparedStatement(sql);
// 4. 设置模板参考
ps.setInt(1, 5);
// 5. 执行语句
rs = ps.executeQuery();
// 6. 处理结果
while (rs.next()) {
System.out.println(rs.getObject(1) + "\t" + rs.getObject(2) + "\t" + rs.getObject(3) + "\t" + rs.getObject(4));
}
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
}
JdbcUtils 1.0版本
public class JdbcUtils {
private static Properties pros = null;
// 只在JdbcUtils类都加载执行一次
static {
// 1. 给pros进行初始化,加载jdbc.properties文件到props对象中
try {
InputStream in = JdbcUtils.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
props = new Properties();
props.load(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
//加载驱动类
try {
Class.forName(props.getProperty("driver"));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
// 得到Connection
return DriverManager.getConnection(props.getProperty("url"),
props.getProperty("username"),
props.getProperty("password"));
}
// 释放连接
public static void free (ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrack();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrack();
} finally {
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
CRUD
crud
- dao
- UserDao(interface)
- UserDaoJdbcImpl
- pojo
- User
- DAOTest
- JdbcUtils
UserDao
public interface UserDao {
int addUser(User user) throws SQLException;
int update(User user) throws SQLException;
int delete(User user) throws SQLException;
User getUser(int Id) throws SQLException;
User findUser(String name, int age) throws SQLException;
}
UserDaoJdbcImpl
public class UserDaoJdbcImpl implements UserDao {
public int addUser(User user) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "insert into t_user(name,age, birthday) values (?,?,?) ";
ps = conn.prepareStatement(sql);
ps.setString(1, user.getName());
ps.setInt(2, user.getAge());
ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
return ps.executeUpdate();
/* } catch (SQLExceptoin e) {
//转为DaoException(运行时异常)抛出,Service层可以不处理
throw new DaoException(e.getMessage(), e); */
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
public int delete(User user) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "delete from t_user where id=?";
ps = conn.prepareStatement(sql);
ps.setInt(1, user.getId());
System.out.println(sql);
return ps.executeUpdate(sql);
/* } catch (SQLExceptoin e) {
//转为DaoException(运行时异常)抛出,Service层可以不处理
throw new DaoException(e.getMessage(), e); */
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
public int update(User user) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
ps = conn.prepareStatement(sql);
ps.setString(1, user.getName());
ps.setInt(2, user.getAge());
ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
ps.setInt(4, user.getId());
return ps.executeUpdate();
/* } catch (SQLExceptoin e) {
//转为DaoException(运行时异常)抛出,Service层可以不处理
throw new DaoException(e.getMessage(), e); */
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
public User findUser(String name, int age) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select id, name, birthday from t_user where name=? and age=?";
ps = conn.prepareStatement(sql);
ps.setString(1, name);
ps.setInt(2, age);
rs = ps.executeQuery();
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setBirthday(rs.getDate("birthday"));
/* } catch (SQLExceptoin e) {
//转为DaoException(运行时异常)抛出,Service层可以不处理
throw new DaoException(e.getMessage(), e); */
} finally {
JdbcUtils.free(rs, ps, conn);
}
return user;
}
public User getUser(int userId) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select id, name, age, birthday from t_user where id=?";
ps = conn.prepareStatement(sql);
ps.setInt(1, userId);
rs = ps.executeQuery();
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setBirthday(rs.getDate("birthday"));
/* } catch (SQLExceptoin e) {
//转为DaoException(运行时异常)抛出,Service层可以不处理
throw new DaoException(e.getMessage(), e); */
} finally {
JdbcUtils.free(rs, ps, conn);
}
return user;
}
DAOTest
public class DAOTest {
public static void main(String[] args) throws SQLException {
UserDao userDao = new UserDaoJdbcImpl();
User user = new User();
user.setAge(19);
user.setName("little ming");
user.setBirthday(new Date());
userDao.addUser(user);
}
}
异常处理 上面的CRUD并没有捕获异常,而是直接往外抛。这会带来两个后果:
SQLException是编译时异常,Service在调用DAO时必须处理异常,否则编译不通过。如何处理?要么继续抛,交给Controller处理(意义不大),要么try catch(Service层代码很臃肿,不美观)。 DAO接口有声明异常SQLException,这等于向外界暴露DAO层是JDBC实现。而且针对该接口只能用关系型数据库,耦合度太高了。后期无法切换DAO实现。 比较好的做法是,将SQLException转为运行时异常抛出,Service层可处理也可不处理。
DaoException
public class DaoException extends RuntimeException {
public DaoException() {
}
public DaoException(String message) {
super(message);
}
public DaoException(Throwable cause) {
super(cause);
}
public DaoException(String message, Throwable cause) {
super(message, cause);
}
}
模板方法模式重构
繁琐, 如果还有StudentDaoJdbcImpl、TeacherDaoJdbcImpl,那么同样的代码要写好多遍。所以,必须要重构。大体思路是:相同的代码抽取到父类AbstractDao。观察UserDao, 不论是UserDaoLmpl还是StudentDaoJdbcImpl,TeacherDaoJdbcImp, 只有sql模板和设置模板参数的代码不同。可以把sql和参数抽取成父类方法的形参
String sql = “select id, name, age, birthday from t_user where id=?";
ps.setInt(1, userId);
AbstractDao
public abstract class AbstractDao {
public int addUser(String sql, Object...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// String sql = "insert into t_user(name,age, birthday) values (?,?,?) ";
// sql由调用者传入
ps = conn.prepareStatement(sql);
// ps.setString(1, user.getName());
// ps.setInt(2, user.getAge());
// ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
for (int i = 0; i < args.length; i++) {
ps.setObject(i+ 1, args[i]);
}
return ps.executeUpdate();
} catch (SQLExceptoin e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
}
UserDaoImpl
public class UserDaoImpl extends AbstractDao implements UserDao {
public int addUser(User user) {
String sql = "insert into t_user(name, age, birthday) values (?,?,?) ";
Object[] args = new Object[]{user.getName(), user.getAge(), user.getBirthday()};
// 调用父类AbstractDao方法
return super.update(sql, args);
}
public int update(User user) {
String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
Object[] args = new Object[]{user.getName(), user.getAge(),
user.getBirthday(), user.getId()};
return super.update(sql, args);
}
//改
public int delete(User user) {
String sql = "delete from t_user where id=?";
Object[] args = new Object[]{user.getId()};
return super.update(sql, args);
}
}
如何抽取查询方法?
user, student都需要用map来装
父类无法制定一个通用代码满足所有子类的结果集映射,因为只有子类自己知道映射规则。所以,我们只能把结果集映射的权利交还给子类去实现。子类如果需要查询,就必须自己实现AbstractDao的rowMapper方法。
AbstractDao
public abstract class AbstractDao {
public int update(String sql, Object...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// String sql = "insert into t_user(name,age, birthday) values (?,?,?) ";
// sql由调用者传入
ps = conn.prepareStatement(sql);
// ps.setString(1, user.getName());
// ps.setInt(2, user.getAge());
// ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
for (int i = 0; i < args.length; i++) {
ps.setObject(i+ 1, args[i]);
}
return ps.executeUpdate();
} catch (SQLExceptoin e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
//查询
public List<Object> query(String sql, Object[] args, RowMapper, rowMapper) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List list = new ArrayList<>();
try {
conn = JdbcUtils.getConnection();
// sql由调用者传入
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+ 1, args[i]);
}
rs = ps.executeQuery();
Object obj = null;
while (rs.next()) {
Object o = rowMapper.mapRow(rs);
list.add(o);
}
return list;
} catch (SQLExceptoin e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
// 定义成抽象方法,让子类去实现
abstract protected Object rowMapper(ResultSet rs);
UserDaoImpl
public User findUser(String name, int age) {
String sql = "select id, name, age, birthday from t_user where id=?";
Object[] args = new Object[] {name, age};
Object user = super.query(sql, args);
return (User) user;
}
public User getUser(int id) {
String sql = "select id, name, age, birthday from t_user where name=? and age=?";
Object[] args = new Object[] {id};
Object user = super.query(sql, args);
return (User) user;
}
//UserDaoImpl的结果map
protected Object rowMapper(ResultSet rs) {
User user = null;
try {
user = new User();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setBirthday(rs.getDate("birthday"));
} catch (SQLException e) {
throw new DaoException("mapping error");
}
return user;
}
}
假设现在UserDao增加了一个新方法?
public interface UserDao {
int addUser(User user);
int update(User user);
int delete(User user);
User getUser(int Id);
User findUser(String name, int age);
//新增查询方法:根据年龄查询
List<User> selectUsers(int age);
}
返回值是List,而UserDaoImpl中实现的映射方法rowMapper()只能封装User对象:
直接传方法不行?那我就把这个方法塞进一个对象里,通过对象去调用方法(把需要代理对象执行的代码写在InvocationHandler对象的invoke方法中,再把invocationHandler塞进代理对象供它调用)。
这种模式其实叫策略模式,而且一般是传入接口的实现类。
现在子类已经不需要实现父类的抽象方法了(一个规则无法满足不同返回值映射),改为由子类实现RowMapper接口传入匿名对象的方式,所以AbstractDao中的抽象方法可以删除。也就是说AbstractDao已经没有抽象方法了。于是我把它声明为普通类(可以new),并改名为MyJDBCTemplate。而且,使用MyJDBCTemplate时,我决定不再使用继承,而是选择组合方式(组合比继承灵活)。 RowMapper
public interfae RowMapper {
// map result
Object mapRow(ResultSet rs) throws SQLException;
}
MyJDBCTemplate
public class MyJDBCTmplate {
//增删改
public int update(String sql, Object...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// String sql = "insert into t_user(name,age, birthday) values (?,?,?) ";
// sql由调用者传入
ps = conn.prepareStatement(sql);
// ps.setString(1, user.getName());
// ps.setInt(2, user.getAge());
// ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
for (int i = 0; i < args.length; i++) {
ps.setObject(i+ 1, args[i]);
}
return ps.executeUpdate();
} catch (SQLExceptoin e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
//查询
public Object query(String sql, Object[] args, RowMapper rowMapper) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List list = new ArrayList<>();
try {
conn = JdbcUtils.getConnection();
// sql由调用者传入
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+ 1, args[i]);
}
rs = ps.executeQuery();
Object obj = null;
while (rs.next()) {
Object o = rowMapper.mapRow(rs);
list.add(o);
}
return list;
} catch (SQLExceptoin e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
UserDaoImpl
public class UserDaoImpl implements UserDao {
MyDBCTemplate jdbcTemplate = new MyJDBCTemplate();
public int addUser(User user) {
String sql = "insert into t_user(name, age, birthday) values (?,?,?) ";
Object[] args = new Object[]{user.getName(), user.getAge(), user.getBirthday()};
// 调用父类AbstractDao方法
return jdbcTemplate.update(sql, args);
}
public int update(User user) {
String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
Object[] args = new Object[]{user.getName(), user.getAge(), user.getBirthday(), user.getId()};
// 调用父类AbstractDao方法
return jdbcTemplate.update(sql, args);
}
public User findUser(String name, int age) {
String sql = "select id, name, age, birthday from t_user where id=?";
Object[] args = new Object[] {name, age};
Object user = super.query(sql, args);
return (User) user;
}
public int delete(User user) {
String sql = "delete from t_user where id=?";
Object[] args = new Object[]{user.getId()};
//调用jdbcTemplate的update方法
return jdbcTemplate.update(sql, args);
public User getUser(int id) {
String sql = "select id, name, age, birthday from t_user where id=?";
Object[] args = new Object[] {id};
//调用jdbcTemplate的query方法,传入sql,args, RowMapper匿名对象
List list = jdbcTemplate.query(sql, args, new RowMapper() {
public Object mapRow(ResultSet rs) throws SQLException {
User user = new User();
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
});
return (User)list.get(0);
}
public User findUser(String name, int age) {
String sql = "select id, name, age, birthday from t_user where name=? and age=?";
Object[] args = new Object[] {name, age};
//调用jdbcTemplate的query方法,传入sql,args, RowMapper匿名对象
List list = jdbcTemplate.query(sql, args, new RowMapper() {
public Object mapRow(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
});
return (User)list.get(0);
}
public List selectUser(int age) {
String sql = "select id, name, age, birthday from t_user where age=?";
Object[] args = new Object[] {age};
//调用jdbcTemplate的query方法,传入sql,args, RowMapper匿名对象
List list = jdbcTemplate.query(sql, args, new RowMapper() {
public Object mapRow(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setName(rs.getString("name"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
});
return list;
}
jdbcUtils 2.0
public class JdbcUtils {
//初始化一个数据库
private static MyDataSource dataSource = new MyDataSource();
// 获取连接
public static Connection getConnection() throws SQLException {
// 从数据源获取Connection并返回
return dataSource.getConnection();
}
// 获取数据源
public static MyDataSource getDataSource() {
return dataSource;
}
// 释放连接
public static void free (ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrack();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrack();
} finally {
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
MyDataSource
public class MyDataSource {
//数据库信息,用于连接数据库,放进properties里面
/* private static String url = "jdbc:mysql://192.168.56.10:3306/test?userSSL-false";
private static String user = "root";
private static String password = "root"; */
private static Properties pros = null;
// 数据库信息
static {
// 1. 给pros进行初始化,加载jdbc.properties文件到props对象中
try {
InputStream in = JdbcUtils.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
props = new Properties();
props.load(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//连接池数据,省略
private static int initCount = 5;
private static int currentIdleCount = 0;
//LinkedList充当连接池,removeFirst取出连接,addLast归还连接
private final static LinkedList<Connection> connectionpool = new LinkedList<>();
public MyDataSource() {
try {
for (int i = 0; i < initCount; i++) {
// 创建RealConnection
Connection realConnection = DriverManager.getConnection(props.getProperty("url"),
props.getProperty("username"),
props.getProperty("password"));
// 将RealConnection传入createProxyConnection(),得到代理连接并加入池中, currentIdleCount++
connectionPool.addLast(this.createProxyConnection(realConnection));
currentIdleCount++;
}
Sout("...连接池初始化结束" + currentIdleCount + "个Connection...")
} catch
}
// 获取连接
public Connection getConnection() throws SQLException {
// 同步代码
synchronized (connectionPool) {
// 连接池中还有空闲连接,从池中取出,currentIdleCount--
}
return connectionPool.removeFirst();
}
// 释放连接
public static void free (ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrack();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrack();
} finally {
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
工厂模式重构
User
public class User {
private Long id;
private String name;
private Integer age;
private Date birthday;
//省略getter、setter...
}
DaoFactory
public class DaoFactory {
private static UserDao userDao = null;
private static DaoFactory daoFactory = new DaoFactory();
private DaoFactory() {
try {
Properties prop = new Properties();
InputStream inStream = DaoFactory.class.getClassLoader()
.getResourceAsStream("daoconfig.properties");
prop.load(inStream);
//从配置文件中读取UserDao的实现类全类名
String userDaoClass = prop.getProperty("userDao");
Class userDaoImplClazz = Class.forName(userDaoClass);
//反射创建对象
userDao = (UserDao) userDaoImplClazz.newInstance();
} catch (Throwable e) {
throw new ExceptionInInitializerError(e);
}
}
public static DaoFactory getInstance() {
return daoFactory;
}
public UserDao getUserDao() {
return userDao;
}
}
daoconfig.properties
userDao = com.test.crudrefactorfinal.dao.UserDaoImpl2
DAOTest
public class DAOTest {
public static void main(String[] args) {
//通过工厂得到DAO实现类,如果想换成UserDaoImpl2,修改配置即可
UserDao userDao = DaoFactory.getInstance().getUserDao();
List<User> users = userDao.selectUsers(18);
for (User user : users) {
System.out.println(user);
}
}
}
UserDao
public interface UserDao {
int addUser(User user);
int update(User user);
int delete(User user);
User getUser(Long Id);
User findUser(String name, Integer age);
//新增查询方法:根据年龄查询
//MyJDBCTemplate中query的返回值设置成List存在局限性。如果用户想映射出Map呢?所以用Object最好
List<User> selectUsers(Integer age);
}
UserDaoImpl2
public class UserDaoImpl2 implements UserDao {
MyJDBCTemplate jdbcTemplate = new MyJDBCTemplate();
//增
public int addUser(User user) {
String sql = "insert into t_user(name, age, birthday) values (?,?,?) ";
Object[] args = new Object[]{user.getName(), user.getAge(),
user.getBirthday()};
//调用jdbcTemplate的update方法
return jdbcTemplate.update(sql, args);
}
//删
public int update(User user) {
String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
Object[] args = new Object[]{user.getName(), user.getAge(),
user.getBirthday(), user.getId()};
//调用jdbcTemplate的update方法
return jdbcTemplate.update(sql, args);
}
//改
public int delete(User user) {
String sql = "delete from t_user where id=?";
Object[] args = new Object[]{user.getId()};
//调用jdbcTemplate的update方法
return jdbcTemplate.update(sql, args);
}
public User getUser(Long id) {
String sql = "select id, name, age, birthday from t_user where id=?";
Object[] args = new Object[]{id};
//调用jdbcTemplate的query方法
// List list = jdbcTemplate.query(sql, args, new RowMapper() {
// public Object mapRow(ResultSet rs) throws SQLException {
// User user = new User();
// user.setId(rs.getInt("id"));
// user.setAge(rs.getInt("age"));
// user.setName(rs.getString("name"));
// user.setBirthday(rs.getDate("birthday"));
// return user;
// }
// });
// return (User)list.get(0);
// }
//调用jdbcTemplate的query方法
Object query = jdbcTemplate.query(sql, args, new BeanHandler(user.class));
return (User)query;
public User findUser(String name, Integer age) {
String sql = "select id, name, age, birthday from t_user where name=? and age=?";
Object[] args = new Object[]{name, age};
//调用jdbcTemplate的query方法
// List list = jdbcTemplate.query(sql, args, new RowMapper() {
// public Object mapRow(ResultSet rs) throws SQLException {
// User user = new User();
// user.setId(rs.getInt("id"));
// user.setAge(rs.getInt("age"));
// user.setName(rs.getString("name"));
// user.setBirthday(rs.getDate("birthday"));
// return user;
// }
// });
// return (User)list.get(0);
// }
Object query = jdbcTemplate.query(sql, args, new BeanHandler(user.class));
return (User)query;
public List selectUsers(Integer age) {
String sql = "select id, name, age, birthday from t_user where age=?";
Object[] args = new Object[]{age};
// new BeanHandler,每次都让用户自己写一个匿名内部类实在太烦了,而且findUser和getUser方法返回值都是User,会重复。返回值类型其实是可以穷举的,比如单个Bean,List<Bean>、Map、List<Map>等。我们预先定义几个映射器供用户使用.
Object query = jdbcTemplate.query(sql, args, new BeanHandler(user.class));
return (List)query;
}
}
RowMapper
public interface ResultSetHandler {
//映射结果集
Object handler(ResultSet rs) ;
}
public class MyJDBCTemplate {
// 增删改
public int update(String sql, Object...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// sql由调用者传入
ps = conn.prepareStatement(sql);
// 遍历设置模板参数
for (int i = 0; i < args.length; i++){
ps.setObject(i + 1, args[i]);
}
return ps.executeUpdate();
} catch (SQLException e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
//查询
public Object query(String sql, Object[] args, ResultSetHandler resultSetHandler) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// sql由调用者传入
ps = conn.prepareStatement(sql);
// 遍历设置模板参数
for (int i = 0; i < args.length; i++)
ps.setObject(i + 1, args[i]);
rs = ps.executeQuery();
// 映射规则由子类传入
//MyJDBCTemplate中query的返回值设置成List存在局限性。如果用户想映射出Map呢?所以用Object最好
return resultSetHandler.handler(rs);
} catch (SQLException e) {
throw new DaoException(e.getMessage(), e);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
}
Listener
注解
public @interface 注解名称{
属性列表;
}
public interface 注解名称 extends Annotation{
属性列表;
}
ThreadLocal
ThreadLocal其实不存东西,ThreadLocalMap的key也不是Thread,是ThreadLocal。
title: “Spring note” date: 2021-06-12T15:56:39+08:00 Description: “learning note of Spring 5” Tags: [ “IOC”,“AoP”,“JdbcTemplate”,“事务管理”, “Spring5 framework” ] Categories: [] DisableComments: false
Spring JDBC ORM
模板方法,建造者模式
st=>start: Start:>http://www.google.com[blank]
e=>end:>http://www.google.com
op1=>operation: My Operation
sub1=>subroutine: My Subroutine
cond=>condition: Yes
or No?:>http://www.google.com
io=>inputoutput: catch something...
para=>parallel: parallel tasks
st->op1->cond
cond(yes)->io->e
cond(no)->para
para(path1, bottom)->sub1(right)->op1
para(path2, top)->op1
本地 branch 和 远程 branch 挂钩错误
PS F:\笔记\Spring\minSpring> git status On branch InstanStrtg Your branch is up to date with ‘origin/BeanDefinition-BeanDefinitionRegistry’