样例代码:
原始类:
@Repository
public class UserDaoImp implements UserDao {
@Override
public int addUser() {
System.out.println("add user ......");
return 6666;
}
@Override
public void updateUser() {
System.out.println("update user ......");
}
@Override
public void deleteUser() {
System.out.println("delete user ......");
}
@Override
public void findUser() {
System.out.println("find user ......");
}
}
@Aspect
public class MyAspect {
/**
* 前置通知
*/
@Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public void before(){
System.out.println("前置通知....");
}
/**
* 后置通知
* returnVal,切点方法执行后的返回值
*/
@AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
public void AfterReturning(Object returnVal){
System.out.println("后置通知...."+returnVal);
}
/**
* 环绕通知
* @param joinPoint 可用于执行切点的类
* @return
* @throws Throwable
*/
@Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前....");
Object obj= (Object) joinPoint.proceed();
System.out.println("环绕通知后....");
return obj;
}
/**
* 抛出通知
* @param e
*/
@AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出现异常:msg="+e.getMessage());
}
/**
* 无论什么情况下都会执行的方法
*/
@After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public void after(){
System.out.println("最终通知....");
}
}
编写配置文件交由Spring IOC容器管理
<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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 启动@aspectj的自动代理支持-->
<aop:aspectj-autoproxy />
<!-- 定义目标对象 -->
<bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
<!-- 定义aspect类 -->
<bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
</beans>
编写测试类:
/**
* Created by zejian on 2017/2/19.
* Blog : http://blog.youkuaiyun.com/javazejian [原文地址,请尊重原创]
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= "classpath:spring/spring-aspectj.xml")
public class UserDaoAspectJ {
@Autowired
UserDao userDao;
@Test
public void aspectJTest(){
userDao.addUser();
}
}
————————————————
版权声明:本文为优快云博主「zejian_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/javazejian/article/details/56267036
结果输出:
结论:
@After 被代理的方法执行完成之后,要执行的代码。
@AfterReturning 新生成的代理方法执行完成之后(return 之后),要执行的代码,是放在finally块中的。
备注:
很多人在评论区认为结论写反了, 可能是由于Spring官方文档也说After是最终(finally)通知,应该被最后执行。
Types of advice:
-
Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
-
After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
-
After throwing advice: Advice to be executed if a method exits by throwing an exception.
-
After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).
为此,我拿aspectj的工具写了测试代码,如下:
public class Boss {
public static void main(String[] args) {
Boss boss = new Boss();
boss.meeting();
}
public void meeting() {
System.out.println("The boss is in a meeting");
}
}
public aspect BossAspect{
//定义切点
pointcut bossPoint() : execution(* meeting(..));
//前置通知,对切点增强
before() : bossPoint(){
System.out.println("before advise");
}
//after通知
after() : bossPoint(){
System.out.println("after advise");
}
//after returning通知
after() returning: bossPoint(){
System.out.println("after returning advise");
}
}
然后执行如下命令编译:
./ajc ../src/*.java -injars /Users/tt0008641/IdeaProjects/aspectj/lib/aspectjrt.jar
然后对生成的Boss.class反编译,得到如下结果:
public class Boss {
public Boss() {
}
public static void main(String[] args) {
Boss boss = new Boss();
boss.meeting();
}
public void meeting() {
try {
BossAspect.aspectOf().ajc$before$BossAspect$1$7ba14e64();
System.out.println("The boss is in a meeting");
} catch (Throwable var2) {
BossAspect.aspectOf().ajc$after$BossAspect$2$7ba14e64();
throw var2;
}
BossAspect.aspectOf().ajc$after$BossAspect$2$7ba14e64();
BossAspect.aspectOf().ajc$afterReturning$BossAspect$3$7ba14e64();
}
}
可以看到,最后两行代码就是Aspectj为我们添加的代码,一个是AfterAdvise的织入代码,一个是AfterReturning织入的代码。他们都没有放入finally块中,换句话说没有finally块。他们都是在被代理方法执行完成之后执行的,只是after织入方法先被执行,after returning后被执行。而且通过反编译代码可以发现,如果有异常,在catch块中接着继续抛出异常,after和afterreturning的方法(上边最后两行)代码都不被执行。
当然在Spring中,并没有使用Aspect 的编译器来编译,而只是借鉴了他的语法,同时使用jdk动态代理或者cglib的方式来生成代理对象,至于生成的对象长什么样子,被代理的方法和各种织入的代码之间的执行顺序,大家有兴趣可以继续深入探讨一下(由于spring是直接在内存中生成代理对象的字节码,我们没办法找到对应的文件进行反编译哈),我看网上有的文章说当异常发生时,所有的织入代码都会被执行,顺序是这样的顺,对此保持怀疑啊,大家有兴趣可以继续研究下:
有异常的情况
@Around start
@Before...
method invoke
@After...
@AfterThrowing...
try{
try{
//@Around start
//@Before
method.invoke(..);
//@Around end
}finally{
//@After
}
//@AfterReturning
}catch(){
//@AfterThrowing