Java開發框架Spring——Spring的 AOP 功能

一、概述

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 的配置

  1. 基於 XML 的 AOP 配置
  2. 基於註解的 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());
        }
    }
}

五、基於註解的 AOP 配置

請點擊 Spring 基於註解的AOP配置

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章