1 AOP概述

  AOP 并不是 Spring 框架的专属名称,它的全称是 Aspect Oriented Programming ,意为:面向切面编程。
  在程序运行某个方法的时候,不修改原始执行代码逻辑,由程序动态地执行某些额外的功能,对原有的方法做增强,这就叫做面向切面编程。
  在学习AOP之前,建议先学习一下设计模式中的代理模式。

2 术语解释

Join point(连接点)
  连接点是指那些被拦截到的点。在 Spring 中这些点指的是方法,可以看作正在访问的,或者等待访问的那些需要被增强功能的方法。Spring 只支持方法类型的连接点。

Pointcut(切入点)
  切入点是一个规则,定义了我们要对哪些 Joinpoint 进行拦截。因为在一个程序中会存在很多的类,每个类又存在很多的方法,而哪些方法会应用 AOP 对该方法做功能增强呢?这就需要依据我们配置的切入点规则。

Advice(通知)
  拦截到 Joinpoint 之后所要做的事情就是通知。 也就是对方法做的增强功能。

通知分类
  前置通知:在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为,当然 —— 除非它引发异常;
  后置通知:在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知,当然 —— 方法正常返回而没有引发异常;
  最终通知:无论连接点执行后的结果如何,正常还是异常,都会执行的通知;
  异常通知:如果连接点执行因抛出异常而退出,则执行此通知;
  环绕通知:环绕通知可以在方法调用之前和之后执行自定义行为。

Target (目标)
  Target 指的是代理的目标对象,也就是连接点所在的类。

Aspect(切面)
  切面本质是一个类,只不过是个功能类,作为整合 AOP 的切入点和通知。一般来讲,需要在 Spring 的配置文件中配置,或者通过注解来配置。

Weaving(织入)
  织入是一种动作的描述,在程序运行时将增强的功能代码也就是通知,根据通知的类型(前缀后缀等…)放到对应的位置,生成代理对象。

Proxy(代理)
  一个类被 AOP 织入增强后,产生的结果就是代理类

3 Spring AOP 的代理模式

  代理名词解释为:以被代理人名义,在授权范围内与第三方实施行为。而在软件行业中代理模式是一种非常常用的设计模式,跟现实生活中的逻辑一致。
  在开发中代理模式的表现为:创建带有现有对象的代理对象以便向外界提供功能接口。代理对象可以为委托对象执行一些附带的,增加的额外功能。

3.1 代理模式分类

  在开发中,实现代理模式可以分为两种。
  静态代理:若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理 ,这种情况下的代理类通常都是我们在 Java 代码中定义的, 静态代理中的代理类和委托类会实现同一接口;

  动态代理:代理类在程序运行时创建的代理方式被称为动态代理。 也就是说,这种情况下,代理类并不是在 Java 代码中定义的,而是在运行时根据我们在 Java 代码中的 “指示” 动态生成的。

3.2 静态代理示例

  userService 接口代码

public interface UserService {public void saveUser();
}

  userServiceImpl 实现类代码

@Service
public class UserServiceImpl implements  UserService {public void saveUser() {System.out.println("执行service中的保存逻辑");}
}

  userServiceProxy 代理类代码

public class UserServiceProxy implements UserService {private UserService userService;public UserServiceProxy(UserService userService) {this.userService = userService;}@Overridepublic void saveUser() {System.out.println("委托类执行saveUser方法之前的逻辑代码");userService.saveUser();System.out.println("委托类执行saveUser方法之后的逻辑代码");}
}

代码解析:
  userServiceProxy 代理类中的属性为委托类的接口对象,目的是在构造方法中接收委托类实例,对实例方法做功能增强。

  saveUser 方法是代理类执行的逻辑,在方法内部有增强的代码逻辑,也保留了原始实例的代码功能。

测试代码:

public class test {public static void main(String[] args) {AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext();context.register(SpringConfig.class);context.refresh();UserService userService=context.getBean(UserService.class);UserServiceProxy proxy=new UserServiceProxy(userService);proxy.saveUser();}
}

运行结果

  可以看到,执行结果中即包含了被代理对象的原始保存方法的逻辑,也有代理类中对原始方法的两个增强代码。

3.3 动态代理示例

  创建动态处理器

public class DynamicProxy implements InvocationHandler {private Object object;public DynamicProxy(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("执行前逻辑");Object result = method.invoke(object, args);System.out.println("执行后逻辑");return result;}
}

  测试类代码

public class test {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(SpringConfig.class);context.refresh();// 获取接口实例UserService userService = context.getBean(UserService.class);// 动态创建实例的代码UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class}, new DynamicProxy(userService));// proxy执行方法proxy.saveUser();}
}

代码解析
  Proxy.newProxyInstance 是 JDK 提供的一个用于动态创建代理实例的方法,参数解释如下:
  ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法是固定的。
  Class<?>[] interfaces: 指定目标对象实现的接口的类型,使用泛型方式确认类型。
InvocationHandler: 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法。

运行结果

4 Spring AOP 实现之 XML 配置

4.1 工程搭建介绍

数据库建表 SQL:

CREATE TABLE `account` (`id` int(11) NOT NULL auto_increment COMMENT 'id',`accountNum` varchar(20) default NULL COMMENT '账号',`money` int(8) default NULL COMMENT '余额',PRIMARY KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

工程代码介绍
  实体类: 跟数据库表对应的 Java 类 Account ;
  操作实体类的: Dao 和 Dao 的接口实现类 ;
  调用持久层的业务类: Service 和 Service 的实现类 ;
  事务管理器类: TransactionManager 提供事务的一系列操作 ;
  测试代码类: 初始化 Spring 调用类中的方法测试 。

AOP 中的核心概念

  所以:在对原始业务类中的方法执行之前的增强行为就是前置通知,在对原始业务类中的方法执行之后的增强行为就是后置通知。而一旦出现异常,那么所做的动作就是异常通知。本案例使用几种通知,来实现事务的控制。

4.2 代码实现

创建 maven 工程:省略

pom 文件的依赖坐标如下:

     <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.17</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.17</version></dependency><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</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.13.2</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency></dependencies>

实体类 Account

public class Account implements Serializable {//数据idprivate Integer id;//账号编码private String accountNum;//账号金额private Float money;//省略get 和set 方法
}

数据库连接工具类

public class ConnectionUtils {private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}/*** 获取当前线程上的连接* @return*/public Connection getThreadConnection() {try{//1.先从ThreadLocal上获取Connection conn = tl.get();//2.判断当前线程上是否有连接if (conn == null) {//3.从数据源中获取一个连接,并且存入ThreadLocal中conn = dataSource.getConnection();tl.set(conn);}//4.返回当前线程上的连接return conn;}catch (Exception e){throw new RuntimeException(e);}}/*** 把连接和线程解绑*/public void removeConnection(){tl.remove();}
}

持久层 dao 和 dao 的 实现类:

//dao的接口
public interface IAccountDao {/*** 更新* @param account*/void updateAccount(Account account);/*** 根据编号查询账户*/Account findAccountByNum(String accountNum);
}
//dao的实现类
public class AccountDaoImpl implements IAccountDao {//dbutil的查询工具类private QueryRunner runner;//连接的工具类private ConnectionUtils connectionUtils;public void setRunner(QueryRunner runner) {this.runner = runner;}public void setConnectionUtils(ConnectionUtils connectionUtils) {this.connectionUtils = connectionUtils;}//修改账号public void updateAccount(Account account) {try{runner.update(connectionUtils.getThreadConnection(),"update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId());}catch (Exception e) {throw new RuntimeException(e);}}//根据账号查询public Account findAccountByNum(String accountNum) {try{List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum);if(accounts == null || accounts.size() == 0){return null;}if(accounts.size() > 1){throw new RuntimeException("结果集不唯一,数据有问题");}return accounts.get(0);}catch (Exception e) {throw new RuntimeException(e);}}
}

业务类 Service 和 Service 的实现类

//业务接口
public interface IAccountService {/*** 转账* @param sourceAccount        转出账户名称* @param targetAccount        转入账户名称* @param money             转账金额*/void transfer(String sourceAccount, String targetAccount, Integer money);}
//业务实现类
public class AccountServiceImpl implements IAccountService {//持久层对象private IAccountDao accountDao;//省略 set 和 get 方法//转账的方法public void transfer(String sourceAccount, String targetAccount, Integer money) {//查询原始账户Account source = accountDao.findAccountByNum(sourceAccount);//查询目标账户Account target = accountDao.findAccountByNum(targetAccount);//原始账号减钱source.setMoney(source.getMoney()-money);//目标账号加钱target.setMoney(target.getMoney()+money);//更新原始账号accountDao.updateAccount(source);//更新目标账号accountDao.updateAccount(target);System.out.println("转账完毕");}
}

事务管理器类

package com.ty.utils;/*** 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接*/
public class TransactionManager {private ConnectionUtils connectionUtils;public void setConnectionUtils(ConnectionUtils connectionUtils) {this.connectionUtils = connectionUtils;}/*** 开启事务*/public  void beginTransaction(){try {System.out.println("开启事务");connectionUtils.getThreadConnection().setAutoCommit(false);}catch (Exception e){e.printStackTrace();}}/*** 提交事务*/public  void commit(){try {System.out.println("提交事务");connectionUtils.getThreadConnection().commit();}catch (Exception e){e.printStackTrace();}}/*** 回滚事务*/public  void rollback(){try {System.out.println("回滚事务");connectionUtils.getThreadConnection().rollback();}catch (Exception e){e.printStackTrace();}}/*** 释放连接*/public  void release(){try {System.out.println("释放连接");connectionUtils.getThreadConnection().close();//还回连接池中connectionUtils.removeConnection();}catch (Exception e){e.printStackTrace();}}
}

  代码解释:此工具类作为 Spring 使用 AOP 管理事务的通知类,里面的各个方法用于配置 Spring 的通知使用。为了测试效果,在每个通知方法内,我们输出打印了测试语句。

  配置文件中添加 AOP 的相关配置

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 配置Service --><bean id="accountService" class="com.ty.service.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean><!--配置Dao对象--><bean id="accountDao" class="com.ty.dao.AccountDaoImpl"><property name="runner" ref="runner"></property><property name="connectionUtils" ref="connectionUtils"></property></bean><!--配置QueryRunner--><bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean><!-- 配置数据源 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--连接数据库的必备信息--><property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mysql"></property><property name="user" value="root"></property><property name="password" value="root"></property></bean><!-- 配置Connection的工具类 ConnectionUtils --><bean id="connectionUtils" class="com.ty.utils.ConnectionUtils"><!-- 注入数据源--><property name="dataSource" ref="dataSource"></property></bean><!-- 配置事务管理器--><bean id="txManager" class="com.ty.utils.TransactionManager"><!-- 注入ConnectionUtils --><property name="connectionUtils" ref="connectionUtils"></property></bean><!-- aop相关的节点配置 --><aop:config><aop:pointcut expression="execution ( * com.ty.service.*.*(..))" id="pc"/><aop:aspect ref="txManager"><aop:before method="beginTransaction" pointcut-ref="pc"/><aop:after-returning method="commit" pointcut-ref="pc"/><aop:after method="release" pointcut-ref="pc"/><aop:after-throwing method="rollback" pointcut-ref="pc"/></aop:aspect></aop:config></beans>

配置文件说明:
  connectionUtils: 获取数据库连接的工具类;
  dataSource: 采用 c3p0 数据源,要注意修改成自己数据库的名称与账号名和密码;
  queryRunner: dbutils 第三方框架提供用于执行 SQL 语句,操作数据库的一个工具类;
  accountDao 和 accountService:自定义的业务层实现类和持久层实现类;
  aop:config: 此节点是新增加 AOP 配置,AOP 相关信息都在这;
  aop:pointcut: 此节点是切入点,表示哪些类的哪些方法在执行的时候会应用 Spring 配置的通知进行增强;
  aop:aspect: 此节点是配置切面类的节点,它的作用主要就是整合通知和切入点。
  null 前置、后置、异常、和最终。可以看得出来 before 前置通知执行的方法是开启事务, after-returning 成功执行的方法是提交事务,after 最终执行的方法是释放连接,after-throwing 出现异常执行的方法是回滚。

测试类代码

import com.ty.service.IAccountService;
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:applicationConfig.xml")
public class AccountServiceTest {@Autowiredprivate IAccountService accountService;@Testpublic  void testTransfer(){accountService.transfer("1","2",100);}
}

注意:将transfer方法的参数修改为自己数据库中对应的accountNum,比如我这里的是1和2。

运行结果:

  可以看到,我们通过在 xml 文件中配置 Spring 的 AOP 相关配置,就可以实现对业务类中的方法实现了增强,无需自定义对业务类做代理实现。

5 Spring AOP 实现之注解配置

5.1 工程搭建介绍

数据库建表 SQL:

CREATE TABLE `account` (`id` int(11) NOT NULL auto_increment COMMENT 'id',`accountNum` varchar(20) default NULL COMMENT '账号',`money` int(8) default NULL COMMENT '余额',PRIMARY KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

工程代码介绍
  实体类: 跟数据库表对应的 Java 类 Account;
  操作实体类的: Dao 和 Dao 的接口实现类;
  调用持久层的业务类: Service 和 Service 的实现类;
  事务管理器类: TransactionManager 提供事务的一系列操作;
  测试代码类: 初始化 Spring 调用类中的方法测试。

使用注解介绍
  @Aspect:用于表明某个类为切面类,而切面类用于整合切入点和通知;
  @Pointcut:用于声明一个切入点,表明哪些类的哪些方法需要被增强;
  @Before:表示一个前置通知。即在业务方法执行之前做的事情;
  @AfterReturning:表示一个后置通知。即在业务方法执行之后做的事情;
  @After:表示一个后置通知。即在业务方法执行之后做的事情;
  @AfterThrowing:表示一个异常通知。即在业务代码执行过程中出现异常时做的事情。

5.2 代码实现

创建 maven 工程:省略

1.pom 文件的依赖坐标如下:

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.17</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.17</version></dependency><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</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.13.2</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency></dependencies>

2.实体类 Account

public class Account implements Serializable {//数据idprivate Integer id;//账号编码private String accountNum;//账号金额private Float money;//省略get 和set 方法
}

3.数据库连接工具类

@Component
public class ConnectionUtils {private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();@Autowiredprivate DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}/*** 获取当前线程上的连接* @return*/public Connection getThreadConnection() {try{//1.先从ThreadLocal上获取Connection conn = tl.get();//2.判断当前线程上是否有连接if (conn == null) {//3.从数据源中获取一个连接,并且存入ThreadLocal中conn = dataSource.getConnection();tl.set(conn);}//4.返回当前线程上的连接return conn;}catch (Exception e){throw new RuntimeException(e);}}/*** 把连接和线程解绑*/public void removeConnection(){tl.remove();}
}

4.持久层 dao 和 dao 的 实现类:

//dao的接口
public interface IAccountDao {/*** 更新* @param account*/void updateAccount(Account account);/*** 根据编号查询账户*/Account findAccountByNum(String accountNum);
}//dao的实现类
@Repository
public class AccountDaoImpl implements IAccountDao {//dbutil的查询工具类@Autowiredprivate QueryRunner runner;//连接的工具类@Autowiredprivate ConnectionUtils connectionUtils;public void setRunner(QueryRunner runner) {this.runner = runner;}public void setConnectionUtils(ConnectionUtils connectionUtils) {this.connectionUtils = connectionUtils;}//修改账号public void updateAccount(Account account) {try{runner.update(connectionUtils.getThreadConnection(),"update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId());}catch (Exception e) {throw new RuntimeException(e);}}//根据账号查询public Account findAccountByNum(String accountNum) {try{List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum);if(accounts == null || accounts.size() == 0){return null;}if(accounts.size() > 1){throw new RuntimeException("结果集不唯一,数据有问题");}return accounts.get(0);}catch (Exception e) {throw new RuntimeException(e);}}
}

  代码解释: AccountDaoImpl 类上的注解 @Repository 表示使用注解实例化此类,并交给 Spring 的容器管理。

5.业务类 Service 和 Service 的实现类:

//业务接口
public interface IAccountService {/*** 转账* @param sourceAccount        转出账户名称* @param targetAccount        转入账户名称* @param money             转账金额*/void transfer(String sourceAccount, String targetAccount, Integer money);}
//业务实现类
@Service
public class AccountServiceImpl implements IAccountService {@Autowiredprivate IAccountDao accountDao;public void setAccountDao(IAccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String sourceAccount, String targetAccount, Integer money) {Account source = accountDao.findAccountByNum(sourceAccount);Account target = accountDao.findAccountByNum(targetAccount);source.setMoney(source.getMoney()-money);target.setMoney(target.getMoney()+money);accountDao.updateAccount(source);accountDao.updateAccount(target);System.out.println("转账完毕");}
}

  代码解释:AccountServiceImpl 类上的注解 @Service 表示使用注解实例化此类,并交给 Spring 的容器管理。

6.事务管理器类

@Component
@Aspect
public class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;public void setConnectionUtils(ConnectionUtils connectionUtils) {this.connectionUtils = connectionUtils;}@Pointcut("execution(* com.offcn.service.impl.*.*(..))")private void pt1() {}/*** 开启事务*/@Before("pt1()")public  void beginTransaction(){try {System.out.println("开启事务");connectionUtils.getThreadConnection().setAutoCommit(false);}catch (Exception e){e.printStackTrace();}}/*** 提交事务*/@AfterReturning("pt1()")public  void commit(){try {System.out.println("提交事务");connectionUtils.getThreadConnection().commit();}catch (Exception e){e.printStackTrace();}}/*** 回滚事务*/@AfterThrowing("pt1()")public  void rollback(){try {System.out.println("回滚事务");connectionUtils.getThreadConnection().rollback();}catch (Exception e){e.printStackTrace();}}/*** 释放连接*/@After("pt1()")public  void release(){try {System.out.println("释放连接");connectionUtils.getThreadConnection().close();//还回连接池中connectionUtils.removeConnection();}catch (Exception e){e.printStackTrace();}}
}

代码解释
  此类通过注解 @Componet 实例化,并且交由 Spring 容器管理,@Aspect 表明它是一个切面类。

7.配置文件:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!--配置QueryRunner--><bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean><!-- 配置数据源 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--连接数据库的必备信息--><property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mysql"></property><property name="user" value="root"></property><property name="password" value="root"></property></bean><!-- 注解扫描工程下的包路径--><context:component-scan base-package="com.ty"></context:component-scan><!-- 注解代理模式 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

配置文件说明
  dataSource: 采用 c3p0 数据源,注意修改成自己数据库的名称与账号名和密码;
  queryRunner: dbutils 第三方框架提供用于执行 sql 语句,操作数据库的一个工具类;
  context:component-scan:表示初始化容器扫描的包路径;
  aop:aspectj-autoproxy:表示开启代理模式。

8.测试类代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {@Autowiredprivate IAccountService accountService;@Testpublic  void testTransfer(){accountService.transfer("622200009999","622200001111",100);}
}

运行结果:

  可以看到,通过注解方式配置 Spring 的 AOP 相关配置,同样可以实现对数据的操作。

ps:以上内容来自对慕课教程的学习与总结

Spring——面向切面编程(AOP)相关推荐

  1. Spring→面向切面编程AOP、相关概念、通知Advice类型、配置切面切入点通知、AOP相关API、AOP代理类ProxyFactoryBean、AOP注解@AspectJ

    面向切面编程AOP CGLib AOP相关概念 Advice类型 Spring实现AOP Spring配置切面aspect 配置切入点pointcut 配置通知advice 配置通知参数 调用新的父类 ...

  2. Spring面向切面编程-AOP详解

    文章目录 前言 介绍AOP 一.实现AOP 1.1.全注解形式实现AOP 前提准备(引入jar包) 实现AOP(五种通知) 二.认识JoinPont与ProceedingJoinPoint 2.1.初 ...

  3. Spring in Action 入门之面向切面编程AOP

    注明:这篇文章一是当成学习笔记,二是给大家提供另一个快速理解学习Spring的参考.欢迎留言讨论,持续更新中~ (该部分是Spring的面向切面编程AOP) 第四章 通知Bean 在软件编程中,散布于 ...

  4. Spring(四):面向切面编程AOP

    2019独角兽企业重金招聘Python工程师标准>>> 横切关注点:分布于应用中多处的功能 面向切面编程AOP:将横切关注点与业务逻辑相分离 在使用面向切面编程时,仍在一个地方定义通 ...

  5. Spring 面向切面编程(AOP) D5

    Spring AOP简介 问题提出 首先我们回顾一下OOP(Object Oriented Programming-面向对象编程),OOP引入了封装.继承.多态等概念建立了一种对象层次结构,用于模拟公 ...

  6. Spring之面向切面编程AOP(八)

    介绍&步骤 视频教程: https://www.bilibili.com/video/BV1WZ4y1P7Bp?p=121 官方笔记链接:https://pan.baidu.com/s/1dn ...

  7. Spring面向切面编程(AOP)详解

    Spring面向切面编程(AOP)详解 面向切面编程(AOP)是Spring框架的另外一个重要的核心内容. 而在讲AOP之前,先来了解一下动态代理这个概念,因为AOP基于动态代理. 动态代理概念:在程 ...

  8. 服务端第三次课程:面向切面编程AOP

    3:面向切面编程AOP 1:回顾 bean的组装方式 规划的装配 component autowired sacn是在configuration底下的 Java config 使用configurat ...

  9. Spring-学习笔记08【面向切面编程AOP】

    Java后端 学习路线 笔记汇总表[黑马程序员] Spring-学习笔记01[Spring框架简介][day01] Spring-学习笔记02[程序间耦合] Spring-学习笔记03[Spring的 ...

最新文章

  1. Ubuntu 16.04 命令行 关机 or 重启
  2. 四位先行进位电路逻辑表达式_如何用基本的逻辑门设计32bit的超前进位加法器?...
  3. 为了OFFER,继续深入学习树和二叉树
  4. 基于matlab的图解粒度参数计算,基于MATLAB的图解粒度参数计算-热带地理.PDF
  5. hdu 2112 ——HDU Today
  6. Sprint计划列表
  7. CentOS7 Minimal 安装后出现的若干问题解决办法
  8. python字符串反向输出_Python反向字符串– 5种方法和最佳方法
  9. mac上配置rails开发环境
  10. 现代通信原理14.2:M进制数字调制信号波形的向量表示
  11. 【转】演化博弈理论(EGT)
  12. prisma2.0和nexus搭建graphql后端(2)—nexus
  13. docx文件转pdf,使用aspose words 转pdf,并且解决表格格式错乱
  14. Web.17.EL表达式JSTL标签的使用
  15. 容器部署在物理机还是虚拟机上?
  16. alter命令的使用
  17. 跳汰机的实操经验总结
  18. 如何计算IT投资回报(ROI)
  19. 豆瓣的python库安装源
  20. 《天天魔塔》源码下载

热门文章

  1. markdown数学类总结
  2. 网络拓扑 代码 matlab,matlabdaima 复杂网络拓扑特征统计的编程代码,可以计算 中各种 值 Other systems 其他 246万源代码下载- www.pudn.com...
  3. 管理学中的知名定律之彼得原理(The Peter Principle)
  4. 调试的方法以及重要性
  5. 供应链管理问题会否导致小米在印度市场受挫?
  6. 让动物保育理念能更贴近民众 街景服务与胜博发公益结合迈进大象保护区
  7. linux内核中的ASID是什么?
  8. sysstat工具简介
  9. 指纹识别与交互(冯建江,2022)
  10. [0xFA-2021][Buuctf]Java逆向1