Spring——面向切面编程(AOP)
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)相关推荐
- Spring→面向切面编程AOP、相关概念、通知Advice类型、配置切面切入点通知、AOP相关API、AOP代理类ProxyFactoryBean、AOP注解@AspectJ
面向切面编程AOP CGLib AOP相关概念 Advice类型 Spring实现AOP Spring配置切面aspect 配置切入点pointcut 配置通知advice 配置通知参数 调用新的父类 ...
- Spring面向切面编程-AOP详解
文章目录 前言 介绍AOP 一.实现AOP 1.1.全注解形式实现AOP 前提准备(引入jar包) 实现AOP(五种通知) 二.认识JoinPont与ProceedingJoinPoint 2.1.初 ...
- Spring in Action 入门之面向切面编程AOP
注明:这篇文章一是当成学习笔记,二是给大家提供另一个快速理解学习Spring的参考.欢迎留言讨论,持续更新中~ (该部分是Spring的面向切面编程AOP) 第四章 通知Bean 在软件编程中,散布于 ...
- Spring(四):面向切面编程AOP
2019独角兽企业重金招聘Python工程师标准>>> 横切关注点:分布于应用中多处的功能 面向切面编程AOP:将横切关注点与业务逻辑相分离 在使用面向切面编程时,仍在一个地方定义通 ...
- Spring 面向切面编程(AOP) D5
Spring AOP简介 问题提出 首先我们回顾一下OOP(Object Oriented Programming-面向对象编程),OOP引入了封装.继承.多态等概念建立了一种对象层次结构,用于模拟公 ...
- Spring之面向切面编程AOP(八)
介绍&步骤 视频教程: https://www.bilibili.com/video/BV1WZ4y1P7Bp?p=121 官方笔记链接:https://pan.baidu.com/s/1dn ...
- Spring面向切面编程(AOP)详解
Spring面向切面编程(AOP)详解 面向切面编程(AOP)是Spring框架的另外一个重要的核心内容. 而在讲AOP之前,先来了解一下动态代理这个概念,因为AOP基于动态代理. 动态代理概念:在程 ...
- 服务端第三次课程:面向切面编程AOP
3:面向切面编程AOP 1:回顾 bean的组装方式 规划的装配 component autowired sacn是在configuration底下的 Java config 使用configurat ...
- Spring-学习笔记08【面向切面编程AOP】
Java后端 学习路线 笔记汇总表[黑马程序员] Spring-学习笔记01[Spring框架简介][day01] Spring-学习笔记02[程序间耦合] Spring-学习笔记03[Spring的 ...
最新文章
- Ubuntu 16.04 命令行 关机 or 重启
- 四位先行进位电路逻辑表达式_如何用基本的逻辑门设计32bit的超前进位加法器?...
- 为了OFFER,继续深入学习树和二叉树
- 基于matlab的图解粒度参数计算,基于MATLAB的图解粒度参数计算-热带地理.PDF
- hdu 2112 ——HDU Today
- Sprint计划列表
- CentOS7 Minimal 安装后出现的若干问题解决办法
- python字符串反向输出_Python反向字符串– 5种方法和最佳方法
- mac上配置rails开发环境
- 现代通信原理14.2:M进制数字调制信号波形的向量表示
- 【转】演化博弈理论(EGT)
- prisma2.0和nexus搭建graphql后端(2)—nexus
- docx文件转pdf,使用aspose words 转pdf,并且解决表格格式错乱
- Web.17.EL表达式JSTL标签的使用
- 容器部署在物理机还是虚拟机上?
- alter命令的使用
- 跳汰机的实操经验总结
- 如何计算IT投资回报(ROI)
- 豆瓣的python库安装源
- 《天天魔塔》源码下载
热门文章
- markdown数学类总结
- 网络拓扑 代码 matlab,matlabdaima 复杂网络拓扑特征统计的编程代码,可以计算 中各种 值 Other systems 其他 246万源代码下载- www.pudn.com...
- 管理学中的知名定律之彼得原理(The Peter Principle)
- 调试的方法以及重要性
- 供应链管理问题会否导致小米在印度市场受挫?
- 让动物保育理念能更贴近民众 街景服务与胜博发公益结合迈进大象保护区
- linux内核中的ASID是什么?
- sysstat工具简介
- 指纹识别与交互(冯建江,2022)
- [0xFA-2021][Buuctf]Java逆向1