AI智能
改变未来

AspectJ如何实现注解配置切面切点?


什么是AspectJ

通俗易懂点,AspectJ是基于Java语言的Aop框架,就是用Java语言来实现,在Aop上更进一步实现简单化的操作。当然不懂AOP也没有办法去理解AspectJ。在前面的文章有详细讲解AOP概念使用方法。

传统AOP实现切面切点通过配置文件中配置,AspectJ通过注解的方式实现。和 bean 道理一样。可以将AspectJ理解是更简单化操作而出现的一种框架。

需要配置的文件

需要提前导入的Jar包
1.spring-aop.jar
2.spring-aspects.jar
3.com.springspurce.org.aopalliance.jar
4.com.springsource.org.aspectj.weaver.jar

在配置文件中配置 xsd文件

<?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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd\"><!--开启AspectJ扫描注解自动代理--><aop:aspectj-autoproxy/></beans>

实现AspectJ有两种方法,一种注解,一种xml文件配置,各有各的好处。

注解方式实现AspectJ框架

首先在配置文件中写上注解扫描,该扫描扫哪里?扫描该xml配置文件中的bean类,看哪个bean 中有注解。

<aop:aspectj-autoproxy/>

主要注解

AspectJ只需要设置一个切面类,在切面类通过注解实现通知的方法和切点即可,主要有这几种注解

@Before前置通知,相当于BeforeAdvice
@AfterReturning后置通知,相当于AfterReturningAdvice
@Around环绕通知,相当于MethodInterceptor
@AfterThrowing异常抛出通知,相当于ThrowAdvice
@After最终final通知,不管是否异常,该通知都会执行

execution语法规则

execution(注解?修饰符?返回值类型 类型声明?方法名(参数)异常?)
1.注解:可选,如要hock注解,则必须添加。例如execution(@java.lang.Override * *(. .))

2.修饰符:可选,如public,protected,写在返回值前;

3.返回值类型:必选,可以使用 * 来代表任意返回值;

4.类型声明:可选,可以是任意类型;

5.方法名:必选,可以用*来代表任意方法;

6.参数:()代表是没有参数,(. .)代表是匹配任意数量,任意类型的参数,当然也可以指定类型的参数进行匹配,如要接受一个String类型的参数,则(java.lang.String);任意数量的String类型参数:(java.lang.String. .);第一个是任意类型,第二个是String的参数匹配:(*,java.lang.String)等等;

7.异常:可选,语法:“throws 任意异常类型”,可以是多个,用逗号分隔,例如:throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。

注意:问号前面的都是可写可不写,没有问号的都是必写,例如返回值类型,和方法名。以上所有的类都需要是全类名写入,不能简写(就是你不能模糊用 * 代替)。

举几个例子:
execution(* *(. .)) 这是最基本的,意思是切点为所有返回值类
型,对所有包下的类的方法,因为就算你可选的不写,但是这两个必须写

execution(public * *(. .)) 可选的加进去后,可任意加都可识别,但是返回值和方法必须有,这里切点范围就为public类型,任意返回值,任意包下所有类的方法。

execution(void com.csdn.*(. .)) 对返回值为void,在包com.csdn下的所有类方法设置为切点(不包含子包,就是这个包下面没有包了,全是类)

execution(void com.csdn. .*(. .)) 对返回值为void,在包com.csdn下的子孙包和自身下的所有类方法设置为切点(这里两个点,上面那个一个点)

execution(* com.csdn.User.*(. .)) 对返回值类型任意,在com.csdn下具体类User里的方法设置为切点

所以execution里面必须有返回值类型,和方法名俩个筛选的基础,那些其余可以加上,将筛选条件范围减小。

注解如何实现切面

1.设置切面类,用@org.aspectj.lang.annotation.Aspect在类前申明该类为切面类(这样在bean配置时候,扫描注解可以节省时间)
2.在切面里设置通知方法,这里不像传统aop需要实现某个接口
3.在方法前面设置注解,什么类型的注解,最上面有解释,这里就是通知
4.在通知注解里使用execution设置切点范围
在一个类里面就实现了通知和切点,该类就成了切面类
5.在配置文件里申明bean,那么这个类里面的注解就被激活了,因为被

<aop:aspectj-autoproxy/>

扫描了,实现了功能。

实例

用一个简单的例子,这里就假设使用Cglib代理,假设一个用户类里有save和delete方法

public class User {public void save(){System.out.println(\"save方法\");}public void delete(){System.out.println(\"delete方法\");}}

只对User类里的save方法进行加强,前置通知的用户校验。

@org.aspectj.lang.annotation.Aspect      //首先声明该类为切面public class Aspect {//注解申明,你先定义任意一个函数,这里不需要传统的方式去实现哪个接口,直接在前面注解你要申明的是申明类型的通知//这里是一个前置通知,所以注解为Before,切点在before里的execution里设置@Before(\"execution(* com.maoge.demo1.User.(..))\")h\'lpublic void UserCheck(){System.out.println(\"用户校验\");}}

然后在配置文件种配置bean,首先配置文件配置好环境,可以看顶部代码

<aop:aspectj-autoproxy/><bean id=\"User\" class=\"com.maoge.demo1.User\"></bean><bean class=\"com.maoge.demo1.Aspect\"/>

那么此时切面就创建好了,同时也已经实现完了加强的操作。
下面写一个测试类

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(\"classpath:Application.xml\")  //配置文件public class test {@Resource(name = \"User\")private User user;     //实例化@Testpublic void tt(){user.save();user.delete();}}

出现以下测试结果

用户校验save方法delete方法

这里设置的切点范围为只对save方法进行拦截加强,所以只有调用save方法会触发

其他类型的通知注解

@AfterReturning(后置通知),具体操作和上面的before差不多,因为他是在函数执行完成才会执行,所以函数可能会return一个值,那么后置通知可能会接受到这个return,此时在AfterReturning里面有一个execution设置切点,还有一个returing参数来获得返回值,这里还是以上面那个为例子

//returning的参数名称随便定@AfterReturning(value = \"execution(* com.maoge.demo1.User.save(..))\",returning = \"djj\")public void UserCheck(Object djj)//这里Object定义的参数名称必须和上面的returning参数名称相同//Object是所有类的父类,因为不知道返回的是int还是String类型的值,所以用Object定义参数,Object起到封装的作用{System.out.println(\"用户校验\"+djj);}

设置save方法为

public String save(){System.out.println(\"save方法\");return \"=====\";}

那么测试的结果

save方法用户校验=====

可见通知获得了save方法返回的值 “=====”

@Around(环绕通知)可以在切入点前后执行操作,也可以控制切入点方法执行顺序,什么时候执行,获得了切入点的主动权,可以说环绕通知十分强大,和上面一样,在通知函数前面加上@Around即可声明该函数为环绕通知。在里面设置切点范围。但是定义通知函数返回值类型必须为Object类型,因为你调用了原函数,不知道返回的声明类型的值,所以用Object 进行封装。

@Around(value = \"execution(* com.maoge.demo1.User.save(..))\")public Object UserCheck(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println(\"用户校验前\");Object obj= joinPoint.proceed();    //此刻调用原来的函数System.out.println(\"用户校验后\");return obj;     //这里并不是调用原函数!}

会出现下面结果

用户校验前save方法用户校验后

可见环绕通知实现了控制原函数的主动权,可以调用原函数,也可以不调用,如果不写 joinPoint.proceed();即不调用原来函数。
会出现下面结果

用户校验前用户校验后

@AfterThrowing(异常抛出通知)也是用execution设置切点范围,这个更像监听器,当你切点方法出现了错误,就会执行该通知函数。

@After(最终final通知)用execution设置切点范围,当切点方法不管有没有异常,都会执行该通知函数

通过@Pointcut为切点命名

这里有个场景,在切面类定义了若个个通知函数,则需要在注解里面设置切点的范围

@Before(value = \"execution(* com.maoge.demo1.User.save(..))\")public void UserCheck1(){System.out.println(\"用户校验1\");}@AfterThrowing(value = \"execution(* com.maoge.demo1.User.save(..))\")public void UserCheck2(){System.out.println(\"用户校验2\");}@AfterReturning(value = \"execution(* com.maoge.demo1.User.save(..))\")public void UserCheck3(){System.out.println(\"用户校验3\");}

比如上面设置的三种通知,都是对save方法实现拦截,突然不用对save方法拦截了,对delete方法实现拦截,那怎么办,很简单直接将每个通知execution里面的切点范围变成delete就可以了,复制粘贴。但这显的不够装逼,一旦有了成百个,你复制粘贴一百次,不容易维护,很显然不可能的。那么将每个通知函数的切点范围都用一个值代替,每当需要改变所有通知的时候,改变这个代替值就可以了。相当于C语言里面的#define的作用。

那么怎么定义这个代替值?方法很简单用@Pointcut在切面类里面注解一个函数方法(private类型,无参,无返回值,就是一个空空的函数,将其函数名称用来代替名称),在@PointCut里面使用execution来标明切点的范围

//设置切点为delete方法@Pointcut(value = \"execution(* com.maoge.demo1.User.delete(..))\")private void myPointcut(){}@Before(value = \"myPointcut()\")public void UserCheck1(){System.out.println(\"用户校验1\");}@AfterThrowing(value = \"myPointcut()\")public void UserCheck2(){System.out.println(\"用户校验2\");}@AfterReturning(value = \"myPointcut()\")public void UserCheck3(){System.out.println(\"用户校验3\");}

将execution用函数名称代替即可。
这样只需要在每个通知切点value值用函数名称,在函数里面修改切点范围即可实现将所有通知的切点范围改变。若多个切点范围,可以在value里用 | | 并用。

总结

AspectJ注解方法就是将所有切面放到一个类里,然后定义一个函数,用注解同时实现通知类型和切点的范围,在配置文件配置相应的bean,使用扫描语句开启扫描,激活该切面类。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » AspectJ如何实现注解配置切面切点?