1. 介绍
通过aspectJ,使用aspectJ,就不用像之前一样,切面类需要继承一些切面父类了,可以随意自定义一个切面类,然后配置一下就可以了,可以通过配置的方式,也可以通过注解的方式
2. xml配置方式
实现类
package com.test.spring.aspectJ; public class UserServiceImpl implements UserService{ public void addUser() { System.out.println("添加用户"); } public void deleteUser() { System.out.println("删除用户"); } public void editUser() { System.out.println("修改用户"); } }
切面类
public class MyAspect { public void monitorBefore() { System.out.println("执行前"); } }
xml配置
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 实现类 --> <bean id="userService" class="com.test.spring.aspectJ.UserServiceImpl"></bean> <!-- 切面类 --> <bean id="myAspect" class="com.test.spring.aspectJ.MyAspect"></bean> <aop:config> <!-- 切面配置 ref 对应面类--> <aop:aspect ref="myAspect" > <!-- 切入点, expression对应执行方法的公式,这个公式很好理解,声明类型 返回值 包名.类名.方法名(参数类型) throws 抛出异常全限定名 id 切入点ID --> <aop:pointcut expression="execution(* com.test.spring.aspectJ.UserServiceImpl.addUser())" id="addPointCut"/>
<!-- aop:before就是切入的类型,这里使用before意思就是在方法之前调用,method就是切入的方法,pointcut-ref就是切入点ID,
这里还可以自定义表达式,pointcut就是自己重新定义切入点表达式,这里使用pointcut或者pointcut-ref都可以 -->
<aop:before method="monitorBefore" pointcut-ref="addPointCut"/> </aop:aspect> </aop:config> </beans>
运行测试类
package com.test.spring.aspectJ; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAspectJ { @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("/com/test/spring/aspectJ/springConfig.xml"); UserService userservice = context.getBean("userService",UserService.class); userservice.addUser(); userservice.deleteUser(); userservice.editUser(); } }
运行结果,可以看出,切面先执行切面方法在执行目标方法,并且可以看出,因为表达式中只对addUser的方法进行切入,所以只要在添加用户前才进行切入
执行前
添加用户
删除用户
修改用户
这里我们注意的是所有的切入类型,如aop:before 或者aop:after,都可以没有入参,或者有一个或以上的参数,默认第一个参数为 Joinpoint, 这个对象里面包含了很多切入点的信息,包括切入的方法名,类名,等等的一些信息
3.不同切入类型配置和切入类的写法
- aop:before
<aop:before method="monitorBefore" pointcut-ref="addPointCut"/>
切面类方法
public void monitorBefore(JoinPoint joinPoint) { System.out.println("执行前"); }
- aop:after-returning
这里的return等于切入方法monitorAfter入参中,第二个参数名,用于告诉切入方法返回的值是什么
<aop:after-returning method="afterReturning" pointcut-ref="addPointCut" returning="ret"/>
切面类方法
/** * 参数1 连接点的描述,包括目标函数的名称,字段 * 参数2 类型Object,需要在配置指定名称 与这里的名称一致 * @param joinPoint * @param ret */ public void afterReturning(JoinPoint joinPoint, Object ret) { System.out.println("After Returning"); }
- aop:around
<aop:around method="myAround" pointcut-ref="addPointCut"/>
切面类方法,这里必须手动调用目标方法
/** * 环绕 * @param joinPoint * @return * @throws Throwable */ public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("前"); Object obj = joinPoint.proceed(); System.out.println("后"); return obj; }
- aop:after-throwing
这里的throwing等于入参中的第二个方法,告诉切入点,异常的对象,当目标切入点方法执行并抛出异常,机会进入到这个切面类型当中
<aop:after-throwing method="myThrow" pointcut-ref="addPointCut" throwing="e"/>
切面类方法
public void myThrow(JoinPoint joinPoint, Throwable e) { System.out.println("异常了"); }
- aop:after
<aop:after method="monitorAfter" pointcut-ref="addPointCut"/>
切面类方法
/** * * @param joinPoint 目标函数 */ public void monitorAfter(JoinPoint joinPoint) { System.out.println("执行后"); }
3.基于注解
基于下面的xml配置,把它转化成注解的方式
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="userService" class="com.test.spring.aspectJ.UserServiceImpl"></bean> <bean id="myAspect" class="com.test.spring.aspectJ.MyAspect"></bean> <aop:config> <aop:aspect ref="myAspect" > <aop:pointcut expression="execution(* com.test.spring.aspectJ.UserServiceImpl.addUser())" id="addPointCut"/> <aop:before method="monitorBefore" pointcut-ref="addPointCut"/> <aop:after-returning method="monitorAfter" pointcut-ref="addPointCut" returning="ret"/> <aop:around method="myAround" pointcut-ref="addPointCut"/> <aop:after-throwing method="myThrow" pointcut-ref="addPointCut" throwing="e"/> <aop:after method="myThrow" pointcut-ref="addPointCut"/> </aop:aspect> </aop:config> </beans>
这里分几个步骤
- xml配置中,把所有的内容都去掉,只剩下组件扫描和aspectJ自定代理配置
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <context:component-scan base-package="com.test.spring.aspectJ.anno"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
- 在应用的bean中添加组件注解和切面类中添加注解
实现类声明是bean
package com.test.spring.aspectJ.anno; import org.springframework.stereotype.Component; @Component("UserServiceImpl") public class UserServiceImpl implements UserService{ public void addUser() { System.out.println("添加用户"); } public void deleteUser() { System.out.println("删除用户"); } public void editUser() { System.out.println("修改用户"); } }
切面类
package com.test.spring.aspectJ.anno; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class MyAspect { }
- 切面类方法添加注解
这里注意的是,我们可以在注解当中,假如只有一个属性,我们可以省略value=,假如是两个属性,必须有value值,表达式也可以写一个公共类,value只要指定对应的方法名即可
声明公共表达式的方式,调用的时候,还需要@Before(value="pointCut()")即可,或者不用value, @Before("pointCut()")
@Pointcut("execution(* com.test.spring.aspectJ.anno.UserServiceImpl.deleteUser())") private void pointCut() { }
@Before
@Before("execution(* com.test.spring.aspectJ.anno.UserServiceImpl.deleteUser())") public void monitorBefore(JoinPoint joinPoint) { System.out.println("执行前"); }
@AfterReturning
@AfterReturning(value="pointCut()",returning="ret") public void afterReturning(JoinPoint joinPoint, Object myReturn) { System.out.println("After Returning"); }
@Around
@Around(value="pointCut()") public Object myRound(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("前"); Object obj = joinPoint.proceed(); System.out.println("后"); return obj; }
@AfterThrowing
@AfterThrowing(value="pointCut()",throwing = "e") public void afterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("异常了"); }
@After
@After(value="pointCut()") public void monitorAfter(JoinPoint joinPoint) { System.out.println("执行后"); }