spring的aop
aop
aop,面向切面编程。面向对象编程时对一个对象进行抽象。而面向切面编程时对不同的事物进行抽象,例如将不同的类中的方法都统一提出来,集中对这些方法进行操作。
aop实现原理
aop的底层是通过一个BeanDefinition注册一个AspectJAwareAdvisorAutoProxyCreator的bean,这个bean是个BeanPostProcess后处理器,而就是这个后处理器内部会调用aop封装的动态代理方法,底层有两个方法,分别是JDK和Cglib。
而注解方法和XML配置方式的底层都是相同,就是进入底层代码是调用的接口不同。
aop的基本概念
名词 | 解释 |
---|---|
Target(目标对象) | 被增强的方法的对象 |
Proxy( 代理对象) | 对目标对象进行增强后的对象 |
Joinpoint(连接点) | 目标对象中可以被增强的方法 |
Pointcut(切入点) | 目标对象中实际被增强的方法 |
Advice(通知\\增强) | 增强部分的代码逻辑 |
Aspect(切面) | 增强和切入点的结合 |
Weaving(织入) | 将通知和切入点动态结合的过程 |
aop的五种通知
通知名称 | 执行时机 |
---|---|
前置通知 | 目标方法执行之前执行 |
后置通知 | 目标方法执行之后执行,当方法异常时,不执行 |
环绕通知 | 目标方法前后执行,方法异常时,后环绕方法不执行 |
异常通知 | 目标方法抛出异常时执行 |
最终通知 | 目标方法无论是否有异常,最后都会执行 |
aop的切点表达式的配置
aop的两种切面配置方式
execution([访问修饰符] 返回的类型 包名.类名.方法名(参数))
当然在配置表达式时可以使用通配符
// .. 表示任意的参数
execution([访问修饰符] 返回的类型 包名.类名.方法名(..))// * 表示任意类或方法
execution([访问修饰符] 返回的类型 包名.*.*(..))// 包名和类名之间使用双点表示该包极其子包下的类
execution([访问修饰符] 返回的类型 包名..*.*(..))
aspect进行配置
xml标签配置
<aop:config><!--配置切点表达式--><aop:pointcut id="myPointcut" expression="execution(void com.service.impl.UserServiceImpl.show1())"/><aop:pointcut id="myPointcut3" expression="execution(void com.service.impl.UserServiceImpl.show2())"/><aop:pointcut id="myPointcut4" expression="execution(void com.service.impl.UserServiceImpl.show3())"/><!--使用通配符进行配置--><aop:pointcut id="myPointcut2" expression="execution(void com.service.impl.*.*(..))"/><!--配置织入,将切点和通知相结合--><!--aspect标签--><aop:aspect ref="myAdvice"><!--前置方法--><aop:before method="beforeAdvice" pointcut-ref="myPointcut"/><!--普通后置方法--><aop:after method="afterReturningAdvice" pointcut-ref="myPointcut"/><!--强制执行后置方法--><aop:after-returning method="afterAdvice" pointcut-ref="myPointcut" /><!--环绕方法--><aop:around method="around" pointcut-ref="myPointcut"/><!--只有有异常是才执行--><aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="myPointcut"/></aop:aspect></aop:config>
通知类配置
public class MyAdvice {public void beforeAdvice(JoinPoint joinPoint){System.out.println("当前对象是:"+joinPoint.getTarget());System.out.println("当前对象的表达式:"+joinPoint.getStaticPart());System.out.println("前置方法.....");}public void afterAdvice(){System.out.println("最终方法.....");}public void afterReturningAdvice(){System.out.println("后置方法.....");}public Object around(ProceedingJoinPoint joinPoint){try {System.out.println("前置环绕方法执行.....");Object proceed = joinPoint.proceed();System.out.println("后置环绕方法执行.....");return proceed;} catch (Throwable e) {e.printStackTrace();}return null;}public void afterThrowing(Throwable e){System.out.println("当前的异常的信息为:"+e);}}
当然这里的方法名只要一一对应就行,实际上想取什么名字都可以,但是一般还是取一些固定的名字,方便说明。
advisor配置
Xml配置
<aop:config><!--配置切点表达式--><aop:pointcut id="myPointcut" expression="execution(void com.service.impl.UserServiceImpl.show1())"/><aop:pointcut id="myPointcut3" expression="execution(void com.service.impl.UserServiceImpl.show2())"/><aop:pointcut id="myPointcut4" expression="execution(void com.service.impl.UserServiceImpl.show3())"/><!--使用通配符进行配置--><aop:pointcut id="myPointcut2" expression="execution(void com.service.impl.*.*(..))"/><!--advisor标签--><aop:advisor advice-ref="myAdvice2" pointcut-ref="myPointcut3"/><aop:advisor advice-ref="myAdvice3" pointcut-ref="myPointcut4"/></aop:config>
通知类配置
public class MyAdvice2 implements MethodBeforeAdvice, AfterReturningAdvice {@Overridepublic void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {System.out.println("接口实现的后置方法");}@Overridepublic void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println("接口实现的后置方法");}
}
public class MyAdvice3 implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {System.out.println("接口环绕前置方法执行");Object result = methodInvocation.getMethod().invoke(methodInvocation.getThis(), methodInvocation.getArguments());System.out.println("接口环绕后置方法执行");return result;}
}
这中方式需要我们的通知类实现对应的接口,之后重写我们的接口方法。于是,在XML配置中就不需要指定我们的方法了。
Aop注解方式
要想使用注解,我们需要在我们的XML配置配置一下标签
<!--开启AOP自动代理--><aop:aspectj-autoproxy/>
如果使用的的时配置类的话,需要加上@EnableAspectJAutoProxy标签
@Configuration
@ComponentScan("com")
@EnableAspectJAutoProxy
public class SpringConfig {
}
之后只需要在我们的通知类中加一个@Aspect标签,为每一个标签配置一下方法标签就可以了。
@Component
@Aspect
public class MyAdvice {//配置切点@Pointcut("execution(void com.service.impl.UserServiceImpl.show2())")public void pointcut(){}@Before("MyAdvice.pointcut()")public void beforeAdvice(JoinPoint joinPoint){System.out.println("当前对象是:"+joinPoint.getTarget());System.out.println("当前对象的表达式:"+joinPoint.getStaticPart());System.out.println("前置方法.....");}@AfterReturning("MyAdvice.pointcut()")public void afterAdvice(){System.out.println("最终方法.....");}@After("MyAdvice.pointcut()")public void afterReturningAdvice(){System.out.println("后置方法.....");}@Around("MyAdvice.pointcut()")public Object around(ProceedingJoinPoint joinPoint){try {System.out.println("前置环绕方法执行.....");Object proceed = joinPoint.proceed();System.out.println("后置环绕方法执行.....");return proceed;} catch (Throwable e) {e.printStackTrace();}return null;}@AfterThrowing(pointcut = "MyAdvice.pointcut()",throwing = "e")public void afterThrowing(Throwable e){System.out.println("当前的异常的信息为:"+e);}}