Spring框架之AspectJ的概念详解和应用(三)
AspectJ通知类型
aop联盟定义通知类型,具有特性接口,必须要有实现类,从而确定方法名称。aspectJ通知类型,只定义类型名称,以及方法格式个数。
- before:前置通知(应用各种校验)
在方法执行前执行,如果通知抛出异常,阻止方法运行。 - afterReturing:后置通知(应用在常规数据处理)
方法正常返回后执行,如果方法中抛出异常,通知无法执行,必须在方法执行后才能够执行,所以可以获取方法的返回值。 - around:环绕通知(应用十分强大,可以做任何事情)
方法执行异常后执行,如果方法没有抛异常,无法执行。 - after:最终通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常。
aspect常用配置如下
aop:after 最终
aop:after-returing 后置
aop:after-throwing 抛出异常
aop:around 环绕
aop:declare-parents 引介
aop:pointcut 切入点
aop:before 前置
Aspect切面实战:
切面类:
public class MyAspect {
public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
System.out.println("异常通知:" + e.getMessage());
}
public void myBefore(JoinPoint joinPoint) {
System.out.println("前置通知");
}
public Object myAround(ProceedingJoinPoint pip) throws Throwable {
System.out.println("前置通知");
Object proxyObj = pip.proceed();
System.out.println("后置通知");
return proxyObj;
}
}
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.zzx.service.impl.UserServiceImpl"/>
<bean id="myAspect1" class="com.zzx.aspect.MyAspect1"/>
<aop:config>
<aop:aspect ref="myAspect1">
<aop:pointcut id="myPointCut" expression="execution(* com.zzx.service.impl.UserServiceImpl.*(..))"/>
<!-- <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>-->
<!-- <aop:before method="myBefore" pointcut-ref="myPointCut"/>-->
<aop:around method="myAround" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
输出结果:
image.png
以上是环绕通知的实战实例,其中pip.proceed()表示放行,并把返回的结果作为方法的返回值。
AspectJ简介
AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切面表达式的支持,@AspectJ是AspectJ1.5新增功能通知JDK5注解技术,允许直接在Bean类中定义切面。新版本Spring框架建议使用AspectJ方式来开发AOP。
切入点表达式
execution用于描述方法
语法:execution(修饰符 返回值 包 类 方法名(参数) throw异常 修饰符一般省略)。
public 公共方法
- 任意
- 返回值,不能省略
void 返回没有值
String 返回值是字符串
*任意 - 包省略
例如:
com.zzx.crm 固定包
com.zzx.crm..service crm包下面子包任意
com.zzx.crm.. crm包下面的所有的子包,包括它自己
com.zzx.crm..service.. crm包下面任意子包,固定目录service service目录下的任意包。 - 类【省略】
UserServiceImpl 指定类
Impl 以Impl结尾
User 以User开头
*任意 - 方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
*任意
参数详解
within:匹配包或子包中的方法 within(com.zzx.aop.*)
this: 匹配实现接口的目标对象中的方法this(com.zzx.aop.user.UserDao)
target:匹配实现接口的目标对象中的方法target(com.zzx.aop.user.UserDao)
声明使用注解:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.zzx"/>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--业务类-->
<bean id="userService" class="com.zzx.service.impl.UserServiceImpl"/>
<!--切面类-->
<bean id="myAspect" class="com.zzx.aspect.MyAspect"/>
<aop:config>
<aop:aspect ref="myAspect"/>
</aop:config>
</beans>
切面类
@Component
@Aspect()
public class MyAspect {
@Before("execution(* com.zzx.service.impl.UserServiceImpl.*(..))")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知.............." + joinPoint.getSignature().getName());
}
}
注意 @Aspect和@Component是成对出现,且@Aspect使用默认的即可,目前最新的AspectJ使用自定义切面会报错,具体原因不知道为啥,如果有读者知道的,可以在评论解释下。
Service类
@Service("userService")
public class UserServiceImpl implements IUserService {
private UserDaoImpl userDao = new UserDaoImpl();
public void setUserDao(UserDaoImpl userDao) {
this.userDao = userDao;
}
@Override
public void addUser(User user) {
userDao.save(user);
}
@Override
public void updateUser(User user) {
userDao.update(user);
}
@Override
public void delete(String username) {
userDao.delete(username);
}
}
声明公共切入点
@Component
@Aspect()
public class MyAspect {
@Before("myPointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知.............." + joinPoint.getSignature().getName());
}
@Pointcut("execution(* com.zzx.service.impl.UserServiceImpl.*(..))")
public void myPointcut() {
}
}
注解详解
以上是使用前置通知,后置通知和其他通知代码相似,我这里就不贴出来了,有兴趣的读者可以自己写下。接下来我们来总结下。
@Aspect 声明切面,修饰切面类,从而获取到通知。
@Before前置
@AfterReturing 后置
@Around环绕
@AfterThrowing 抛出异常
@After最终
@Pointcut 修饰方法private void xxx(){}之后通过方法名获得切入点引用。