一. 什么是aop
- AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等.
- 在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。
- OP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
- 使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
二. 案例
1. 配置
-
导入maven依赖
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies>
-
导入
xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
2. 代码
-
接口
public interface UserService { public void add(); public void delete(); public void query(); public void update(); }
-
实现类
public class UserServiceImpl implements UserService { public void add() { System.out.println("添加"); } public void delete() { System.out.println("删除"); } public void query() { System.out.println("查询"); } public void update() { System.out.println("修改"); } }
3. AOP
1. 方法1: 使用原生Spring API接口
-
调用Spring API接口
public class Log implements MethodBeforeAdvice { /** * * @param method 要执行的目标对象的方法 * @param args 参数 * @param target 目标对象 * @throws Throwable */ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了"); } }
-
调用Spring API接口
public class AfterLog implements AfterReturningAdvice { /** * * @param returnValue 返回值 * @param method 方法 * @param args 参数 * @param target 对象 * @throws Throwable */ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" + target.getClass().getName() + "的" + method.getName() + "方法, 其返回值为: " + returnValue); } }
-
配置文件
<!--注册bean--> <bean id="userService" class="com.hjf.service.UserServiceImpl" /> <bean id="log" class="com.hjf.log.Log"/> <bean id="afterLog" class="com.hjf.log.AfterLog" /> <!--方式一: 使用原生Spring API接口--> <!--配置aop: 需要导入aop的约束--> <aop:config> <!--切入点: expression: 表达式, execution(要执行的位置)--> <!--对com.hjf.service.UserServiceImpl类下所有方法的所有参数生效--> <!-- 第一个*号: 表示返回值类型, *号表示返回所有的类型 包名: 表示需要拦截的包名 .*: 表示当前包下的所有类 *(..): *表示方法名(所有的方法),后面括号里面表示方法的参数。两个句号表示任何参数 --> <aop:pointcut id="point1" expression="execution(* com.hjf.service.UserServiceImpl.add(..))"/> <aop:advisor advice-ref="log" pointcut-ref="point1"/> <aop:advisor advice-ref="afterLog" pointcut-ref="point1" /> </aop:config>
-
测试
@Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = context.getBean("userService", UserService.class); userService.add(); System.out.println("-----------------"); userService.update(); }
2. 方法2: 方式二: 自定义类
-
自定义类
public class DiyPointCut { public void before(){ System.out.println("===方法执行前==="); } public void after(){ System.out.println("===方法执行后==="); } }
-
配置文件
<!-- 方式二: 自定义类--> <bean id="diy" class="com.hjf.diy.DiyPointCut"/> <aop:config> <!--自定义切面--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.hjf.service.UserService.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
-
测试
@Test public void test01() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 动态代理代理的是接口 // UserService userService = (UserService) context.getBean("userService"); UserService userService = context.getBean("userService", UserService.class); userService.add(); }
3. 方式3: 注解
-
注解类
// 使用注解方式实现AOP @Aspect public class AnnotationPointCut { @Before("execution(* com.hjf.service.UserServiceImpl.*(..))") public void before(){ System.out.println("注解-->方式执行前"); } @After("execution(* com.hjf.service.UserServiceImpl.*(..))") public void after(){ System.out.println("注解-->方式执行后"); } // 在环绕增强中, 我们可以给定一个参数, 代表我们要获取处理切入的点 @Around("execution(* com.hjf.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); Signature signature = jp.getSignature();// 获取签名 System.out.println("signature: " + signature); // 执行方法 Object proceed = jp.proceed(); System.out.println("环绕后"); System.out.println(proceed); } }
-
配置
<!--方式三: 注解--> <bean id="annotation" class="com.hjf.diy.AnnotationPointCut"/> <!--开启注解支持--> <aop:aspectj-autoproxy/>
-
测试
@Test public void test01() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 动态代理代理的是接口 // UserService userService = (UserService) context.getBean("userService"); UserService userService = context.getBean("userService", UserService.class); userService.add(); }