Spring注解的使用
一、項目注解 和 XML 的選擇問題
- 學習基于注解的 IOC 配置,即注解配置 和 XML 配置要實現的功能都是一樣的,都是要降低程序間的耦合。只是配置的形式不一樣。
- 關于實際的開發(fā)中到底使用xml還是注解,每家公司有著不同的使用習慣 , 所以這兩種配置方式我們都需要掌握。
- 把 Spring 的 xml 配置內容改為使用注解逐步實現
二、用于創(chuàng)建對象的注解
1、注解介紹
- @Component
- @Repository
- @Service
- @Controller
注解 | 說明 |
---|---|
@Component | 使用在類上用于實例化Bean |
@Controller | 使用在web層類上用于實例化Bean |
@Service | 使用在service層類上用于實例化Bean |
@Repository | 使用在dao層類上用于實例化Bean |
2、注意問題
- 使用注解開發(fā),需要在application.xml中配置組件掃描。
- 主鍵掃描的作用:指定那個包及其子包的Bean需要掃描以便識別使用注解配置的類、字段和方法。
<?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-3.0.xsd">
<context:component-scan base-package="com.etime"></context:component-scan>
</beans>
3、編寫示例代碼
(1)使用@Component或@Repository標識UserDaoImpl需要Spring進行實例化。
- UserDao接口
package com.etime.dao;
public interface UserDao {
void show();
}
- UserDaoImpl實現類
package com.etime.dao.impl;
package com.etime.dao.impl;
import com.etime.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
//@Component("ud")
@Repository("ud")
public class UserDaoImpl implements UserDao {
@Override
public void show() {
System.out.println("這是userDao的方法");
}
}
(2)使用@Component或@Service標識UserServiceImpl需要Spring進行實例化
- UserService接口
package com.etime.service;
public interface UserService {
void show();
}
- UserServiceImpl
package com.etime.service.impl;
import com.etime.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {
@Override
public void show() {
System.out.println("這是userService的方法");
}
}
(3)測試
@Test
public void t07() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserDao userDao = (UserDao) context.getBean("ud");
userDao.show();
UserService userService = (UserService) context.getBean("us");
userService.show();
}
三、基于注入數據的注解
(1)注解介紹
- @Value
- @Resource
- @Autowrited
- Qualifier
注解 | 說明 |
---|---|
@Value | 注入普通屬性 |
@Autowired | 自動按照類型注入。當使用注解注入屬性時,set 方法可以省略。它只能注入其他 bean 類型。當有多個類型匹配時,使用要注入的對象變量名稱作為 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就報錯。 如果IOC容器當中有多個接口得實現類, 首先根據類型自動裝配, 然后再根據名稱自動裝配。 |
@Qualifier | 結合@Autowired一起使用用于根據名稱進行依賴注入 |
@Resource | 相當于@Autowired+@Qualifier,按照名稱進行注入,是java提供的,不是框架提供的 |
(2)使用@Value進行字符串的注入
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Value("字符串")
private String str;
public void addUser() {
System.out.println(str);
System.out.println("開始新增用戶了");
}
}
(3)使用@Autowired或者@Autowired+@Qulifier或者@Resource進行userDao的注入
第一種:
package com.etime.service.impl;
import com.etime.dao.UserDao;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void show() {
userDao.show();
}
}
第二種:
package com.etime.service.impl;
import com.etime.dao.UserDao;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("ud")
private UserDao userDao;
@Override
public void show() {
userDao.show();
}
}
第三種:
package com.etime.service.impl;
import com.etime.dao.UserDao;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {
@Resource(name = "ud")
private UserDao userDao;
@Override
public void show() {
userDao.show();
}
}
(4)測試
@Test
public void t07() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
/*UserDao userDao = (UserDao) context.getBean("ud");
userDao.show();*/
UserService userService = (UserService) context.getBean("us");
userService.show();
}
四、和生命周期相關的注解
(1)注解介紹
- @PostConstruct
- @PreDestroy
注解 | 說明 |
---|---|
@PostConstruct | 使用在方法上標注該方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上標注該方法是Bean的銷毀方法 |
(2) 使用注解初始化方法、注銷方法
- 這兩個注解是java提供的
package com.etime.dao.impl;
import com.etime.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
//@Component("ud")
@Repository("ud")
public class UserDaoImpl implements UserDao {
@PostConstruct
public void init(){
System.out.println("userDao被初始化了");
}
@Override
public void show() {
System.out.println("這是userDao的方法");
}
@PreDestroy
public void destroy(){
System.out.println("userDao被銷毀了");
}
}
(3)測試
@Test
public void t08() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserDao userDao = (UserDao) context.getBean("ud");
userDao.show();
context.close();
}
五、用戶改變作用范圍的注解:@Scope
1、注解介紹
- 使用@Scope標注Bean的范圍
注解 | 說明 |
---|---|
@Scope | 標注Bean的作用范圍,scope取值singleton prototype request session globalsession |
Spring基于注解的IOC
1、 構建maven工程,添加框架技術依賴
<dependencies>
<!--導入spring的context坐標-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--導入Jdbc模塊依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--導入Mysql 驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--導入C3P0連接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--導入junit單元測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2、創(chuàng)建數據庫及數據表,并創(chuàng)建實體類
- 實體類
package com.etime.entity;
public class User {
private int id;
private String name;
private String gender;
public User() {
}
public User(int id, String name, String gender) {
this.id = id;
this.name = name;
this.gender = gender;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
3、注解配置管理的資源
(1)Dao層
- UseDaor接口
package com.etime.dao;
import com.etime.entity.User;
import java.util.List;
public interface UserDao {
List<User> getAllUser();
}
- USerDaoImpl實現類
package com.etime.dao.impl;
import com.etime.dao.UserDao;
import com.etime.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("ud")
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<User> getAllUser() {
List<User> list = null;
String sql = "select * from user";
list = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(User.class));
return list;
}
}
(2)Service層
- UserService接口
package com.etime.service;
import com.etime.entity.User;
import java.util.List;
public interface UserService {
List<User> getAllUser();
}
- UseServiceImpl實現類
package com.etime.service.impl;
import com.etime.dao.UserDao;
import com.etime.entity.User;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("us")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> getAllUser() {
return userDao.getAllUser();
}
}
4、配置文件并開啟對注解的支持并配置
<?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.etime"/>
<!--讀取properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--JdbcTemplate對象的創(chuàng)建-->
<bean id="jt" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"></property>
</bean>
</beans>
5、編寫測試代碼
@Test
public void t09() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userService = (UserService) context.getBean("us");
List<User> list = userService.getAllUser();
System.out.println(list);
}
Spring純注解配置
1、注解IOC案例待改造的問題
(1)問題引入
- 上面的注解還不能全部替代xml配置文件,還需要使用注解替代
(2)注解替代的配置
- 非自定義的Bean的配置:@Bean
- 加載properties文件的配置:context:property-placeholder
- 組件掃描的配置:context:component-scan
- 引入其他文件:@import
- 替代Spring核心配置文件:@Configuration,@ComponentScan,@Import
2、新注解說明
注解 | 說明 |
---|---|
@Configuration | 用于指定當前類是一個 Spring 配置類,當創(chuàng)建容器時會從該類上加載注解,作用等價于applicationContext.xml 配置文件。 |
@ComponentScan | 用于指定 Spring在初始化容器時要掃描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.offcn”/>一樣 |
@Bean | 使用在方法上,標注將該方法的返回值存儲到 Spring容器中。 id的值默認是方法的名稱, 可以自定義id的值 |
@PropertySource | 用于加載xxx.properties 文件中的配置 結合@Value(“${}”) 取配置文件的值。 |
@Import | 用于導入其他配置類 |
2、編寫示例代碼
(1)創(chuàng)建db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=111
(2)@PropertySource,@value,@Bean的使用:
package com.etime.util;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* 做數據庫相關配置
* 1、讀取properties文件內容
* 2、根據讀取的內容生成連接池對象
* 3、根據連接池對象獲取JdbcTemplate對象
*/
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driverClass);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate() throws PropertyVetoException {
return new JdbcTemplate(getDataSource());
}
}
(3)@Configuration,@ComponentScan,@Import的使用
package com.etime.util;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(DataSourceConfig.class)
@ComponentScan("com.etime")
public class SpringConfig {
}
3、通過注解獲取容器
@Test
public void t10() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = (UserService) context.getBean("us");
List<User> list = userService.getAllUser();
System.out.println(list);
}
Spring整合Junit
1、Spring整合Junit介紹
- Spring整合Junit單元測試在學習階段、開發(fā)階段是經常使用的。
- 如果開發(fā)過程當中完全不使用該技術點,不影響開發(fā)效果,可能會影響開發(fā)進度,為提高開發(fā)效率,避免重復性代碼,Spring整合Junit單元測試務必要掌握,在使用Spring框架開發(fā)時,我們也推薦使用整合Junit后進行測試
2、IOC測試類中的問題和解決思路
(1)問題引出
- 在測試類中,每個測試方法都有以下兩行代碼:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
(2)解決思路
- 這兩行代碼的作用是獲取容器,如果不寫的話,直接會提示空指針異常。所以又不能輕易刪掉。
- 故,引出Spring整合Junit
3、注解介紹
注解 | 說明 |
---|---|
@RunWith | 替換原有運行器 |
@ContextConfiguration | 指定 Spring 配置文件的位置 |
@Autowired | 給測試類中的變量注入數據 |
4、 整合Junit配置步驟
(1) 添加技術依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
(2)使用注解完成容器的加載
@RunWith :替換原有運行器
@ContextConfiguration:指定 Spring 配置文件的位置
@Autowired:給測試類中的變量注入數據
package com.etime.test;
import com.etime.entity.Account;
import com.etime.dao.UserDao;
import com.etime.entity.User;
import com.etime.service.AccountService;
import com.etime.service.UserService;
import com.etime.util.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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})
public class SpringTest {
@Autowired
private ApplicationContext context;
@Test
public void t11() {
UserService userService = (UserService) context.getBean("us");
List<User> list = userService.getAllUser();
System.out.println(list);
}
}
SpringAOP入門
1、什么是 AOP
- AOP 為 Aspect Oriented Programming 的縮寫。
- 意思為面向切面編程,通過 預編譯方式 和 運行期動態(tài)代理實現程序功能的統一維護的一種技術
2、AOP編程思想
- 面向切面編程(AOP)是一種編程的思想,是 OOP 的延續(xù),是軟件開發(fā)中的一個熱點,也是Spring框架中的一個重要內容,是 函數式編程的一種衍生泛型。
- 利用 AOP 可以對業(yè)務邏輯的各個部分進行隔離,,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。
- AOP 使用場景
- 事務管理
- 權限校驗
- 日志記錄
- 性能監(jiān)控
3、SPring中的常用術語
(1)Joinpoint
- Joinpoint(連接點):所謂連接點是指那些被攔截到的點。
- 在spring中,這些點指的是方法,因為spring只支持方法類型的連接點
(2)Pointcut
- Pointcut(切入點):所謂切入點是指我們要對哪些 Joinpoint 進行攔截的定義。
- 真正被增強的方法
(3)Advice
- Advice(通知/ 增強):所謂通知是指攔截到 Joinpoint 之后所要做的事情就是通知。
4、面試問題
(1)通知的類型
- 前置通知,正常返回通知,異常通知,最終通知,環(huán)繞通知。
(2)Introduction
- Introduction(引介,了解): 引介是一種特殊的通知在不修改類代碼的前提下, Introduction 可以在運行期為類動態(tài)地添加一些方 法或 Field。
(3)Target
- Target(目標對象):代理的目標對象
(4)Proxy
- Proxy (代理):一個類被 AOP 織入增強后,就產生一個結果代理類
(5)Weaving
- Weaving(織入):是指把增強應用到目標對象來創(chuàng)建新的代理對象的過程。
- spring采用動態(tài)代理織入,而AspectJ采用編譯期織入和類裝載期織入
(5)Aspect
- Aspect(切面):是切入點和通知(引介)的結合,描述了 增強具體應用的位置。
5、AOP 的作用及優(yōu)勢
(1)作用
- 在程序運行期間,在不修改源碼的情況下對方法進行功能增強。體現了java語言的動態(tài)性【反射】
(2)優(yōu)勢
- 減少重復代碼,提高開發(fā)效率,并且便于維護
Spring實現轉賬案例
一、實現步驟
1、搭建一個轉賬的業(yè)務環(huán)境,準備所需資源,數據庫表以及實體類
2、持久層具體實現使用DBUtils 方便操作連接對象,管理事務
3、搭建業(yè)務層,完成對持久層的調用
4、演示轉賬測試用例,在沒有遇到異常情況下成功轉賬,在轉賬過程當中遇到意外情況,損失了數據的一致性,從而引出事務。
二、編寫代碼示例
1、準備Account表以及對應的實體bean
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
2、創(chuàng)建AccountDao接口以及對應的實現類
- AccountDao接口
package com.etime.dao;
import com.etime.entity.Account;
public interface AccountDao {
public Account getByName(String name);
public int update(Account account);
}
- AccountDaoImpl實現類
package com.etime.dao.impl;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.etime.dao.AccountDao;
import com.etime.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account getByName(String name) {
String sql ="select *from account where name =? ";
return jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Account>(Account.class),name);
}
@Override
public int update(Account account) {
String sql ="update account set money =? where name =? ";
return jdbcTemplate.update(sql,account.getMoney(),account.getName());
}
}
3、創(chuàng)建AccountService接口以及對應的實現類
package com.etime.service;
public interface AccountService {
public void transfer(String sourceAccountName,String targetAccountName,double money);
}
package com.etime.service.impl;
import com.etime.dao.AccountDao;
import com.etime.dao.impl.AccountDaoImpl;
import com.etime.entity.Account;
import com.etime.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String sourceAccountName, String targetAccountName, double money) {
//收錢的
Account tAccount = accountDao.getByName(targetAccountName);
tAccount.setMoney(tAccount.getMoney()+money);
int rows2 = accountDao.update(tAccount);
System.out.println("收錢的:"+rows2);
//給錢的
Account sAccount = accountDao.getByName(sourceAccountName);
sAccount.setMoney(sAccount.getMoney()-money);
int rows1 = accountDao.update(sAccount);
System.out.println("給錢的:"+rows1);
}
}
4、測試轉賬業(yè)務
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfiguration.class})
public class SpringTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void t01(){
AccountService us = (AccountService)applicationContext.getBean("accountService");
us.transfer("aaa","bbb",500);
}
5、測試沒有事務正常情況和異常情況轉賬結果
-
沒有遇到異常情況,轉賬結果正常。aaa賬戶減少500元,bbb賬戶增加500元
-
模擬異常情況轉賬結果aaa向bbb轉賬1000
6、編寫ConnectionUtils工具類管理數據庫連接
-
轉賬過程,由于遇到異常情況,轉賬損失了數據的一致性,加入事務控制。
-
事務要么同時成功,事務提交;要么都同時失敗,事務回滾;要保證咋同一個事務當中進行操作,那么必須保證同一個連接對象。
-
如何保證持久層和業(yè)務層使用同一個連接對象呢?文章來源:http://www.zghlxwxcb.cn/news/detail-437879.html
- 引入ThreadLocal對象,該對象的特點,對象提供的存,取,移除的常用方法。
-
ConnectionUtils 工具類提供的管理連接的方法,就能保證持久層以及業(yè)務層獲得相同的連接對象,保證在同一個事務當中進行操作文章來源地址http://www.zghlxwxcb.cn/news/detail-437879.html
package com.etime.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
@Component
public class ConnectionUtil {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 獲取當前線程上的連接
* @return
*/
public Connection getThreadConnection() {
try{
//1.先從ThreadLocal上獲取
Connection connection = tl.get();
//2.判斷當前線程上是否有連接
if (connection == null) {
//3.從jdbcTemplate中獲取一個連接,并且存入ThreadLocal中
connection = jdbcTemplate.getDataSource().getConnection();
tl.set(connection);
}
//4.返回當前線程上的連接
return connection;
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 把連接和線程解綁
*/
public void removeConnection(){
tl.remove();
}
}
7、編寫事務管理工具類TransactionManager
- TransactionManager 是管理事務的工具類,它主要定義了 和事務相關的方法。
- 例如:例如:事務的開啟,事務的提交,事務的回滾操作等
- 該工具類目前需要手動創(chuàng)建和管理。
- 使用Spring進行事務管理后,該工具類由Spring提供,不需要手動編寫和管理。
- 該工具類重在理解,為Spring進行事務管理做好鋪墊
package com.etime.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TransactionManager {
@Autowired
private ConnectionUtil connectionUtil;
//開啟事務
public void beginTransaction(){
try {
connectionUtil.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
//提交事務
public void commit(){
try {
connectionUtil.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
//回滾事務
public void rollback(){
try {
connectionUtil.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
//釋放資源
public void release(){
try {
connectionUtil.removeConnection();//當前線程解除綁定
connectionUtil.getThreadConnection().close();//還回連接池中
}catch (Exception e){
e.printStackTrace();
}
}
}
8、編寫業(yè)務層及數據訪問層事務控制代碼并配置Spring的IOC
(1)Dao層
package com.etime.dao.impl;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.etime.dao.AccountDao;
import com.etime.entity.Account;
import com.etime.util.ConnectionUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private ConnectionUtil connectionUtil;
@Autowired
private QueryRunner queryRunner;
@Override
public Account getByName(String name) throws SQLException {
Connection conn = connectionUtil.getThreadConnection();
String sql ="select *from account where name =? ";
return queryRunner.query(conn,sql,new BeanHandler<>(Account.class),name);
}
@Override
public int update(Account account) throws SQLException {
Connection conn = connectionUtil.getThreadConnection();
String sql ="update account set money =? where name =? ";
return queryRunner.update(conn,sql,account.getMoney(),account.getName());
}
}
- 數據訪問層用到了QueryRunner,相關配置
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
- SpringConfiguration.java 配置類將QueryRunner對象交給Spring進行管理
@Bean
public QueryRunner getQueryRunner(){
return new QueryRunner();
}
(2)Service層
package com.etime.service.impl;
import com.etime.dao.AccountDao;
import com.etime.entity.Account;
import com.etime.service.AccountService;
import com.etime.util.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired
private TransactionManager transactionManager;
@Override
public void transfer(String sourceAccountName, String targetAccountName, double money) {
try{
//開啟事務
transactionManager.beginTransaction();
//收錢的
Account tAccount = accountDao.getByName(targetAccountName);
tAccount.setMoney(tAccount.getMoney()+money);
int rows2 = accountDao.update(tAccount);
System.out.println("收錢的:"+rows2);
//給錢的
Account sAccount = accountDao.getByName(sourceAccountName);
sAccount.setMoney(sAccount.getMoney()-money);
int rows1 = accountDao.update(sAccount);
System.out.println("給錢的:"+rows1);
//提交事務
transactionManager.commit();
}catch (Exception exception){
//回滾事務
transactionManager.rollback();
exception.printStackTrace();
}finally {
//釋放資源
transactionManager.release();
}
}
}
(3)轉賬測試
package com.etime.test;
import com.etime.entity.User;
import com.etime.service.AccountService;
import com.etime.service.UserService;
import com.etime.service.impl.AccountServiceImpl;
import com.etime.util.DataSourceConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfiguration.class})
public class SpringTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void t01(){
AccountService us = (AccountService)applicationContext.getBean("accountService");
us.transfer("aaa","bbb",2000);
}
}
- 結果:正常轉賬成功,遇到異常情況, 事務進行回滾,保證了數據的一致性。
Spring基于配置文件的AOP
1、環(huán)境搭建
(1)構建maven工程添加依賴
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
(2)代碼編寫
- 實體類、持久層、業(yè)務層使用Spring實現轉賬案例的代碼
(3)創(chuàng)建 Spring 的配置文件并導入約束
<?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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:db.properties</value>
</list>
</property>
</bean>
<!--數據源對象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置JdbcTemplate模板對象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<context:component-scan base-package="com.etime"></context:component-scan>
<bean id="transactionManager" class="com.etime.util.TransactionManager"></bean>
</beans>
2、AOP 配置步驟
(1)使用 aop:confing 聲明 AOP 配置
- 作用: 開始聲明aop配置
<aop:config>
<!-- 配置的代碼都寫在此處 -->
</aop:config>
(2) 使用 aop:aspect 配置切面
- 作用: 用于配置切面
- 屬性
- id :給切面提供一個唯一標識。
- ref:引用配置好的通知類 bean 的 id。
<aop:aspect id="tm" ref="transactionManager">
...
</aop:aspect>
(3) 使用 aop:pointcut 配置切入點表達式
- 作用: 用于配置切入點表達式。就是指定對哪些類的哪些方法進行增強。
- 屬性
- expression:用于定義切入點表達式。
- id:用于給切入點表達式提供一個唯一標識
<aop:pointcut id="point1" expression="execution(public void com.etime.service.impl.AccountServiceImpl.transfer(..))"/
(4) 使用 aop:xxx 配置對應的通知類型
a.前置通知
- aop:before
- 作用:用于配置前置通知。指定增強的方法在切入點方法之前執(zhí)行
- 屬性
- method:用于指定通知類中的增強方法名稱
- ponitcut-ref:用于指定切入點的表達式的引用
- poinitcut:用于指定切入點表達式
- 執(zhí)行時間
- 切入點方法執(zhí)行之前執(zhí)行
<aop:before method="beginTransaction" pointcut-ref="point1"></aop:before>
b.后置通知
- aop:after-returning
- 作用: 用于配置后置通知
- 屬性
- method:指定通知中方法的名稱。
- pointct:定義切入點表達式
- pointcut-ref:指定切入點表達式的引用
- 執(zhí)行時間
- 切入點方法正常執(zhí)行之后。它和異常通知只能有一個執(zhí)行
<aop:after-returning method="commit" pointcut-ref="point1"></aop:after-returning>
c.異常通知
- aop:after-throwing
- 作用:用于配置異常通知
- 屬性
- method:指定通知中方法的名稱。
- pointct:定義切入點表達式
- pointcut-ref:指定切入點表達式的引用
- 執(zhí)行時間
- 切入點方法執(zhí)行產生異常后執(zhí)行。它和后置通知只能執(zhí)行一個
<aop:after-throwing method="rollback" pointcut-ref="point1"></aop:after-throwing>
d.最終通知
- aop:after
- 作用:用于配置最終通知
- 屬性
- method:指定通知中方法的名稱。
- pointct:定義切入點表達式
- pointcut-ref:指定切入點表達式的引用
- 執(zhí)行時間
- 無論切入點方法執(zhí)行時是否有異常,它都會在其后面執(zhí)行。
<aop:after method="release" pointcut-ref="point1"></aop:after>
3、切入點表達式說明
(1)切點表達式的語法
execution([修飾符] 返回值類型 包名.類名.方法名(參數))
- 訪問修飾符可以省略
- 返回值類型、包名、類名、方法名可以使用星號* 代表任意
- 包名與類名之間一個點 . 代表當前包下的類,兩個點 … 表示當前包及其子包下的類
- 參數列表可以使用兩個點 … 表示任意個數,任意類型的參數列表
a.全匹配方式
public void
com.etime.service.impl.AccountServiceImpl.saveAccount(com.etime.domain.Account)
b.訪問修飾符可以省略
void com.etime.service.impl.AccountServiceImpl.saveAccount(com.etime.domain.Account)
c.返回值可以使用*號,表示任意值
* com.etime.service.impl.AccountServiceImpl.saveAccount(com.etime.domain.Account)
d.包名可以使用 * 號,表示任意包 (但是有幾級包,需要寫幾個 *)
* *.*.*.*.AccountServiceImpl.saveAccount(com.etime.domain.Account)
e.使用…來表示當前包,及其子包
* com..AccountServiceImpl.saveAccount(com.etime.domain.Account)
f.類名可以使用*號,表示任意類
* com..*.saveAccount(com.etime.domain.Account)
g.方法名可以使用*號,表示任意方法
* com..*.*( com.etime.domain.Account)
h.參數列表可以使用*,表示參數可以是任意數據類型,但是必須有參數
* com..*.*(*)
i.參數列表可以使用…表示有無參數均可,有參數可以是任意類型
* com..*.*(..)
j.全通配方式:
* *..*.*(..)
k.注意:通常情況下,我們都是對業(yè)務層的方法進行增強,所以切入點表達式都是切到業(yè)務層實現類。
execution(* com.etime.service.impl.*.*(..))
4、環(huán)繞通知配置事務管理
(1)在TransactionManager類當中添加方法
/**
* 環(huán)繞通知:
* spring 框架為我們提供了一個接口:ProceedingJoinPoint,它可以作為環(huán)繞通知的方法參數。
* 在環(huán)繞通知執(zhí)行時,spring 框架會為我們提供該接口的實現類對象,我們直接使用就行。
* @param pjp
* @return
*/
public Object transactionAround(ProceedingJoinPoint pjp) {
//定義返回值
Object returnValue = null;
try {
//獲取方法執(zhí)行所需的參數
Object[] args = pjp.getArgs();
//前置通知:開啟事務
beginTransaction();
//執(zhí)行方法
returnValue = pjp.proceed(args);
//后置通知:提交事務
commit();
}catch(Throwable e) {
//異常通知:回滾事務
rollback();
e.printStackTrace();
}finally {
//最終通知:釋放資源
release();
}
return returnValue;
}
(2)環(huán)繞通知
- aop:around
- 作用:用于配置環(huán)繞通知
- 屬性
- method:指定通知中方法的名稱。
- pointct:定義切入點表達式
- pointcut-ref:指定切入點表達式的引用
- 說明:它是 spring 框架為我們提供的一種可以在代碼中手動控制增強代碼什么時候執(zhí)行的方式。
- 注意:通常情況下,環(huán)繞通知都是獨立使用的
<aop:config>
<aop:aspect id="tm" ref="transactionManager">
<aop:pointcut id="point1"
expression="execution(public void com.etime.service.impl.AccountServiceImpl.transfer(..))"/>
<aop:around method="transactionAround" pointcut-ref="point1"></aop:around>
</aop:aspect>
</aop:config>
到了這里,關于Spring02-Spring注解的使用、基于注解的IOC、純注解配置、整合Junit、AOP入門、基于配置文件的AOP、切入點表達式、基于配置的文件環(huán)繞通知的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!