Spring系列教程——13AspectJ讲解
上一篇:Spring系列教程——12配置文件实现AOP
下一篇:Spring系列教程——14Spring数据库操作讲解
一.Aspect简介及简单实例
1.概念
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式支持
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
在使用AspectJ框架之前我们需要在前面章节的基础上导入jar包
:
链接:https://pan.baidu.com/s/1YdulOzREOplari0y0slVuA
提取码:rfek
2.AspectJ通知类型
aspectj 通知类型,只定义类型名称,以及方法格式
。
个数
:6种,知道5种,掌握1中。
before:前置通知(应用:各种校验)
在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)
方法正常返回后执行,如果方法中抛出异常,通知无法执行
必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情)
方法执行前后分别执行,可以阻止方法的执行
必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息)
方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常
3.案例讲解
接下来我们看一个案例:
定义一个切面类MyAspect2
package aspect;
public class MyAspect2 {
public void myBefore(){
System.out.println("我的前置通知");
}
public void myAfterReturning(){
System.out.println("我的后置通知");
}
}
目标类还是前面章节的:
package service;
public class UserServiceImpl implements IUserService {
@Override
public void addUser() {
System.out.println("添加User");
}
@Override
public void deleteUser() {
System.out.println("删除User");
}
@Override
public void updateUser() {
System.out.println("更新User");
}
}
配置文件内容为:
<?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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标类-->
<bean id="userService" class="service.UserServiceImpl"></bean>
<!--配置切面类-->
<bean id="aspect" class="aspect.MyAspect2"></bean>
<aop:config proxy-target-class="true">
<aop:aspect ref="aspect">
<aop:pointcut id="myPointCut" expression="execution(* service.UserServiceImpl.*(..))"/>
<!--前置通知-->
<aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before>
<!--后置通知-->
<aop:after-returning method="myAfter" pointcut-ref="myPointCut"></aop:after-returning>
</aop:aspect>
</aop:config>
</beans>
测试代码为:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
IUserService userService = (IUserService) context.getBean("userService");
userService.updateUser();
小结:这里我们发现使用AspectJ框架比上一章的全自动还要简洁,我们的切面类根本不需要去实现接口,在这里只要有目标类,切面类,配置就OK了
二.基于XML文件配置的讲解
上一小节我们已经做了一些初步的认识了,这里我们再来继续深入的讲解。上一小节我们为大家演示了前置通知与后置通知。
环绕通知:
//MyAspect2新添下面方法
public void myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知");
System.out.println("正在使用的方法是:"+pjp.getSignature().getName());
System.out.println("方法的参数为"+pjp.getArgs());
System.out.println("前置代码");
//在使用环绕通知时需要将方法放行
pjp.proceed(pjp.getArgs());
System.out.println("后置代码");
}
注意环绕通知需要在切面类的对应方法上加上ProceedingJoinPoint
类型参数,因为需要将方法放行才可以生效,当然在我们的上一小节里面的前置与后置
的讲解中我们也可以在myBefore与myAfterReturning方法里面添加JoinPoint
类型的参数来获取方法的相关信息(比如方法名字等
),还有一点需要注意的就是在后置通知中如果需要获取方法的返回值可以这么写:
//retObj这个参数便是返回值,不过此时还需要在xml文件里面把配置后置通知的地方加上,像这样:
//<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"></aop:after-returning>指定ret为返回值
public void myAfterReturning(Objiect retObj){
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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标类-->
<bean id="userService" class="service.UserServiceImpl"></bean>
<!--配置切面类-->
<bean id="aspect" class="aspect.MyAspect2"></bean>
<aop:config proxy-target-class="true">
<aop:aspect ref="aspect">
<aop:pointcut id="myPointCut" expression="execution(* service.UserServiceImpl.*(..))"/>
<aop:around method="myAround" pointcut-ref="myPointCut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
异常通知:
//myApspect2添加新的方法
public void myAfterThrowing(JoinPoint jp,Throwable throwable){
System.out.println("异常通知:"+throwable.getMessage());
}
异常通知的配置相关为:
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="throwable"></aop:after-throwing>
在UserServiceImpl
的deleteUser
方法里面加上:
int i=10/0;
还是前面的测试代码,测试结果如下:
最终通知
//myAspect2里面添加下面方法
public void myAfter(){
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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标类-->
<bean id="userService" class="service.UserServiceImpl"></bean>
<!--配置切面类-->
<bean id="aspect" class="aspect.MyAspect2"></bean>
<aop:config proxy-target-class="true">
<aop:aspect ref="aspect">
<aop:pointcut id="myPointCut" expression="execution(* service.UserServiceImpl.*(..))"/>
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="throwable"></aop:after-throwing>
<aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
还是之前的测试代码,测试结果如下:
我们发现即使在有异常的情况下最终通知仍然会执行。当然没有异常也会执行,不过它都是在最后执行。
三.基于注解配置的讲解
首先我们需要开启注解,初始的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: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/contetxt
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置扫描注解-->
<context:component-scan base-package="aspect"></context:component-scan>
<context:component-scan base-package="service"></context:component-scan>
<!--配置aop注解生效-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
我们来看我们前面实现前置通知的xml文件配置内容:
<!--配置目标类-->
<bean id="userService" class="service.UserServiceImpl"></bean>
<!--配置切面类-->
<bean id="aspect" class="aspect.MyAspect2"></bean>
<aop:config proxy-target-class="true">
<!--声明切面-->
<aop:aspect ref="aspect">
<!--切入点-->
<aop:pointcut id="myPointCut" expression="execution(* service.UserServiceImpl.*(..))"/>
<!--前置通知-->
<aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before>
</aop:aspect>
</aop:config>
</beans>
接下来我们一个一个用注解替换这个前置通知的例子:
1.替换目标业务类(UserServiceImpl
)的bean对象和切面类(myAspect2
)的bean对象:
2.声明切面
3声明前置通知
注意这里的execution表达式已经说明了切入点的位置,因此不需要再来声明切入点。
还是之前的测试代码,结果如下:
接下来我们展示其他的通知:
1.后置通知
我们在这里停一下,我们每次写通知注解的时候,都要写execution(* service.UserServiceImpl.*(..))
这么长的东西,所以我们可以选择一个公共的方式:
在MyAspect2切面类里面添加下面内容:
@Pointcut("execution(* service.UserServiceImpl.*(..))")
public void myPointCut(){
}
上面这个方法名可以随便取,在接下来我们将使用这一方式来简化。
2.环绕通知
3.异常通知与最终通知
这里不再演示结果。
上一篇:Spring系列教程——12配置文件实现AOP
下一篇:Spring系列教程——14Spring数据库操作讲解