一、概述
1、AOP
- 全稱是 Aspect Oriented Programming 即:面向切面編程
- AOP 就是把我們程序重複的代碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改源碼的基礎上,對我們的已有方法進行增強。
2、作用:
- 在程序運行期間,不修改源碼對已有方法進行增強。
3、優勢:
- 減少重複代碼
- 提高開發效率
- 維護方便
4、AOP 的實現方式
- 使用動態代理技術
關於動態代理請點擊
二、AOP(面向切面編程)
1、AOP 相關術語
- Joinpoint(連接點) : 是指那些被攔截到的點。在 spring 中,這些點指的是方法,因爲 spring 只支持方法類型的連接點。
- Pointcut(切入點) : 是指我們要對哪些 Joinpoint 進行攔截的定義。
- Advice(通知/增強) : 所謂通知是指攔截到 Joinpoint 之後所要做的事情就是通知。
通知的類型 :前置通知,後置通知,異常通知,最終通知,環繞通知。 - Introduction(引介) : 是一種特殊的通知在不修改類代碼的前提下, Introduction 可以在運行期爲類動態地添加一些方法或 Field。
- Target(目標對象) : 代理的目標對象。
- Weaving(織入) : 是指把增強應用到目標對象來創建新的代理對象的過程。
spring 採用動態代理織入,而 AspectJ 採用編譯期織入和類裝載期織入。 - Proxy(代理): 一個類被 AOP 織入增強後,就產生一個結果代理類。
- Aspect(切面) : 是切入點和通知(引介)的結合。
2、spring 中的 AOP 要明確的事
a、開發階段(程序員做的)
- 編寫核心業務代碼(開發主線):大部分程序員來做,要求熟悉業務需求。
- 把公用代碼抽取出來,製作成通知。(開發階段最後再做):AOP 編程人員來做。
- 在配置文件中,聲明切入點與通知間的關係,即切面。:AOP 編程人員來做。
b、運行階段(Spring 框架完成的)
- Spring 框架監控切入點方法的執行。一旦監控到切入點方法被運行,使用代理機制,動態創建目標對象的代理對象,根據通知類別,在代理對象的對應位置,將通知對應的功能織入,完成完整的代碼邏輯運行。
3、AOP 的配置
- 基於 XML 的 AOP 配置
- 基於註解的 AOP 配置
三、基於 XML的 AOP 配置
第一步:新建實體類 Account.java 來映射上面的數據庫表
package cn.lemon.domain;
public class Account {
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;
}
}
第二步:新建數據庫訪問層(dao)
IAccountDao.java接口
package cn.lemon.dao;
import cn.lemon.domain.Account;
import java.util.List;
public interface IAccountDao {
void addAccount(Account account);
void deleteAccount(Integer accountId);
void updateAccount(Account account);
Account findOne(Integer accountId);
List<Account> findAll();
}
接口的實現類 AccountDaoImpl.java
package cn.lemon.dao.impl;
import cn.lemon.dao.IAccountDao;
import cn.lemon.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void addAccount(Account account) {
try{
jdbcTemplate.update("insert into account (name,money) values (?,?)",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer accountId) {
try{
jdbcTemplate.update("delete from account where id = ?",accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public void updateAccount(Account account) {
try{
jdbcTemplate.update("update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public Account findOne(Integer accountId) {
try{
return jdbcTemplate.queryForObject("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public List<Account> findAll() {
try{
return jdbcTemplate.query("select * from account",new BeanPropertyRowMapper<Account>(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
第三步:新建業務邏輯層(service)
IAccountService.java接口
package cn.lemon.service;
import cn.lemon.domain.Account;
import java.util.List;
public interface IAccountService {
void addAccount(Account account);
void deleteAccount(Integer accountId);
void updateAccount(Account account);
Account findOne(Integer accountId);
List<Account> findAll();
}
接口的實現類 AccountServiceImpl.java
package cn.lemon.service.impl;
import cn.lemon.dao.IAccountDao;
import cn.lemon.domain.Account;
import cn.lemon.service.IAccountService;
import java.util.List;
public class AccountServiceImpl implements IAccountService {
private IAccountDao iAccountDao;
public void setiAccountDao(IAccountDao iAccountDao) {
this.iAccountDao = iAccountDao;
}
@Override
public void addAccount(Account account) {
iAccountDao.addAccount(account);
}
@Override
public void deleteAccount(Integer accountId) {
iAccountDao.deleteAccount(accountId);
}
@Override
public void updateAccount(Account account) {
iAccountDao.updateAccount(account);
}
@Override
public Account findOne(Integer accountId) {
return iAccountDao.findOne(accountId);
}
@Override
public List<Account> findAll() {
return iAccountDao.findAll();
}
}
第四步:新建 xml Spring配置文件 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///db_account"></property>
<property name="username" value="root"></property>
<property name="password" value="lemon"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountDao" class="cn.lemon.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="accountService" class="cn.lemon.service.impl.AccountServiceImpl">
<property name="iAccountDao" ref="accountDao"></property>
</bean>
</beans>
第五步:新建測試類 AccountServiceImplTest.java ,實現數據庫的增刪改查
package cn.lemon.service.impl;
import cn.lemon.domain.Account;
import cn.lemon.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class AccountServiceImplTest {
private IAccountService iAccountService;
public AccountServiceImplTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
iAccountService = (IAccountService) applicationContext.getBean("accountService");
}
@Test
public void addAccount() {
Account account = new Account();
account.setName("馬東敏");
account.setMoney(10d);
iAccountService.addAccount(account);
}
@Test
public void deleteAccount() {
iAccountService.deleteAccount(14);
}
@Test
public void updateAccount() {
Account account = iAccountService.findOne(17);
account.setName("張英");
iAccountService.updateAccount(account);
}
@Test
public void findOne() {
Account account = iAccountService.findOne(1);
System.out.println("ID:" + account.getId() + "\tName:" + account.getName() + "\tMoney:" + account.getMoney());
}
@Test
public void findAll() {
List<Account> accountList = iAccountService.findAll();
for (Account account : accountList) {
System.out.println("ID:" + account.getId() + "\tName:" + account.getName() + "\tMoney:" + account.getMoney());
}
}
}
四、基於 XML和註解 的 AOP 配置
第一步:通過 @Component 和 @Autowired 修改上面的代碼
注意:使用 @Autowired 時,set 方法可以省略
第一步:修改數據訪問層的實現類 AccountDaoImpl.java
package cn.lemon.dao.impl;
import cn.lemon.dao.IAccountDao;
import cn.lemon.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Component:在spring 中產生對象,相當於 <bean id="",class=""></bean>
* 默認的id爲:accountDaoImpl(類名的首字母小寫),可以使用默認的
* @Repository 專門用於 dao 層
*/
@Component
//@Repository
public class AccountDaoImpl implements IAccountDao {
/**
* @Autowired :依賴注入的註解
* 默認找Spring容器中,跟屬性類型相同的對象注入
* 可以不寫set 方法
* 如果有多個類型相同的對象,或者沒有響應類型的對象,會報錯
* 使用 @Qualifier 指定 id 注入
*/
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addAccount(Account account) {
try {
jdbcTemplate.update("insert into account (name,money) values (?,?)", account.getName(), account.getMoney());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer accountId) {
try {
jdbcTemplate.update("delete from account where id = ?", accountId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void updateAccount(Account account) {
try {
jdbcTemplate.update("update account set name = ?,money = ? where id = ?", account.getName(), account.getMoney(), account.getId());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findOne(Integer accountId) {
try {
return jdbcTemplate.queryForObject("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public List<Account> findAll() {
try {
return jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
第二步:修改業務邏輯層的實現類 AccountServiceImpl.java
package cn.lemon.service.impl;
import cn.lemon.dao.IAccountDao;
import cn.lemon.domain.Account;
import cn.lemon.service.IAccountService;
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;
import java.util.List;
/**
* Component:在spring 中產生對象,相當於 <bean id="",class=""></bean>
* 默認的id爲:accountServiceImpl(類名的首字母小寫),可以使用默認的
*
* @Service 專門用於 Service 層
*/
@Component("accountServiceImpl")
//@Service
public class AccountServiceImpl implements IAccountService {
/**
* @Autowired :依賴注入的註解
* 默認找Spring容器中,跟屬性類型相同的對象注入
* 可以不寫set 方法
* 如果有多個類型相同的對象,或者沒有響應類型的對象,會報錯
* 使用 @Qualifier 指定 id 注入,或者在 xml 配置文件中使用 primary = true
* @Resource 依賴注入的註解
*/
//@Autowired
//@Qualifier("accountDaoImpl")
@Resource(name = "accountDaoImpl")
private IAccountDao iAccountDao;
@Value("這是下面 name 的值")
private String name;
@Override
public void addAccount(Account account) {
iAccountDao.addAccount(account);
}
@Override
public void deleteAccount(Integer accountId) {
iAccountDao.deleteAccount(accountId);
}
@Override
public void updateAccount(Account account) {
iAccountDao.updateAccount(account);
}
@Override
public Account findOne(Integer accountId) {
return iAccountDao.findOne(accountId);
}
@Override
public List<Account> findAll() {
System.out.println(name);
return iAccountDao.findAll();
}
}
第三步:修改測試類 AccountServiceImplTest.java
package cn.lemon.service.impl;
import cn.lemon.domain.Account;
import cn.lemon.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class AccountServiceImplTest {
private IAccountService iAccountService;
public AccountServiceImplTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
iAccountService = (IAccountService) applicationContext.getBean("accountServiceImpl");
}
@Test
public void addAccount() {
Account account = new Account();
account.setName("馬東敏");
account.setMoney(10d);
iAccountService.addAccount(account);
}
@Test
public void deleteAccount() {
iAccountService.deleteAccount(14);
}
@Test
public void updateAccount() {
Account account = iAccountService.findOne(17);
account.setName("張英");
iAccountService.updateAccount(account);
}
@Test
public void findOne() {
Account account = iAccountService.findOne(1);
System.out.println("ID:" + account.getId() + "\tName:" + account.getName() + "\tMoney:" + account.getMoney());
}
@Test
public void findAll() {
List<Account> accountList = iAccountService.findAll();
for (Account account : accountList) {
System.out.println("ID:" + account.getId() + "\tName:" + account.getName() + "\tMoney:" + account.getMoney());
}
}
}