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 {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("接口实现的后置方法");
}
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("接口实现的后置方法");
}
}
public class MyAdvice3 implements MethodInterceptor {
@Override
public 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);
}
}