Spring核心之面向切面编程(AOP)
- 一、简介
- 二、术语
- 三、方式
- 1. Spring AOP
- 2. Spring Aspect AOP (XML)
- 3. Spring Aspect AOP (Annotation)
- 四、通知(Spring AOP方式)
- 1. MethodBeforeAdvice(前置通知)
- 2. AfterReturningAdvice(后置通知)
- 3. MethodInterceptor(环绕通知)
- 4. ThrowsAdvice(异常通知)
- 5. IntroductionInterceptor(引介通知)
- 五、通知(Spring Aspect AOP -XML)
- 六、通知(Spring Aspect AOP -Annotation)
一、简介
- AOP(Aspect Oriented Programming,面向切面编程)是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP,可以对业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- AOP主要应用在事务处理、日志管理、权限控制、异常处理等方面。
二、术语
名称 | 解释 |
Aspect(切面) | 是对象操作过程中的截面,实际上切面是一段程序代码,这段代码将被植入到程序的流程中。 |
Proxy(代理) | 是将通知应用当目标对象后创建的对象。 |
Joinpoint(连接点) | 应用程序执行过程中插入切面的点,是对象操作过程中的某个阶段点。 |
Pointcut(切入点) | 是连接点的集合切面与程序流的交叉点,及程序的切入点,确切地说它是切面注入到程序中的位置。 |
Advice(通知) | 是在切面的某个特定的连接点上执行的动作。通知有多种类型。 |
Target Object(目标对象) | 是被通知的对象,既可以是编写的类,也可以是需要添加指定行为的第三方类。 |
Weaving(织入) | 把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象的过程 |
Introduction(引入) | 引入允许我们向现有的类添加新的方法和属性 |
三、方式
1. Spring AOP
- 需要导入的jar包
2. Spring Aspect AOP (XML)
- 需要导入的jar包
3. Spring Aspect AOP (Annotation)
- 需要导入的jar包
四、通知(Spring AOP方式)
1. MethodBeforeAdvice(前置通知)
- 在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
- 案例(使UserDao中的方法在执行前输出一些信息)
接口
package com.aop;
public interface UserDao {
int userLogin(String username,String password);
}实现类
package com.aop;
public class UserDaoImpl implements UserDao {
@Override
public int userLogin(String username, String password) {
System.out.println("---方法逻辑执行---");
return 0;
}
}切面类
package com.aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LogBeforeAspect implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
// 执行逻辑之前输出的内容
System.out.println("---前置通知开始---");
// method表示通知的方法名称
System.out.println(method);
// objects表示通知的方法的各个参数
System.out.println(objects.length);
// o表示通知的方法的哈希值
System.out.println(o);
System.out.println("---前置通知结束---");
}
}配置文件
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- target object -->
<bean id="userDao" class="com.aop.UserDaoImpl"></bean>
<!-- 切面 -->
<bean id="logAspect" class="com.aop.LogAspect"></bean>
<bean id="logBeforeAspect" class="com.aop.LogBeforeAspect"></bean>
<!-- 代理 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口 -->
<property name="proxyInterfaces" value="com.aop.UserDao"></property>
<!-- 目标对象 -->
<property name="target" ref="userDao"></property>
<!-- 切面-->
<property name="interceptorNames" value="logBeforeAspect"></property>
<!-- 代理目标对象是否为类 使用JDK代理用的是接口-->
<property name="proxyTargetClass" value="false"></property>
</bean>
</beans>测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test_AOP {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDaoProxy",UserDao.class);
userDao.userLogin("test1","123");
}
}控制台输出内容

2. AfterReturningAdvice(后置通知)
- 在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
- 案例(使UserDao中的方法在执行后输出一些信息)
接口
package com.aop;
public interface UserDao {
int userLogin(String username,String password);
}实现类
package com.aop;
public class UserDaoImpl implements UserDao {
@Override
public int userLogin(String username, String password) {
System.out.println("---方法逻辑执行---");
return 0;
}
}切面类
package com.aop;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class LogAfterAspect implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
// 执行逻辑之后输出的内容
System.out.println("---后置通知开始---");
// o表示通知的方法的返回值,若没有返回值则o为null
System.out.println(o);
// method表示通知的方法名称
System.out.println(method);
// objects表示通知的方法中的各个参数
System.out.println(objects.length);
// o1表示通知的方法的哈希值
System.out.println(o1);
System.out.println("---后置通知结束---");
}
}配置文件
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- target object -->
<bean id="userDao" class="com.aop.UserDaoImpl"></bean>
<!-- 切面 -->
<bean id="logAspect" class="com.aop.LogAspect"></bean>
<bean id="logBeforeAspect" class="com.aop.LogBeforeAspect"></bean>
<bean id="LogAfterAspect" class="com.aop.LogAfterAspect"></bean>
<!-- 代理 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口 -->
<property name="proxyInterfaces" value="com.aop.UserDao"></property>
<!-- 目标对象 -->
<property name="target" ref="userDao"></property>
<!-- 切面-->
<property name="interceptorNames" value="LogAfterAspect"></property>
<!-- 代理目标对象是否为类 使用JDK代理用的是接口-->
<property name="proxyTargetClass" value="false"></property>
</bean>
</beans>测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test_AOP {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDaoProxy",UserDao.class);
userDao.userLogin("test1","123");
}
}控制台输出内容

3. MethodInterceptor(环绕通知)
- 在方法前后自动执行的通知称为焕然通知,可以应用于日志、事务管理等功能。
- 案例(使UserDao中的方法在执行前后输出一些信息)
接口
package com.aop;
// 接口
public interface UserDao {
int userLogin(String username,String password);
}实现类
package com.aop;
// 实现类
public class UserDaoImpl implements UserDao {
@Override
public int userLogin(String username, String password) {
System.out.println("---方法逻辑执行---");
return 0;
}
}切面类
package com.aop;
// 切面类
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 1.执行逻辑之前输出的内容
System.out.println("---环绕通知开始---");
// 2.执行逻辑
Object obj = methodInvocation.proceed();
// 3.执行逻辑之后输出的内容
System.out.println("---环绕通知结束---");
return obj;
}
}配置文件
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- target object -->
<bean id="userDao" class="com.aop.UserDaoImpl"></bean>
<!-- 切面 -->
<bean id="logAspect" class="com.aop.LogAspect"></bean>
<!-- 代理 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口 -->
<property name="proxyInterfaces" value="com.aop.UserDao"></property>
<!-- 目标对象 -->
<property name="target" ref="userDao"></property>
<!-- 切面-->
<property name="interceptorNames" value="logAspect"></property>
<!-- 代理目标对象是否为类 使用JDK代理用的是接口-->
<property name="proxyTargetClass" value="false"></property>
</bean>
</beans>测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test_AOP {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDaoProxy",UserDao.class);
userDao.userLogin("test1","123");
}
}控制台输出内容

4. ThrowsAdvice(异常通知)
- 在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
- 案例(在UserDao执行发生异常时输出一些内容)
接口
package com.aop;
public interface UserDao {
int userLogin(String username,String password);
}实现类
package com.aop;
public class UserDaoImpl implements UserDao {
@Override
public int userLogin(String username, String password) {
System.out.println("---方法逻辑执行---");
// 以下代码会产生一个异常
String id = null;
id.charAt(0);
return 0;
}
}package com.aop;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class LogThrowsAspect implements ThrowsAdvice {
public void afterThrowing(Method method,Object[] objects,Object o,Throwable error){
//执行逻辑时发生异常所输出的内容
System.out.println("---异常通知开始---");
// method表示通知的方法名称
System.out.println(method);
// objects表示通知的方法中的各个参数
System.out.println(objects.length);
// o表示通知的方法的哈希值
System.out.println(o);
// error表示通知的方法的异常
System.out.println(error);
System.out.println("---异常通知结束---");
}
}配置文件
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- target object -->
<bean id="userDao" class="com.aop.UserDaoImpl"></bean>
<!-- 切面 -->
<bean id="logAspect" class="com.aop.LogAspect"></bean>
<bean id="logBeforeAspect" class="com.aop.LogBeforeAspect"></bean>
<bean id="logAfterAspect" class="com.aop.LogAfterAspect"></bean>
<bean id="logThrowsAspect" class="com.aop.LogThrowsAspect"></bean>
<!-- 代理 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口 -->
<property name="proxyInterfaces" value="com.aop.UserDao"></property>
<!-- 目标对象 -->
<property name="target" ref="userDao"></property>
<!-- 切面-->
<property name="interceptorNames" value="logThrowsAspect"></property>
<!-- 代理目标对象是否为类 使用JDK代理用的是接口-->
<property name="proxyTargetClass" value="false"></property>
</bean>
</beans>测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test_AOP {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDaoProxy",UserDao.class);
userDao.userLogin("test1","123");
}
}控制台输出内容

5. IntroductionInterceptor(引介通知)
- 在目标中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。
- 案例(在UserDao接口中加入新的业务)
接口
package com.aop;
public interface EUserDao {
int del(Integer id);
}实现类
package com.aop;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInterceptor;
public class EUserDaoImpl implements EUserDao, IntroductionInterceptor {
@Override
public int del(Integer id) {
System.out.println("---新的方法执行逻辑---");
return 0;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (implementsInterface(methodInvocation.getMethod().getDeclaringClass())) {
return methodInvocation.getMethod().invoke(this, methodInvocation.getArguments());
} else {
return methodInvocation.proceed();
}
}
@Override
public boolean implementsInterface(Class<?> aClass) {
// 返回实现接口
return aClass.isAssignableFrom(EUserDao.class);
}
}配置文件
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- target object -->
<bean id="userDao" class="com.aop.UserDaoImpl"></bean>
<bean id="eUserDao" class="com.aop.EUserDaoImpl"></bean>
<!-- 切面 -->
<bean id="logAspect" class="com.aop.LogAspect"></bean>
<bean id="logBeforeAspect" class="com.aop.LogBeforeAspect"></bean>
<bean id="logAfterAspect" class="com.aop.LogAfterAspect"></bean>
<bean id="logThrowsAspect" class="com.aop.LogThrowsAspect"></bean>
<!-- 引介通知 -->
<bean id="userDaoAspect" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg ref="eUserDao"></constructor-arg>
<constructor-arg value="com.aop.EUserDao"></constructor-arg>
</bean>
<!-- 代理 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口 -->
<property name="proxyInterfaces" value="com.aop.UserDao"></property>
<!-- 目标对象 -->
<property name="target" ref="userDao"></property>
<!-- 切面-->
<property name="interceptorNames" value="userDaoAspect"></property>
<!-- 代理目标对象是否为类 使用JDK代理用的是接口-->
<property name="proxyTargetClass" value="false"></property>
</bean>
</beans>测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test_AOP {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EUserDao eUserDao = context.getBean("userDaoProxy",EUserDao.class);
eUserDao.del(1);
}
}控制台输出内容

五、通知(Spring Aspect AOP -XML)
- 案例
接口
package com.aop;
public interface UserDao {
int userLogin(String username,String password);
}实现类
package com.aop;
public class UserDaoImpl implements UserDao{
@Override
public int userLogin(String username, String password) {
System.out.println("---执行方法逻辑---");
return 0;
}
}切面类
package com.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogAspect {
// 前置通知
public void myBefore(JoinPoint joinPoint){
System.out.println("---前置通知开始---");
System.out.println("---目标对象:"+ joinPoint.getTarget()+"---");
System.out.println("---方法:"+joinPoint.getSignature().getName()+"---");
System.out.println("---前置通知结束---");
}
// 后置通知
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("---后置通知开始---");
System.out.println("---目标对象:"+ joinPoint.getTarget()+"---");
System.out.println("---方法:"+joinPoint.getSignature().getName()+"---");
System.out.println("---后置通知结束---");
}
// 环绕通知
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("---环绕通知开始---");
System.out.println("---目标对象:"+ proceedingJoinPoint.getTarget()+"---");
System.out.println("---方法:"+proceedingJoinPoint.getSignature().getName()+"---");
Object obj = proceedingJoinPoint.proceed();
System.out.println("---环绕通知结束---");
return obj;
}
// 异常通知
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("---异常通知开始");
System.out.println("异常:" + e.getMessage());
System.out.println("---异常通知结束");
}
// 最终通知 Aspect中的最终通知是在执行完方法逻辑后执行,而Spring中的最终通知是在
public void myAfter(JoinPoint joinPoint){
System.out.println("---最终通知开始---");
System.out.println("---目标对象:"+ joinPoint.getTarget()+"---");
System.out.println("---方法:"+joinPoint.getSignature().getName()+"---");
System.out.println("---最终通知结束---");
}
}测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop {
@Test
public void fun1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.userLogin("test","123");
}
}控制台输出内容

六、通知(Spring Aspect AOP -Annotation)
- 案例
接口
package com.aop;
public interface UserDao {
int userLogin(String username,String password);
}实现类
package com.aop;
import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDaoImpl implements UserDao{
@Override
public int userLogin(String username, String password) {
System.out.println("---执行方法逻辑---");
return 0;
}
}切面类
package com.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 切面类
*/
@Component //组件注解
@Aspect //切面类注解
public class LogAspect {
// 切入点
@Pointcut("execution(* com.aop.UserDao.userLogin(..))")
public void myPointCut(){
}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint){
System.out.println("---前置通知开始---");
System.out.println("---目标对象:"+ joinPoint.getTarget()+"---");
System.out.println("---方法:"+joinPoint.getSignature().getName()+"---");
System.out.println("---前置通知结束---");
}
// 后置通知
@AfterReturning("myPointCut()")
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("---后置通知开始---");
System.out.println("---目标对象:"+ joinPoint.getTarget()+"---");
System.out.println("---方法:"+joinPoint.getSignature().getName()+"---");
System.out.println("---后置通知结束---");
}
// 环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("---环绕通知开始---");
System.out.println("---目标对象:"+ proceedingJoinPoint.getTarget()+"---");
System.out.println("---方法:"+proceedingJoinPoint.getSignature().getName()+"---");
Object obj = proceedingJoinPoint.proceed();
System.out.println("---环绕通知结束---");
return obj;
}
// 异常通知
@AfterThrowing(value = "myPointCut()",throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("---异常通知开始");
System.out.println("异常:" + e.getMessage());
System.out.println("---异常通知结束");
}
// 最终通知 Aspect中的最终通知是在执行完方法逻辑后执行,而Spring中的最终通知是在
@After("myPointCut()")
public void myAfter(JoinPoint joinPoint){
System.out.println("---最终通知开始---");
System.out.println("---目标对象:"+ joinPoint.getTarget()+"---");
System.out.println("---方法:"+joinPoint.getSignature().getName()+"---");
System.out.println("---最终通知结束---");
}
}配置文件
<?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:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描com.aop包中所有的类 -->
<context:component-scan base-package="com.aop"></context:component-scan>
<!-- 开启切面的自动代理 -->
<aop:aspectj-autoproxy/>
</beans>测试类
package com.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop {
@Test
public void fun1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.userLogin("test","123");
}
}控制台输出内容

















