Java-Spring-AOP 实现
AOP即面向切面编程,是针对OOP编程编程的不足之处的补充计划。OOP通过引入封装,多态,继承等概念建立一种对象的层次结构,用于模拟公共行为的一个集合。
不过OOP定义的是对象的纵向关系,顶部总是高度抽象化的对象,底部总是具体的实现。但OOP不能适应定义横向的关系。比如日志功能,异常的处理,请求的安全检查等等,这些功能一般都存在散布在各个层次的代码中,又与具体的功能没有什么关联。在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP恰恰相反,它是定义了对象的横向关系,剖开对象的内部,讲那些会影响多个类的公共行为封装到单独的模块。这个模块称之为切面,原本分散的代码称之为横切。所谓的横切就是与业务逻辑无关的,却被多个业务所调用的逻辑的封装,以便减少代码冗余。
AOP把代码分成2个部分:核心关注点和横切关注点。核心关注点主要是功能部分,也就是业务逻辑代码块。与之关系不大的就是横切关注点。同一个横切关注点一般重复出现在核心关注点的某处,比如比如权限认证、日志、事物。将横切剥离出来,也就是把应用业务逻辑和系统服务分开。使得业务逻辑更加的纯粹。
核心概念
横切关注点
需要对那些方法进行拦截,进行怎么样的处理。
切面
具体的是对横切剥离后的公共模块,是横切关注点的抽象。
连接点
被拦截的位置。一般指的是具体的方法。
切入点
对连接点进行拦截的定义。
通知
拦截到连接点之后要执行的代码
目标对象
代理的目标对象
织入
将切面应用到目标对象并导致代理对象创建的过程
引入
运行期为类动态地添加一些方法或字段
基于aspectj框架实现最简单的aop
1.在pom中添加aspectj依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.5</version> </dependency>
2.添加切面类
package com.demo.oa.test; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Aspect public class AspectModule { // 配置切入点 是一个表达式 可以使用【within && || !】组合 这里表示user所有下一级类中的所有方法 // 切入点可能是一个普通的方法,也可能是一个构造方法,也可能是读写变量的行为,方法可分为方法的执行和调用。。。 @Pointcut("execution(* com.ywsoftware.oa.controller.user.*.*(..))") public void pointCut(){} // 本方法和下面的方法都使用Advice注解,指示代码在何时执行 // Before 表示方法执行之前 @Before("pointCut()") public void doBefore(JoinPoint joinPoint){ System.out.println("AOP Before Advice..."); } // After表示在方法执行之后执行 @After("pointCut()") public void doAfter(JoinPoint joinPoint){ System.out.println("AOP After Advice..."); } //AfterReturning表示在方法返回结果之后返回,可以访问方法的返回值 @AfterReturning(pointcut="pointCut()",returning="returnVal") public void afterReturn(JoinPoint joinPoint,Object returnVal){ System.out.println("AOP AfterReturning Advice:" + returnVal); } //AfterThrowing表示关联的方法若抛出异常则执行,可以访问该异常,并进行操作 @AfterThrowing(pointcut="pointCut()",throwing="error") public void afterThrowing(JoinPoint joinPoint,Throwable error){ System.out.println("AOP AfterThrowing Advice..." + error); System.out.println("AfterThrowing..."); } //around表示围绕着方法进行执行,上述四种的功能都能实现,其必须有返回值 @Around("pointCut()") public void around(ProceedingJoinPoint pjp){ System.out.println("AOP Aronud before..."); try { pjp.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("AOP Aronud after..."); } }
Tips:
切入点也是可以自定义的,可以让我们更加精确的切入一个或多个指定的切入点。
首先定义一个注解(也可以是个接口之类的)
@Retention(RetentionPolicy.CLASS) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface DebugTrace { }
在需要插入代码的地方使用这个注解
@DebugTrace public void logTest() { Log.e(TAG, "log test"); }
然后创建切入代码
@Pointcut("execution(@com.demo.oa.test.DebugTrace * *..*.*(..))") public void DebugTraceMethod() {} @Before("DebugTraceMethod()") public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable { String key = joinPoint.getSignature().toString(); Log.e(TAG, "beforeDebugTraceMethod: " + key); }
这个时候,所有使用@DebugTrace 注解的方法都会被代理。
3.注册bean 并启用aspectj
<bean id="log" class="com.test.oa.test.AspectModule"/> <aop:aspectj-autoproxy/>
4.调用结果
18:47:53 DEBUG - Returning cached instance of singleton bean 'log' AOP Aronud before... AOP Before Advice... AOP Aronud after... AOP After Advice... AOP AfterReturning Advice:null