aspectJ开发aop
选择spring的AOP还是AspectJ?
spring有自己的AOP。功能已经基本够用了,除非你的要在接口上动态代理或者方法拦截精确到getter和setter。
在使用AOP的时候,用xml还是注解的方式(@Aspect)?
1)如果使用xml方式,不需要任何额外的jar包。
2)如果使用@Aspect方式,你就可以在类上直接一个@Aspect就搞定,不用在xml里配了。但是这需要额外的jar包( aspectjweaver.jar)。因为spring直接使用AspectJ的注解功能,并不是核心功能 !!!
注意:无论是使用spring aop还是 aspectj都需要aspectjweaver.jar spring-aop.jar这两个jar包。
aspectJ是基于java开发的aop框架,自spring2.0以后,springaop引入对他支持。
使用aspectJ实现aop由两种方式:基于xml和基于注解
相对来说注解方式更加简单,开发中常用注解方式。
基于xml声明式aspectJ
package com.lx.aspectj.xml; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * 切面类֪ * @author Administrator * */ public class MyAspect { //前置通知֪ public void myBefore(JoinPoint joinPoint) { System.out.print("前置通知:模拟执行权限检查...,"); System.out.print("目标类是:"+joinPoint.getTarget()); System.out.println(",被植入增强目标为:"+joinPoint.getSignature()); } //后置通知֪ public void myAfterReturning(JoinPoint joinPoint) { System.out.println("后置通知:模拟记录日志...,"); System.out.println(",被植入增强目标为:"+joinPoint.getSignature().getName()); } /** * 环绕通知֪ * @param proceedingJoinPoint 是JoinPoint的子接口,表示可以执行目标方法 * @return Object * 必须接收一个参数,类型为ProceedingJoinPoint * @必须 throws Throwable */ public Object
myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ //开始 System.out.println("环绕开始:开启之前,模拟开启事务..."); //执行当前目标方法 Object obj=proceedingJoinPoint.proceed(); //结束 System.out.println("环绕结束:模拟关闭事务..."); return obj; } //异常通知֪ public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("异常通知:出错了"+e.getMessage()); } //最终通知֪ 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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 1 目标类
--> <bean id="userDao"
class="com.lx.jdk.UserDaoImpl"></bean> <!-- 2切面 --> <bean id="myAspect"
class="com.lx.aspectj.xml.MyAspect"></bean> <!-- 3aop编程 --> <aop:config> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 3.1配置切入点,通知最后增强哪些方法 --> <aop:pointcut expression="execution(*
com.lx.jdk.*.*(..))" id="myPointCut"></aop:pointcut> <!-- 3.2关联通知Advice和切入点pointCut --> <!-- 3.2.1前置通知 --> <aop:before method="myBefore"
pointcut-ref="myPointCut"></aop:before> <!-- 3.2.2后置通知,在方法返回之后执行,就可获得返回值 returing属性:用于设置后置通知的第二个参数的名称,类型Object--> <aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut"
returning="returnVal"></aop:after-returning> <!-- 3.2.3环绕通知 --> <aop:around method="myAround"
pointcut-ref="myPointCut"></aop:around> <!-- 3.2.4抛出异常 通知,如果程序无异常将不会执行增强, throwing属性:设置通知第二个参数的名称,类型Throwable--> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut"
throwing="e"></aop:after-throwing> <!-- 3.2.5最终通知:无论程序发生任何情况,都执行 --> <aop:after method="myAfter"
pointcut-ref="myPointCut"></aop:after> </aop:aspect> </aop:config> </beans> |
package com.lx.aspectj.xml; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lx.jdk.UserDao; public class TestXmlAspect { public static void main(String[] args) { // TODO Auto-generated method stub String xmlpath="com/lx/aspectj/xml/applicationContext.xml"; ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlpath); UserDao userDao=(UserDao)applicationContext.getBean("userDao"); userDao.addUser(); } } |
基于注解的声明式aspectJ
注解名称 |
描述 |
@Aspect |
定义一个切面 |
@Pointcut |
定义切面表达式 |
@Before |
定义前置通知,相当于BeforeAdvice |
@AfterReturning |
定义后置通知,相当于AfterReturningAdvice |
@Around |
定义环绕通知,相当于MethodIntercetor |
@AfterThrowing |
定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice |
@After |
定义最终final通知 |
根据需要在切入点不同位置的切入内容
使用@Before在切入点开始处切入内容
使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容
使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
使用@After在切入点结尾处切入内容
package com.lx.aspectj.annotation; 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; /** * 切面类֪ * @author Administrator * */ @Aspect @Component public class MyAspect { //定义切点表达式 @Pointcut("execution(* com.lx.jdk.*.*(..))") //使用一个返回值为void,方法体为空的方法来命名切入点 public void myPointCut(){}; //前置通知 @Before("myPointCut()") public void myBefore(JoinPoint joinPoint) { System.out.print("前置通知:模拟执行权限检查...,"); System.out.print("目标类是:"+joinPoint.getTarget()); System.out.println(",被植入增强目标为:"+joinPoint.getSignature()); } //后置通知 @AfterReturning("myPointCut()") public void myAfterReturning(JoinPoint joinPoint) { System.out.println("后置通知:模拟记录日志...,"); System.out.println(",被植入增强目标为:"+joinPoint.getSignature().getName()); } /** * 环绕通知 * @param proceedingJoinPoint 是JoinPoint的子接口,表示可以执行目标方法 * @return Object * 必须接收一个参数,类型为ProceedingJoinPoint * @必须 throws Throwable */ @Around("myPointCut()") public Object
myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ //开始 System.out.println("环绕开始:开启之前,模拟开启事务..."); //执行当前目标方法 Object obj=proceedingJoinPoint.proceed(); //结束 System.out.println("环绕结束:模拟关闭事务..."); return obj; } //异常通知 @AfterThrowing(value="myPointCut()", throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("异常通知:出错了"+e.getMessage()); } //最终通知 @After("myPointCut()") public void myAfter() { System.out.println("最终通知:模拟方法结束后的释放资源..."); } } |
package
com.example.springBootdemo.aop; import java.util.Objects; import
javax.servlet.http.HttpServletRequest; 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.slf4j.Logger; import
org.slf4j.LoggerFactory; import
org.springframework.core.annotation.Order; import
org.springframework.stereotype.Component; import
org.springframework.web.context.request.RequestContextHolder; import
org.springframework.web.context.request.ServletRequestAttributes; /** * 切面类,日志 * @author luolei * @date 2019年2月11日 */ @Aspect //声明为切面类 @Component @Order(1) //标记切点的优先级,i越小,优先级越高 public class MyAspectLog { //定义切点表达式:*,第一个返回值,第二个类名,第三个方法名 @Pointcut("execution(public
* com.example.springBootdemo.service.*.*(..))") //使用一个返回值为void,方法体为空的方法来命名切入点 public void myPointCut(){}; private final Logger logger = LoggerFactory.getLogger(getClass()); //前置通知 @Before("myPointCut()") public void myBefore(JoinPoint joinPoint) { logger.info("-----------@Before 前置通知-----------"); logger.info("前置通知:模拟执行权限检查..."); logger.info("目标类是={}", joinPoint.getTarget()); logger.info("被植入增强目标为={}", joinPoint.getSignature()); ServletRequestAttributes attributes=
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request =attributes.getRequest(); //url logger.info("请求路径URL={}",request.getRequestURL()); //method logger.info("请求方式Method={}",request.getMethod()); //ip logger.info("请求IP={}",request.getRemoteAddr()); //类方法 logger.info("请求类方法ClassMethod={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()); //参数 logger.info("请求参数Args={}",joinPoint.getArgs()); } //后置通知 /* @AfterReturning("myPointCut()") public void
myAfterReturning(JoinPoint joinPoint) { logger.info("-----------@AfterReturning
后置通知-----------"); logger.info("后置通知:模拟记录日志..."); logger.info("返回值 response={}",
joinPoint.toString()); }*/ //后置通知 @AfterReturning(returning = "objects", pointcut = "myPointCut()") public void myAfterReturning(Object objects) { logger.info("-----------@AfterReturning 后置通知-----------"); logger.info("后置通知:模拟记录日志..."); logger.info("返回值 response={}", objects); } /** * 环绕通知 * @param proceedingJoinPoint 是JoinPoint的子接口,表示可以执行目标方法 * @return Object * 必须接收一个参数,类型为ProceedingJoinPoint * @必须 throws Throwable */ @Around("myPointCut()") public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ //开始 logger.info("-----------@Around 环绕通知start-----------"); logger.info("环绕开始:开启之前,模拟开启事务..."); long time = System.currentTimeMillis(); //执行当前目标方法 Object obj=proceedingJoinPoint.proceed(); //结束 logger.info("环绕结束:模拟关闭事务..."); time = System.currentTimeMillis() - time; logger.info("方法用时Time={}", time +"(毫秒)"); logger.info("-----------@Around 环绕通知end-----------"); return obj; } //异常通知 @AfterThrowing(value="myPointCut()", throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { logger.info("-----------@AfterThrowing 异常通知-----------"); logger.info("异常通知:出错了"+ e); logger.info("异常通知:出错了getStackTrace={}", e.getStackTrace()); logger.info("异常通知:出错了Throwable...具体如下", e); } //最终通知 @After("myPointCut()") public void myAfter() { logger.info("-----------@After 最终通知-----------"); logger.info("最终通知:模拟方法结束后的释放资源..."); } } |
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <context:component-scan base-package="com.lx"/> <!—启动基于注解的aspectJ --> <aop:aspactj-autoproxy/> </beans> |
package com.lx.aspectj.annotation; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lx.jdk.UserDao; public class TestAnnotation { public static void main(String[] args) { // TODO Auto-generated method stub String xmlpath="com/lx/aspectj/annotaion/applicationContext.xml"; ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlpath); UserDao userDao=(UserDao)applicationContext.getBean("userDao"); userDao.addUser(); } } |