什么是AOP: AOP是面向切面的编程
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP的作用:在不影响我们正常的业务逻辑执行的前提下,加上一下我们程序必要的功能,比如下面这些:
- 日志的记录
- 权限的校验
- 事务的管理
- 性能的检测
AOP的底层实现机制: Jdk的动态代理:必须对有接口的实现类进行增强
Cglib的动态代理:没有任何要求,可以对我们没有实现接口的java类进行增强(关于动态代理我之前有一篇博客做了介绍,传送门:http://blog.youkuaiyun.com/likemebee/article/details/78074305)
Spring当中AOP的一些专业术语的介绍及图解
1、 目标类target:就是我们需要增强的那个类,target
2、 代理类proxy:就是我们自定义的那个代理的对象proxy
3、 连接点joinPoint:连接点说白了就是我们的目标类里面所有的方法。
4、 切入点pointCut:切入点说白了就是那些在目标类中我们实际上增强的方法。
5、 织入weave:说白了就是将我们的代理类中需要增强的方法放入到目标类中去执行的过程,就叫织入
6、 引介Introduction:是对我们的类中的方法或者属性进行一些创建的过程
7、 通知advice:说白了就是将我们代理对象中的方法应用到目标类的过程中产生的结果。实际上可以理解成就是删除之后的权限校验方法
8、 切面aspect:说白了就是我们的所有的切入点和代理对象的方法组成在一起 构成了我们的切面
Spring基于xml的方式开发我们的aop程序:
首先配置我们的接口与实现类OrderService与OrderServiceImpl
package com.yida.spring.demo01;
public interface OrderService {
public void add();
public int delete();
public void update();
public void find();
public void batch();
}
package com.yida.spring.demo01;
public class OrderServiceImpl implements OrderService {
@Override
public void add() {
System.out.println("add method invoke");
}
@Override
public int delete() {
System.out.println("delete method invoke");
return 10;
}
@Override
public void update() {
System.out.println("update method invoke");
}
@Override
public void find() {
System.out.println("find method invoke");
System.out.println(1/0);
}
@Override
public void batch() {
System.out.println("batch method invoke");
System.out.println(1/0);
}
}
创建我们的代理类OrderProxy
package com.yida.spring.demo01;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class OrderProxy {
/**
* 前置通知执行的方法
*/
public void checkPrivilege(){
System.out.println("权限的校验,我应该再add方法执行之前执行");
}
/**
* 后置通知执行的方法
*/
public void afterMethod(JoinPoint joinpoint,Object object){
System.out.println("我是后置通知,我需要在目标方法执行之后再执行");
System.out.println(object);
}
/**
* ProceedingJoinPoint 如果是环绕通知,我们必须配置一下这个参数,通过执行proceed()方法,才会执行我们目标列当中的目标方法
* @param joinPoint
* @throws Throwable
*/
public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
// 第一步:目标方法执行之前执行的逻辑
System.out.println("我是环绕通知,我会在目标方法执行之前进行执行");
// 第二步:执行我们的目标方法
Object proceed = joinPoint.proceed();
// 第三步:执行目标方法之后的逻辑
System.out.println("我是环绕通知,我会在目标方法执行之后进行执行");
}
/**
* 实现我们的最终通知
*/
public void finalMethod(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getDeclaringType());;
System.out.println(joinPoint.getSignature().getDeclaringTypeName());
System.out.println("我是最终通知,就算程序抛出异常我也会执行");
}
/**
* 实现我们的异常通知
*/
public void exceptMethod(Throwable throwable){
System.out.println(throwable.getMessage());
System.out.println("我是异常通知,只有在程序抛出异常的时候,我才会执行");
}
}
然后在我们的配置文件applicationContext.xml里配置相关信息如下:
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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">
<!-- 初始化我们的目标类和我们的代理类 -->
<bean id="orderServiceImpl" class="com.yida.spring.demo01.OrderServiceImpl"></bean>
<bean id="OrderProxy" class="com.yida.spring.demo01.OrderProxy"></bean>
<aop:config>
<!-- expression是我们的切入点表达式 -->
<!-- 指明我们的切入点 -->
<aop:pointcut expression="execution(* *.add*(..))" id="pointCut1"/>
<aop:pointcut expression="execution(* *.delete*(..))" id="pointCut2"/>
<!-- 环绕通知 -->
<aop:pointcut expression="execution(* *.update*(..))" id="pointCut3"/>
<!-- 最终通知 -->
<aop:pointcut expression="execution(* *.find*(..))" id="pointCut4"/>
<!-- 异常通知 -->
<aop:pointcut expression="execution(* *.batch*(..))" id="pointCut5"/>
<!-- 配置我们的切面类 -->
<aop:aspect ref="OrderProxy">
<!-- aop:before表示在我们目标方法执行之前进行增强 -->
<!-- 指明我们的增强的方法,作用在哪个目标方法上 -->
<aop:before method="checkPrivilege" pointcut-ref="pointCut1"/>
<!-- 后置通知使用的是我们的after-returning -->
<aop:after-returning method="afterMethod" pointcut-ref="pointCut2" returning="object"/>
<!-- 配置我们的环绕通知 -->
<aop:around method="aroundMethod" pointcut-ref="pointCut3" />
<!-- 配置我们的最终通知 -->
<aop:after-throwing method="finalMethod" pointcut-ref="pointCut4"/>
<!-- 配置实现我们的异常通知 -->
<aop:after-throwing method="exceptMethod" pointcut-ref="pointCut5" throwing="throwable"/>
</aop:aspect>
</aop:config>
</beans>
注意spring当中切入点表达式的写法,有三个参数:1、方法的修饰符 2、方法的返回值 /3、方法的路径
基本用法如下:
execution(public * *(..)) :任何以public修饰的方法都可以被增强
execution(* set*(..)):以set开头的方法都可以被增强
execution(* com.xyz.service.AccountService.*(..)) :AccountService这个类下面的所有的方法都可以被增强
execution(* com.xyz.service..(..)) com.xyz.service这个包下面的所有的类,以及所有的方法都可以被增强
execution(* com.xyz.service...(..)) 表示com.xyz.service 这个包,以及这个包下面的所有的子包,所有类所有的方法都可以被增强
代理类OrderProxy:
package com.yida.spring.demo01;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class OrderProxy {
/**
* 前置通知执行的方法
*/
public void checkPrivilege(){
System.out.println("权限的校验,我应该再add方法执行之前执行");
}
/**
* 后置通知执行的方法
*/
public void afterMethod(JoinPoint joinpoint,Object object){
System.out.println("我是后置通知,我需要在目标方法执行之后再执行");
System.out.println(object);
}
/**
* ProceedingJoinPoint 如果是环绕通知,我们必须配置一下这个参数,通过执行proceed()方法,才会执行我们目标列当中的目标方法
* @param joinPoint
* @throws Throwable
*/
public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
// 第一步:目标方法执行之前执行的逻辑
System.out.println("我是环绕通知,我会在目标方法执行之前进行执行");
// 第二步:执行我们的目标方法
Object proceed = joinPoint.proceed();
// 第三步:执行目标方法之后的逻辑
System.out.println("我是环绕通知,我会在目标方法执行之后进行执行");
}
/**
* 实现我们的最终通知
*/
public void finalMethod(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getDeclaringType());;
System.out.println(joinPoint.getSignature().getDeclaringTypeName());
System.out.println("我是最终通知,就算程序抛出异常我也会执行");
}
/**
* 实现我们的异常通知
*/
public void exceptMethod(Throwable throwable){
System.out.println(throwable.getMessage());
System.out.println("我是异常通知,只有在程序抛出异常的时候,我才会执行");
}
}
最后测试类TestSpring:
package com.yida.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.yida.spring.demo01.OrderService;
public class TestSpring {
@Test
public void testAOP(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderService bean = (OrderService) context.getBean("orderServiceImpl");
//bean.add();
//bean.delete();
//bean.update();
//bean.find();
bean.batch();
}
}