簡介、耦合、控制反轉、依賴注入、註解方式反轉控制和依賴注入、Spring整合Junit、銀行轉賬案例、代理、AOP面向切面程式設計、JDBCTemplate
簡介
1
核心內容
IOC反向控制、AOP面向切面程式設計
2
優勢
方便解耦,簡化程式設計
AOP程式設計支援
宣告式事務支援
方便程式的測試
方便整合各種優秀的框架
降低JAVAEE API的使用難度
JAVA原始碼是經典的學習範例
3
四大核心容器
Beans、Core、Context、SpEL
耦合
程式間的依賴關係稱為耦合。解耦就是降低程式間的依賴關係。
1
解耦案例一
DriverManager,registeDriver(new com.mysql.jdbc.Driver());
//編譯依賴
class.forName("com.mysql.jdbc.Driver")
//執行依賴
//分析:如果刪除掉com.mysql.jdbc.Driver檔案,前者在編譯時報錯,後者在執行時報錯
避免使用new關鍵字,透過讀取配置檔案來獲取要建立物件的全類名,再使用反射來建立物件
2
Bean的概念
Bean表示可重複使用的元件
JavaBean表示java語言編寫的重複使用的元件
3
工廠模式案例
1)Bean.properties
service1=services.service1
2)BeanFactory.java
package client;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
public class BeanFactory {
static private Properties properties;
static private HashMap<String,Object> map;
static {
try {
//讀取配置檔案
InputStream in=BeanFactory.class.getClassLoader ().getResourceAsStream ("Bean.properties");
properties=new Properties ();
properties.load (in);
//建立例項並放入容器中
Enumeration keys=properties.keys ();
map=new HashMap<String, Object> ();
while (keys.hasMoreElements ()){
String key=keys.nextElement ().toString ();
Object obj=Class.forName (properties.getProperty (key)).newInstance ();
map.put (key,obj);
}
} catch (IOException e) {
e.printStackTrace ( );
} catch (IllegalAccessException e) {
e.printStackTrace ( );
} catch (InstantiationException e) {
e.printStackTrace ( );
} catch (ClassNotFoundException e) {
e.printStackTrace ( );
}
}
public static Object getBean(String beanname){
return map.get (beanname);
}
}
3)clientMain
package client;
import services.service1;
public class clientMain {
public static void main(String[] args) {
for (int i = 0; i <5 ; i++) {
service1 s=(service1) BeanFactory.getBean ("service1");
System.out.println (s);
s.service ();
}
}
}
//執行的是同一個例項的方法
控制反轉
把建立物件的權力交給框架,此過程稱為控制反轉,是框架的重要特性,包括依賴注入、依賴查詢
1
Spring IOC的基本使用
1)pom檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zhanghuan</groupId>
<artifactId>sprignioc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency><!--匯入相關依賴-->
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
</project>
2)beans配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="service1" class="services.service1"></bean>
</beans>
3)client.java
package client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import services.service1;
public class clientMain {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext ("beans.xml");
//建立Bean物件方式一
service1 s=(service1)ac.getBean ("service1");
//建立Bean物件方式二
service1 s1=ac.getBean ("service1",service1.class);
//總之,透過xml檔案配置的id獲取bean
s.service ();
s1.service ();
}
}
2
ApplicationContext介面的3種實現類
實現類 | 傳入配置檔案特點 |
---|---|
ClassPathXmlApplicationContext | 配置檔案在類路徑下 |
FileSystemXmlApplicationContext | 配置檔案在磁碟任意未知 |
AnnotationConfigApplicationContext | 無配置檔案,配置資訊透過註解設定 |
3
核心容器的介面
介面名 | 特點 |
---|---|
ApplicationContext | 讀取完配置檔案,立即建立物件 |
BeanFactory | 讀取完配置檔案,延遲建立物件 |
常用的是ApplicationContext,可自動根據不同情況實現單例或多例模式
4
建立Bean的三種方式
1)預設建構函式建立
<!--省略部分程式碼-->
<bean id="service1" class="services.service1"></bean>
如果物件無預設建構函式,則無法建立例項
2)使用工廠普通方法建立
package services;
//這是工廠類
public class serviceFactory {
service1 build_service(){
return new service1 ();
}
}
<!--省略部分程式碼-->
<bean id="serviceFactory" class="services.serviceFactory"></bean>
<bean id="service_from_factory1" factory-bean="serviceFactory" factory-method="build_service"></bean>
3)使用工廠靜態方法建立
package services;
//這是工廠類
public class serviceStaticFactory {
static service1 build_service(){
return new service1 ();
}
}
<!--省略部分程式碼-->
<bean id="serviceStaticFactory" class="services.serviceStaticFactory" factory-method="build_service"></bean>
5
bean標籤的scope屬性
屬性名 | 說明 |
---|---|
singleton | 單例 |
prototype | 多例 |
request | 作用於web應用的請求範圍 |
session | 作用於會話的請求範圍 |
global-session | 作用於叢集會話的請求範圍 |
依賴注入
依賴關係的維護交給spring來做,此過程稱為依賴注入。(建立物件的過程中,將引數傳入,引數可能是其他物件,使得物件間產生依賴)
1
可注入的類
基本型別和String型別
其他bean(配置檔案中配置過的或註解配置過的bean)
複合型別/集合型別
注意
經常變化的不要注入
2
注入方式
1)構造方法注入
<bean id="student" class="doMain.Student">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="male" value="男"></constructor-arg>
<constructor-arg name="name" value="zhanghuan"></constructor-arg>
<constructor-arg name="math" value="95"></constructor-arg>
<constructor-arg name="english" value="100"></constructor-arg>
</bean>
需要注意的是,doMain.Student類裡必須要有對應引數的構造方法,否則無法注入,其本質是呼叫構造方法注入
constructor-arg標籤的屬性
屬性 | 解釋 |
---|---|
type | 要注入的引數的型別 |
index | 要注入的引數的下標 |
name | 要注入的引數的引數名 |
value | 要注入的引數的值,支援字串和數字 |
ref | 要注入的引數的值,支援配置檔案中的其他bean |
2)setter方法注入
<bean id="student" class="doMain.Student">
<property name="id" value="1"></property>
<property name="male" value="男"></property>
<property name="name" value="zhanghuan"></property>
<property name="math" value="95"></property>
<property name="english" value="100"></property>
</bean>
property 標籤的屬性
屬性 | 解釋 |
---|---|
name | 要注入的引數的引數名 |
value | 要注入的引數的值,支援字串和數字 |
ref | 要注入的引數的值,支援配置檔案中的其他bean |
複雜型別透過setter方法注入
package doMain;
import java.util.List;
import java.util.Map;
//目標bean
public class LoveYou {
private List<Student> students;
private Map<String,String> map;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
@Override
public String toString() {
return "LoveYou{" +
"students=" + students +
", map=" + map +
'}';
}
}
<bean id="loveyou" class="doMain.LoveYou">
<property name="students">
<list>
<ref bean="student"/>
<ref bean="student"/>
</list>
</property>
<property name="map">
<map>
<entry key="name" value="zhanghuan"></entry>
</map>
</property>
</bean>
列表型別資料可選標籤:list,array,set
鍵值對資料可選標籤:map,props
註解方式反轉控制和依賴注入
1
xml方式和註解方式對比
功能 | xml配置檔案 | 註解 |
---|---|---|
建立物件 | bean標籤 | Component,Controller,Service,Repository |
注入依賴(資料) | property標籤 | Autowired,Quqlifier,Resource,Value |
改變作用範圍 | scope屬性 | Scope |
生命週期相關 | init-method屬性 | Postconstruct |
destroy-method屬性 | PreDestroy |
2
各註解功能
1)Component註解
用於把當前類物件存入spring容器中,屬性value設定該bean的id,如果不設定預設為該類的小寫名。Controller,Service,Repository和該註解的功能一樣。
使用前需要在配置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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="doMain"></context:component-scan>
<!--掃描資料夾,將裡的類例項化後放入容器-->
</beans>
2)注入bean
(1)Autowired註解
指定注入依賴,可配置在類的變數上,也可配置在類的方法上。在變數上配置該註解後,該變數的setter方法可省去。注入的前提是,容器中有唯一一個bean物件和要注入的變數型別匹配,否則導致注入失敗而報錯。
(2)Qualifier註解
不可以單獨使用,必須和Autowored合用,value屬性值設定注入bean的id
(3)Resource註解
可單獨使用,name屬性值設定注入bean的id
注意
以上3中方式只可注入bean型別,無法注入基本型別和String型別,集合型別只能透過xml方式注入
3)注入基本型別和String型別
Value註解
其value屬性值為要注入的基本型別和String型別資料,可以使用spring中的SpEl表示式
4)Scope註解
其value屬性值為singleton和prototype,控制類的使用方式是單例還是多例
5)生命週期相關注解
Postconstruct註解和PreDestroy註解,主要在單例模式下使用,value值為指定的類的構造方法和析構方法
3
資料庫增刪改查案例
1)pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zhanghuan</groupId>
<artifactId>annotationspring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
2)StudentDaoImpl.java
package com.rootpackage.dao;
import com.rootpackage.doMain.Student;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
import java.util.List;
@Repository("studentdao")
public class StudentDaoImpl implements StudentDao {
@Autowired
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public void insertOne(Student student) {
try {
runner.update ("insert into student (male,name,math,english) values(?,?,?,?)",student.getMale (),student.getName (),student.getMath (),student.getEnglish ());
} catch (SQLException e) {
e.printStackTrace ( );
}
}
public void deleteOne(int id) {
try {
runner.update ("delete from student where id=?",id);
} catch (SQLException e) {
e.printStackTrace ( );
}
}
public void updateOne(Student student) {
try {
runner.update ("update student set male=?,name=?,math=?,english=? where id=?",student.getMale (),student.getName (),student.getMath (),student.getEnglish (),student.getId ());
} catch (SQLException e) {
e.printStackTrace ( );
}
}
public Student findOne(int id) {
try {
return runner.query ("select * from student where id=?",new BeanHandler<Student> (Student.class),id);
} catch (SQLException e) {
throw new RuntimeException ();
}
}
public List<Student> findAll() {
try {
return runner.query ("select * from student",new BeanListHandler<Student> (Student.class));
} catch (SQLException e) {
throw new RuntimeException ();
}
}
}
3)StudentServiceImpl.java
package com.rootpackage.services;
import com.rootpackage.dao.StudentDao;
import com.rootpackage.doMain.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("studentserviceimpl")
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao dao;
public void insertOne(Student student) {
dao.insertOne (student);
}
public void deleteOne(int id) {
dao.deleteOne (id);
}
public void updateOne(Student student) {
dao.updateOne (student);
}
public Student findOne(int id) {
return dao.findOne (id);
}
public List<Student> findAll() {
return dao.findAll ();
}
}
4)beans.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.rootpackage"></context:component-scan>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="datasource"></constructor-arg>
</bean>
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
</beans>
5)test01.java
import com.rootpackage.doMain.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.rootpackage.services.StudentServiceImpl;
import java.util.List;
public class test01 {
private StudentServiceImpl s=null;
@Before
public void init(){
ApplicationContext ac=new ClassPathXmlApplicationContext ("beans.xml");
s=ac.getBean ("studentserviceimpl",StudentServiceImpl.class);
}
@Test
public void findal(){
List<Student> list=s.findAll ();
for (Student i:
list) {
System.out.println (i);
}
}
@Test
public void insertone(){
Student student=new Student ();
student.setMale ("男");
student.setName ("sylvester");
student.setMath (85);
student.setEnglish (90);
s.insertOne (student);
}
@Test
public void deleteone(){
s.deleteOne (13);
}
@Test
public void updateone(){
Student student=new Student ();
student.setId (15);
student.setMale ("男");
student.setName ("sylvester1");
student.setMath (85);
student.setEnglish (90);
s.updateOne (student);
}
@Test
public void findone(){
Student student=s.findOne (15);
System.out.println (student);
}
}
4
配置類
1)Configuration註解
該註解標誌的類表示是一個配置類
2)ComponentScan註解
指定建立容器掃描的包,value值為要掃描的包的位置
3)Bean註解
修飾方法,該方法的返回值將會被存入spring的ioc容器中
//註解配置方式建立容器
ApplicationContext ac=new AnnotationConfigApplicationContext (springConfig.class,jdbcConfig.class);//這裡可傳入多個位元組碼檔案,傳入的位元組碼檔案對應的類的Configuration註解可省略
s=ac.getBean ("studentserviceimpl",StudentServiceImpl.class);
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.rootpackage")
public class springConfig {
}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
public class jdbcConfig {
@Bean
public DataSource createDataSource(){
try {
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass ("com.mysql.jdbc.Driver");
ds.setJdbcUrl ("jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8");
ds.setUser ("root");
ds.setPassword ("");
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException (e);
}
}
@Bean
public QueryRunner createQueryRunner(DataSource ds){
return new QueryRunner (ds);
}
}
4)Import註解
在一個配置類中,匯入另一個配置類,一般是父配置類v匯入子配置類。value值為目標配置類的位元組碼檔案
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("com.rootpackage")
@Import (jdbcConfig.class)//這裡將子配置檔案匯入
public class springConfig {
}
ApplicationContext ac=new AnnotationConfigApplicationContext (springConfig.class);//建立容器時只需將父配置檔案的位元組碼檔案匯入即可
5)Scope註解
value值標記例項是單例的還是多例的
6)PropertySource註解
將xxx.properties檔案讀取到spring的環境變數中,在變數上用@Value("${變數名}")可取到變數值
#duid.properties
# 注意:使用者這裡的變數是user,不可用username,username預設是admin無法使用
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
user=root
password=
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.rootpackage")
@Import (jdbcConfig.class)
@PropertySource("classpath:druid.properties")//將properties檔案讀取到spring的環境中
public class springConfig {
}
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
public class jdbcConfig {
@Value ("${driverClassName}")//spEl表示式將變數讀取到這裡,並注入給變數
private String driverClassName;
@Value ("${url}")
private String url;
@Value ("${user}")
private String user;
@Value ("${password}")
private String password;
@Bean
public DataSource createDataSource(){
try {
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass (driverClassName);
ds.setJdbcUrl (url);
ds.setUser (user);
ds.setPassword (password);
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException (e);
}
}
@Bean
public QueryRunner createQueryRunner(DataSource ds){
return new QueryRunner (ds);
}
}
7)Qualifier註解的另一種用法
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
public class jdbcConfig {
@Value ("${driverClassName}")
private String driverClassName;
@Value ("${url}")
private String url;
@Value ("${user}")
private String user;
@Value ("${password}")
private String password;
@Bean("ds1")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass (driverClassName);
ds.setJdbcUrl (url);
ds.setUser (user);
ds.setPassword (password);
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException (e);
}
}
@Bean("ds2")
public DataSource createDataSource1(){
try {
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass (driverClassName);
ds.setJdbcUrl (url);
ds.setUser (user);
ds.setPassword (password);
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException (e);
}
}
//這裡有兩個datasource分別是ds1和ds2,預設情況下會無法注入而報錯
@Bean
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource ds){
//此處的@Qualifier指定將ds1注入,就不會報錯了
return new QueryRunner (ds);
}
}
Spring整合Junit
預設情況,測試時需要建立容器,獲取容器中要測試的物件。為了方便,我們想在測試時,也想讓程式自動建立容器且自動注入物件,需要使用Spring的整合Junit的依賴
1)pom.xml中匯入依賴
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
注意兩個依賴的版本,這裡版本不對會報錯
2)測試類上使用註解建立容器和自動注入
import com.rootpackage.doMain.Student;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import com.rootpackage.services.StudentServiceImpl;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;;
import java.util.List;
@RunWith (SpringJUnit4ClassRunner.class)//這個註解表示這個測試類是整合spring的
//@ContextConfiguration(classes=springConfig.class)
@ContextConfiguration(locations = "classpath:beans.xml")//建立容器,將配置檔案匯入,支援兩種配置檔案
public class test01 {
@Autowired
private StudentServiceImpl s=null;
@Test
public void findal(){
List<Student> list=s.findAll ();
for (Student i:
list) {
System.out.println (i);
}
}
@Test
public void insertone(){
Student student=new Student ();
student.setMale ("男");
student.setName ("sylvester");
student.setMath (85);
student.setEnglish (90);
s.insertOne (student);
}
@Test
public void deleteone(){
s.deleteOne (13);
}
@Test
public void updateone(){
Student student=new Student ();
student.setId (15);
student.setMale ("男");
student.setName ("sylvester1");
student.setMath (85);
student.setEnglish (90);
s.updateOne (student);
}
@Test
public void findone(){
Student student=s.findOne (15);
System.out.println (student);
}
}
銀行轉賬案例
分析:轉賬過程中涉及事務操作,業務層包含持久層的多個操作,而者多個操作預設情況是多個事務,轉賬過程執行中報錯易導致總資料量發生變化。為解決這個問題,我們使用Threadlocal將資料庫的Connection存放進去,在業務層執行時,直接透過業務層的Threadlocal獲取出Connection,開啟事務進行一些列操作。
1
目錄結構
-main
-java
-com
-rootpackage
-dao
--accountDao
--accountDaoImpl
-doMain
--Account
-service
--AccountService
--AccountServiceImpl
-utils
--ConnectionUtils
--TransactionManager
-config
--jdbcConfig
--SpringConfig
-resources
--druid.properties
-test
-java
--test1
2
檔案程式碼
package com.rootpackage.dao;
import com.rootpackage.doMain.Account;
import java.util.List;
public interface accountDao {
List<Account> findAll();
void addOme(Account account);
void deleteOne(int id);
void updateOne(Account account);
Account findOne(int id);
}
package com.rootpackage.dao;
import com.rootpackage.doMain.Account;
import com.rootpackage.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
import java.util.List;
@Component
public class accountDaoImpl implements accountDao {
@Autowired
private QueryRunner runner;
@Autowired
private ConnectionUtils connectionUtils;
public List<Account> findAll() {
try {
return runner.query (connectionUtils.getThreadConnection (),"select * from account",new BeanListHandler<Account> (Account.class));
} catch (SQLException e) {
throw new RuntimeException (e);
}
}
public void addOme(Account account) {
try {
runner.update (connectionUtils.getThreadConnection (),"insert into account (name,money) values (?,?)",account.getName (),account.getMoney ());
} catch (SQLException e) {
throw new RuntimeException (e);
}
}
public void deleteOne(int id) {
try {
runner.update (connectionUtils.getThreadConnection (),"delete from account where id=?",id);
} catch (SQLException e) {
throw new RuntimeException (e);
}
}
public void updateOne(Account account) {
try {
runner.update (connectionUtils.getThreadConnection (),"update account set name=?,money=? where id=?",account.getName (),account.getMoney (),account.getId ());
} catch (SQLException e) {
throw new RuntimeException (e);
}
}
public Account findOne(int id) {
try {
return runner.query (connectionUtils.getThreadConnection (),"select * from account where id=?",new BeanHandler<Account> (Account.class),id);
} catch (SQLException e) {
throw new RuntimeException (e);
}
}
}
package com.rootpackage.doMain;
public class Account {
private int id;
private String name;
private int money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
package com.rootpackage.service;
import com.rootpackage.doMain.Account;
import java.util.List;
public interface AccountService {
List<Account> findAll();
void addOme(Account account);
void deleteOne(int id);
void updateOne(Account account);
Account findOne(int id);
void transfer(int id1,int id2,int money);
}
package com.rootpackage.service;
import com.rootpackage.doMain.Account;
import com.rootpackage.utils.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import com.rootpackage.dao.accountDaoImpl;
@Component
public class AccountServiceImpl implements AccountService {
@Autowired
private accountDaoImpl dao;
@Autowired
private TransactionManager manager;
public List<Account> findAll() {
try {
manager.beginTransaction ();
List<Account> list=dao.findAll ();
manager.commit ();
return list;
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
public void addOme(Account account) {
try {
manager.beginTransaction ();
dao.addOme (account);
manager.commit ();
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
public void deleteOne(int id) {
try {
manager.beginTransaction ();
dao.deleteOne (id);
manager.commit ();
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
public void updateOne(Account account) {
try {
manager.beginTransaction ();
dao.updateOne (account);
manager.commit ();
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
public Account findOne(int id) {
try {
manager.beginTransaction ();
Account a=dao.findOne (id);
manager.commit ();
return a;
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
public void transfer(int id1, int id2, int money) {
try {
manager.beginTransaction ();
Account a1=dao.findOne (id1);
Account a2=dao.findOne (id2);
a1.setMoney (a1.getMoney ()-money);
a2.setMoney (a2.getMoney ()+money);
dao.updateOne (a1);
int a=10/0;//這裡會報錯,但是會進行回滾,資料總量不會發生改變
dao.updateOne (a2);
manager.commit ();
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
}
package com.rootpackage.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 ConnectionUtils {
private ThreadLocal<Connection> t1=new ThreadLocal<Connection> ();
//一個執行緒內部的儲存類,可以在指定執行緒記憶體儲資料,資料儲存以後,只有指定執行緒可以得到儲存資料
@Autowired
private DataSource dataSource;
public Connection getThreadConnection(){
try {
Connection con=t1.get ();
if(con==null){
con=dataSource.getConnection ();
t1.set (con);
}
return con;
} catch (SQLException e) {
throw new RuntimeException (e);
}
}
public void removeConnection(){
t1.remove ();
}
}
package com.rootpackage.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
@Component
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
public void beginTransaction(){
try {
connectionUtils.getThreadConnection ().setAutoCommit (false);
} catch (SQLException e) {
e.printStackTrace ( );
}
}
public void commit(){
try {
connectionUtils.getThreadConnection ().commit ();
} catch (SQLException e) {
e.printStackTrace ( );
}
}
public void rollback(){
try {
connectionUtils.getThreadConnection ().rollback ();
} catch (SQLException e) {
e.printStackTrace ( );
}
}
public void release(){
connectionUtils.removeConnection ();
}
}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
public class jdbcConfig {
@Value ("${driverClassName}")
private String driverClassName;
@Value ("${url}")
private String url;
@Value ("${user}")
private String user;
@Value ("${password}")
private String password;
@Bean
public DataSource createdatasource(){
try {
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass (driverClassName);
ds.setJdbcUrl (url);
ds.setUser (user);
ds.setPassword (password);
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException (e);
}
}
@Bean
public QueryRunner createqueryrunner(DataSource ds){
return new QueryRunner (ds);
}
}
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.rootpackage")
@PropertySource("classpath:druid.properties")
@Import (jdbcConfig.class)
public class SpringConfig {
}
#duid.properties
# 注意:使用者這裡的變數是user,不可用username,username預設是admin無法使用
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
user=root
password=
import com.rootpackage.doMain.Account;
import com.rootpackage.service.AccountServiceImpl;
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 config.SpringConfig;
import java.util.List;
@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class test1 {
@Autowired
private AccountServiceImpl service;
@Test
public void findall(){
List<Account> list=service.findAll ();
for (Account a:
list) {
System.out.println (a);
}
}
@Test
public void transfer(){
service.transfer (1,2,500);
}
}
代理
1
jdk中的代理類
java在JDK1.5之後提供了一個"java.lang.reflect.Proxy"類,透過"Proxy"類提供的一個newProxyInstance方法用來建立一個物件的代理物件
//部分程式碼
final computermaker m=new computermaker ();//該被代理物件必須實現一個介面,並且final關鍵字修飾
saler n=(saler) Proxy.newProxyInstance (m.getClass ( ).getClassLoader ( ), m.getClass ().getInterfaces (), new InvocationHandler ( ) {
/*
ClassLoader 類載入器,讓代理物件和被代理物件使用相同的類載入器
Class[] 位元組碼陣列,讓代理物件和被代理物件有相同的方法
InvocationHandler 需要建立一個該介面的匿名內部類,並重寫invoke方法以增強
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke (m,args);//注意:第一個引數是被代理物件,最後一個引數是該方法傳過來的引數
}
});
n.sale ();
2
第三方代理類
由於jdk提供的代理要求被代理物件必須實現介面,對於沒有實現介面的被代理物件可使用第三方代理類
1)pom.xml匯入依賴
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2)部分程式碼
final computermaker m=new computermaker ();
computermaker n=(computermaker)Enhancer.create (m.getClass ( ), new MethodInterceptor ( ) {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return method.invoke (m,objects);
}
});
n.sale ();
3
轉賬案例改用代理
1)AccountServiceImpl修改簡化
package com.rootpackage.service;
import com.rootpackage.doMain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import com.rootpackage.dao.accountDaoImpl;
@Component("accountservice")
public class AccountServiceImpl implements AccountService {
@Autowired
private accountDaoImpl dao;
public List<Account> findAll() {
List<Account> list=dao.findAll ();
return list;
}
public void addOme(Account account) {
dao.addOme (account);
}
public void deleteOne(int id) {
dao.deleteOne (id);
}
public void updateOne(Account account) {
dao.updateOne (account);
}
public Account findOne(int id) {
Account a=dao.findOne (id);
return a;
}
public void transfer(int id1, int id2, int money) {
Account a1=dao.findOne (id1);
Account a2=dao.findOne (id2);
a1.setMoney (a1.getMoney ()-money);
a2.setMoney (a2.getMoney ()+money);
dao.updateOne (a1);
int a=9/0;
dao.updateOne (a2);
}
}
2)新建代理工廠
package com.rootpackage.factory;
import com.rootpackage.service.AccountService;
import com.rootpackage.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class BeanFactory {
@Autowired
@Qualifier("accountservice")
private AccountService accountService;
@Autowired
private TransactionManager manager;
public AccountService getAccountService(){
return (AccountService)Enhancer.create (accountService.getClass ( ), new MethodInterceptor ( ) {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result=null;
try {
manager.beginTransaction ();
result=method.invoke (accountService,objects);
manager.commit ();
return result;
} catch (Exception e) {
manager.rollback ();
throw new RuntimeException (e);
} finally {
manager.release ();
}
}
});
}
}
3)測試類
import com.rootpackage.doMain.Account;
import com.rootpackage.factory.BeanFactory;
import com.rootpackage.service.AccountServiceImpl;
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 config.SpringConfig;
import java.util.List;
@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class test1 {
@Autowired
private BeanFactory factory;
@Test
public void findall(){
List<Account> list=factory.getAccountService ().findAll ();//透過代理工廠獲取AccountService物件
for (Account a:
list) {
System.out.println (a);
}
}
@Test
public void transfer(){
factory.getAccountService ().transfer (1,2,500);
}
}
AOP面向切面程式設計
Aspect Oriented Programming,是spring框架中一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可對業務邏輯各個部分進行隔離,從而使業務邏輯各個部分間耦合度降低。
特點:在程式執行期間,不改變原始碼對已有方法進行增強
優勢:減少重複程式碼,提高開發效率,維護方便
1
常用名詞
名詞 | 英文 | 解釋 |
---|---|---|
連線點 | joinpoint | 已經被增強的掛載點,如增強的方法 |
切入點 | pointcut | 所有可以被攔截的點(方法) |
通知 | advice | 攔截到切入點後要做的事情 |
映入 | introduction | 執行期動態新增的方法或Field |
目標物件 | target | 代理的目標物件 |
織入 | weaving | 增強應用到目標物件來建立新的代理物件的過程 |
代理 | proxy | 一個類被aop增強後的一個結果類 |
切面 | aspect | 切入點和通知的結合 |
2
快速開始
1)pom.xml匯入依賴
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
2)定義一個服務類
package com.rootpackage.services;
public class service1 {
public void doservice(){
System.out.println ("開始服務……");
}
}
3)定義一個日誌類
package com.rootpackage.mout;
public class log {
public void dolog1(){
System.out.println ("列印日誌1");
}
public void dolog2(){
System.out.println ("列印日誌2");
}
public void dolog3(){
System.out.println ("列印日誌3");
}
public void dolog4(){
System.out.println ("列印日誌4");
}
}
4)beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="service1" class="com.rootpackage.services.service1"></bean>
<bean id="log" class="com.rootpackage.mout.log"></bean>
<aop:config>
<aop:aspect id="asp1" ref="log">
<!--id是給這個切面的id,ref指向通知類(放入切入點的方法所屬的類)-->
<aop:before method="dolog1" pointcut="execution(* *..*.doservice(..))"></aop:before>
<!--method是通知方法的方法名,pointcut切入點-->
<aop:after-returning method="dolog2" pointcut="execution(* *..*.doservice(..))"></aop:after-returning>
<aop:after-throwing method="dolog3" pointcut="execution(* *..*.doservice(..))"></aop:after-throwing>
<aop:after method="dolog4" pointcut="execution(* *..*.doservice(..))"></aop:after>
</aop:aspect>
</aop:config>
</beans>
5)測試類
import com.rootpackage.services.service1;
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:beans.xml" )
public class test1 {
@Autowired
service1 sv1;
@Test
public void test1(){
sv1.doservice ();
}
}
3
execution指定切點的格式
訪問修飾符 返回值 包名 類名 .方法名(引數型別)
格式 | 萬用字元 | 說明 |
---|---|---|
訪問修飾符 | * | |
返回值 | * | |
包名 | *,.. | |
類名 | * | |
方法名 | * | |
引數型別 | *,.. | 直接寫資料型別如int,java.lang.String,可有引數,也可無引數 |
全通配* *..*.*(..)
,但通常情況都不用全同配。切入到業務實現類的所有方法,會有一個問題就是通知內容會多執行一次,是由於構造方法導致的。
4
四種通知型別
關鍵字 | 型別 |
---|---|
before | 前置 |
after-returning | 後置 |
after-throwing | 異常 |
after | 最終 |
5
配置共用切入點
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="service1" class="com.rootpackage.services.service1"></bean>
<bean id="log" class="com.rootpackage.mout.log"></bean>
<aop:config>
<aop:pointcut id="s1" expression="execution(* * ..*.doservice(..))"></aop:pointcut>
<aop:aspect id="asp1" ref="log">
<aop:before method="dolog1" pointcut-ref="s1"></aop:before>
<aop:after-returning method="dolog2" pointcut-ref="s1"></aop:after-returning>
<aop:after-throwing method="dolog3" pointcut-ref="s1"></aop:after-throwing>
<aop:after method="dolog4" pointcut-ref="s1"></aop:after>
</aop:aspect>
</aop:config>
</beans>
6
環繞通知
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="service1" class="com.rootpackage.services.service1"></bean>
<bean id="log" class="com.rootpackage.mout.log"></bean>
<aop:config>
<aop:pointcut id="s1" expression="execution(* * ..*.doservice(..))"></aop:pointcut>
<aop:aspect id="asp1" ref="log">
<aop:around method="doarround" pointcut-ref="s1"></aop:around>
</aop:aspect>
</aop:config>
</beans>
配置環繞通知後,只有通知方法執行,切入點原方法未執行。此時需要在通知方法中傳入ProceedingJoinPoint的實現類(定義該介面的引數,spring自動傳入實現類),再透過該實現類呼叫切入點方法
package com.rootpackage.mout;
import org.aspectj.lang.ProceedingJoinPoint;
public class log {
public void doarround(ProceedingJoinPoint pjp){
try {
System.out.println ("列印日誌1");
Object[] args=pjp.getArgs ();//獲取切入點引數
pjp.proceed (args);//呼叫切入點方法
System.out.println ("列印日誌2");
} catch (Throwable throwable) {
throwable.printStackTrace ( );
System.out.println ("列印日誌3");
} finally {
System.out.println ("列印日誌4");
}
}
}
7
註解
1)Aspect註解
表示該類是一個切面內
2)Before註解
表示該方法是一個前置通知
3)After註解
表示該方法是一個最終通知
4)AfterReturning註解
表示該方法是一個後置通知
5)AfterThrowing註解
表示該方法是一個異常通知
6)Arround註解
表示該方法是一個環繞通知
7)Point註解
定義一個切點,定義在一個方法上,引數是“execution(...)”,引用切面是需要呼叫該方法
在定義切面、通知前需要在配置檔案中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="service1" class="com.rootpackage.services.service1"></bean>
<bean id="log" class="com.rootpackage.mout.log"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--需要配置這一句-->
</beans>
package com.rootpackage.mout;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect//標識該類是一個切面類
public class log {
@Pointcut("execution(* *..*.doservice(..))")//定義切點
public void pf(){}
@Before ("pf()")//定義前置通知
public void dolog1(){
System.out.println ("列印日誌1");
}
@AfterReturning("pf()")
public void dolog2(){
System.out.println ("列印日誌2");
}
@AfterThrowing("pf()")
public void dolog3(){
System.out.println ("列印日誌3");
}
@After ("pf()")
public void dolog4(){
System.out.println ("列印日誌4");
}
}
執行後發現最終通知在後置通知之前執行,不合理。所以建議使用環繞通知
package com.rootpackage.mout;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect//標識該類是一個切面類
public class log {
@Pointcut("execution(* *..*.doservice(..))")//定義切點
public void pf(){}
@Around ("pf()")
public void doarround(ProceedingJoinPoint pjp){
try {
System.out.println ("列印日誌1");
Object[] args=pjp.getArgs ();
pjp.proceed (args);
System.out.println ("列印日誌2");
} catch (Throwable throwable) {
throwable.printStackTrace ( );
System.out.println ("列印日誌3");
} finally {
System.out.println ("列印日誌4");
}
}
}
9)不使用xml配置
此時應該在配置類上加一行註解@EnableAspectJAutoProxy
8
轉賬案例使用切面新增事務
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="org.zhanghuan"></context:component-scan>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<bean class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="ds"></constructor-arg>
</bean>
<aop:config><!--將事務透過切面織入-->
<aop:pointcut id="p1" expression="execution(* org.zhanghuan.service.AccountServiceImpl.*(..))"></aop:pointcut>
<aop:aspect id="isp1" ref="transaction">
<aop:before method="beginTransaction" pointcut-ref="p1"></aop:before>
<aop:after method="release" pointcut-ref="p1"></aop:after>
<aop:after-returning method="commit" pointcut-ref="p1"></aop:after-returning>
<aop:after-throwing method="rollback" pointcut-ref="p1"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
9
DataSourceTransactionManager的使用
Spring的事務處理中,通用的事務處理流程是由抽象事務管理器AbstractPlatformTransactionManager來提供的,而具體的底層事務處理實現,由PlatformTransactionManager的具體實現類來實現
1)配置檔案方式
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="org.zhanghuan"></context:component-scan>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<bean class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="ds"></constructor-arg>
</bean>
<!--匯入事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--這個包在spring-jdbc中,需在maven中匯入-->
<property name="dataSource" ref="ds"></property>
</bean>
<!--配置事務通知-->
<tx:advice id="manager" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
<tx:method name="*find*" propagation="SUPPORTS" read-only="true"></tx:method><!--表示包含find關鍵字的方法,只讀,不一定包含事務-->
</tx:attributes>
</tx:advice>
<!--配置aop,將事務通知織入-->
<aop:config>
<aop:pointcut id="p1" expression="execution(* org.zhanghuan.service.AccountServiceImpl.*(..))"></aop:pointcut>
<aop:advisor advice-ref="manager" pointcut-ref="p1"></aop:advisor>
</aop:config>
</beans>
事務通知裡方法的屬性
屬性名 | 含義 |
---|---|
isolation | 事務隔離級別 |
propagation | 指定事務傳播形式,預設REQUIRED,表示必有事務。對於查詢的方法,使用SUPPORTS |
read-only | 指定事務是否只讀,預設false,查詢方法設定未true |
timeout | 指定事務超時時間,預設未-1,表示永不超時。如果設定值,以秒未單位 |
rollback-for | 指定一個異常,該異常發生,則回滾;其他異常發生,不回滾。無預設值,未設定表示出現任何異常都回滾 |
no-rollback-for | 指定一個異常,該異常發生,不回滾;其他異常發生,回滾。無預設值,未設定表示出現任何異常都回滾 |
2)註解配置方式
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="org.zhanghuan"></context:component-scan>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<bean class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="ds"></constructor-arg>
</bean>
<!--匯入事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--這個包在spring-jdbc中,需在maven中匯入-->
<property name="dataSource" ref="ds"></property>
</bean>
<!--開啟spring對註解事務的支援-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--在需要開啟事務的地方表上註解@Transactional-->
</beans>
在配置檔案中完成以上配置後,在需要開啟事務的地方表上註解@Transactional
3)無xml方式
//配置檔案
package org.zhanghuan.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@Import (jdbcConfig.class)
@ComponentScan("org.zhanghuan")
@EnableTransactionManagement//相當於xml中的tx:annotation-driven標籤+@Transactional註解
public class springConfig {
@Bean//匯入事務管理器
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource ds){
return new DataSourceTransactionManager(ds);
}
}
找到事務實現類,並開啟事務
JDBCTemplate
1
pom.xml匯入依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2
快速使用
//部分程式碼
//建立資料來源
DriverManagerDataSource ds=new DriverManagerDataSource ();
ds.setDriverClass ("com.mysql.jdbc.Driver");
ds.setJdbcUrl ("jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8");
ds.setUser ("root");
ds.setPassword ("");
JdbcTemplate template=new JdbcTemplate (ds);
List<Map<String,Object>> list=template.queryForList ("select * from account");
for (Map<String,Object> m:
list) {
System.out.println ("id:"+m.get ("id"));
}
3
query方法
query(String sql,Objectp[] args,RowMapper<T> rowmapper);//所有版本可用
query(String sql,RowMapper<T> rowmapper,Object.. args);//jdk1.5之後可用