AOP(Aspect Oriented Programming),即面向切面编程,就是程序运行过程中在不改变程序源码的情况下,通过 预编译 和 运行期动态代理 来增强方法。利用 AOP 能够对业务逻辑的各部分进行隔离,从而降低业务逻辑各部分之间的耦合度,提高程序的可重用性,提高开发效率。
在 AOP 中,常见概念有如下几点:
Spring AOP 中,通过通知(Advice)来定义横切逻辑,支持 5 种类型的 Advice;
通知类型 | 连接点 | 接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.springframework.aop.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中添加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
AOP 实际上是基于 Java 动态代理来实现,因此主要有以下三种实现方式:
package com.cunyu.service;
/**
* @InterfaceName : UserService
* @Author : cunyu
* @Date : 2020/7/20 8:36
* @Version : 1.0
* @Description : 业务接口
**/
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
package com.cunyu.service;
/**
* @author : cunyu
* @version : 1.0
* @className : UserServiceImpl
* @date : 2020/7/20 8:37
* @description : 业务接口实现类
*/
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add user");
}
@Override
public void delete() {
System.out.println("delete user");
}
@Override
public void update() {
System.out.println("update user");
}
@Override
public void search() {
System.out.println("search user");
}
}
package com.cunyu.service;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* @author : cunyu
* @version : 1.0
* @className : StrenthUSerAfter
* @date : 2020/7/20 8:48
* @description : 后置增强
*/
public class StrenthUSerAfter implements AfterReturningAdvice {
/**
* @param o 返回值
* @param method 被调用的方法
* @param objects 被调用方法的对象的参数
* @param o1 被调用的目标对象
* @return
* @description TODO
* @date 2020/7/20 8:49
* @author cunyu1943
* @version 1.0
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(o1.getClass().getName() + " 类的 " + method.getName() + " 方法被执行,返回值是 " + o);
}
}
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Bean 注册 -->
<bean id="userService" class="com.cunyu.service.UserServiceImpl"/>
<bean id="userBefore" class="com.cunyu.service.StrenthUSerBefore"/>
<bean id="userAfter" class="com.cunyu.service.StrenthUSerAfter"/>
<!-- AOP 配置 -->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.cunyu.service.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="userAfter" pointcut-ref="pointcut"/>
</aop:config>
</beans>
package com.cunyu.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : cunyu
* @version : 1.0
* @className : UserServiceTest
* @date : 2020/7/20 8:58
* @description : 测试类
*/
public class UserServiceTest {
@Test
public void testAdd() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
}
}
package com.cunyu.service;
/**
* @author : cunyu
* @version : 1.0
* @className : MyPointCut
* @date : 2020/7/20 9:08
* @description : 自定义切入类
*/
public class MyPointCut {
public void after() {
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Bean 注册 -->
<bean id="userService" class="com.cunyu.service.UserServiceImpl"/>
<bean id="myPointCut" class="com.cunyu.service.MyPointCut"/>
<!-- AOP 配置 -->
<aop:config>
<aop:aspect ref="myPointCut">
<aop:pointcut id="mypointcut1" expression="execution(* com.cunyu.service.UserServiceImpl.*(..))"/>
<aop:after pointcut-ref="mypointcut1" method="after"/>
</aop:aspect>
</aop:config>
</beans>
package com.cunyu.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : cunyu
* @version : 1.0
* @className : UserServiceTest
* @date : 2020/7/20 8:58
* @description : 测试类
*/
public class UserServiceTest {
@Test
public void testDelete() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.delete();
}
}
package com.cunyu.config;
import org.aspectj.lang.annotation.After;
/**
* @author : cunyu
* @version : 1.0
* @className : AnnotationPointCut
* @date : 2020/7/20 9:39
* @description : 基于注解实现的增强类
*/
public class AnnotationPointCut {
@After("execution(* com.cunyu.service.UserServiceImpl.*(..))")
public void after() {
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Bean 注册,并增加支持注解的配置 -->
<bean id="userService" class="com.cunyu.service.UserServiceImpl"/>
<bean id="annotationPointCut" class="com.cunyu.config.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>
</beans>
package com.cunyu.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : cunyu
* @version : 1.0
* @className : UserServiceTest
* @date : 2020/7/20 8:58
* @description : 测试类
*/
public class UserServiceTest {
@Test
public void testUpdate() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.update();
}
}
注意: <aop:aspectj-autoproxy/>
声明会自动为 Spring 容器中配置 @Aspect
切面的 Bean 创建代理,植入切面。但在 Spring 内部,依然采用AnnotationAwareAspectJAutoProxyCreator
进行自动代理的创建工作,只是具体实现的细节已经被 <aop:aspectj-autoproxy />
隐藏起来了。此外,还有一个proxy-target-class
属性,默认为false
,表示使用 Jdk 动态代理注入增强,此时如果目标类没有声明接口,则 Spring 将自动使用CGLib
动态代理。当为 true
时,表示使用 CGLib
动态代理技术注入增强。
- END -
往期精选