AOP 面向切面编程 将一个方法add()【这个方法可以存在于多个对象中】定义为切入点,在执行这个方法时【可以在执行之前插入通知,执行之后插入通知】
类——>通知 方式:
编写通知所需要的jar包:
public class LogBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知——目标对象:"+target+"调用方法:"+method.getName()+"参数个数:"+args.length);
}
}
public void addstudent(Student student) {
// studentDao = null;//测试异常通知
studentDao.addstudent(student);
}
<bean id="logBefore" class="org.motingyu.aop.LogBefore">bean>
<aop:config>
<aop:pointcut expression=" execution(public void org.motinyu.service.impl.StudentServiceImpl.addstudent(org.motinyu.entity.Student )) " id="poioncut"/>
<aop:advisor advice-ref="logBefore" pointcut-ref="poioncut"/>
aop:config>
环绕通知(实现接口MethodInterceptor)在目标方法的前后,异常发生时,最终等各个地方都可以进行的通知
【最强大的通知:可以获取目标方法的全部控制权(目标方法是否执行,执行之前,执行之后的参数,返回值等)】
在使用环绕通知时,目标方法的一切通知都可以通过invocation参数取得。
环绕通知底层是通过拦截器实现的。
public class LogAround implements MethodInterceptor {
Object result = null;
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
System.out.println("用环绕通知实现的【前置通知】......:目标对象:"+invocation.getThis()+"调用方法:"+invocation.getMethod().getName());
//invocation.proceed()之前的代码是前置通知
result = invocation.proceed(); //控制着目标方法的执行,addStudent();
//result 就是目标方法addStudent()的返回值;
//invocation.proceed()之前的代码是后置通知
System.out.println("用环绕通知实现的【后置通知】......");
}catch(Exception e) {
//异常通知
System.out.println("用环绕通知实现的【异常通知】......");
}
return null;
}
}
异常通知 需要实现接口ThrowsAdvice (发生异常,触发通知)【实现同上】
根据异常通知接口的定义可以发现,异常通知的实现类必须编写以下方法
public void afterThrowing([Method, args, target], ThrowableSubclass)
[]里的参数,要么都有,要么都没有
需要在配置文件中开启对AOP的支持
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
需要注意的是:
@Component("logAnnotation") // 类前 将该类纳入IOC容器中 别忘配置扫描器
@Aspect // 类前 声明此类是一个通知类
@Before("execution()") // 方法前加 :定义切入点 前置通知 获取目标参数 JoinPoint
@AfterReturning( pointcut ="execution()",returning= "") //后置通知 添加返回值
@AfterThrowing( pointcut="execution()",throwing = "") //异常通知:如果只捕捉特定异常信息,则可以通过第二个参数实现:e
@Around("execution()") //环绕通知 获取目标信息使用 参数 ProceedingJoinPoint
基于注解方式的通知,应为通知类,业务类都在一个类中有联系,故不需要在配置文件中进行连接。
package org.motingyu.aop;
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.springframework.stereotype.Component;
@Component("logAnnotation") //将该类纳入IOC容器中
@Aspect //声明此类是一个通知类
public class LogAspectAnnotation {
//前置通知
@Before("execution(public * org.motinyu.service.impl.StudentServiceImpl.addstudent(..))") //属性:定义切点
public void myBefore() {
System.out.println("<<注解形式——前置通知>>");
}
//后置通知 对象参数 使用JoinPoint ,返回值 returningValue需要在注解中声明
@AfterReturning( pointcut ="execution(public * org.motinyu.service.impl.StudentServiceImpl.addstudent(..))",returning= "returningValue")
public void myAfter(JoinPoint jp,Object returningValue) {
System.out.println("<<注解形式——后置通知>>:目标对象:"+jp.getTarget()+"方法名:"+jp.getSignature().getName()+"参数列表个数:"+jp.getArgs().length+"返回值:"+returningValue);
}
//异常通知:如果只捕捉特定异常信息,则可以通过第二个参数实现:e
@AfterThrowing( pointcut="execution(public * org.motinyu.service.impl.StudentServiceImpl.addstudent(..))",throwing = "e")
public void myException(JoinPoint jp,NullPointerException e) {//此异常只会捕捉NullPointerException这个类型的异常
System.out.println("<<$$$注解形式——异常通知>>----------e:"+e.getMessage());
}
//环绕通知 对象参数与别个不一样 使用 ProceedingJoinPoint
@Around("execution(public * org.motinyu.service.impl.StudentServiceImpl.addstudent(..))")
public void myAround(ProceedingJoinPoint jp) {
//方法执行之前:前置通知
try {
//方法执行时
jp.proceed();
//方法执行之后:后置通知
System.out.println("<<[环绕]注解形式——后置通知>>:");
}catch(Throwable e) {
//发生异常:异常通知
System.out.println("<<[环绕]注解形式——异常通知>>:");
}finally {
//最终通知
System.out.println("<<[环绕]注解形式——最终通知>>:");
}
}
//最终通知
@After("execution(public * org.motinyu.service.impl.StudentServiceImpl.addstudent(..))")
public void myAfter() {
System.out.println("<<[myFter]注解形式——最终通知>>:");
}
}
基于schema配置方式的通知,自己创建一个普通的类,在配置文件中,将此类配置成为通知类,并将通知类和业务类连接起来。
public class LogSchema {
//前置通知
public void myBefore() {
System.out.println("###[Schema形式]-----前置通知");
}
//后置通知 返回值 returningValue需要在配置文件中声明
public void myAfter(JoinPoint jp,Object returningValue) {
System.out.println("###[Schema形式]-----后置通知:目标对象:" +jp.getThis()+"目标方法:"+jp.getSignature().getName()+"参数个数:"+jp.getArgs().length+"返回值:"+returningValue);
}
//环绕通知 对象参数与别个不一样 使用 ProceedingJoinPoint
public Object myAround( ProceedingJoinPoint jp) {
Object result = null;
System.out.println("!!!!!###[Schema形式环绕通知]-----前置通知");
try {
result = jp.proceed(); //控制目标方法
System.out.println("!!!!!###[Schema形式环绕通知]-----后置通知");
}catch(Throwable e) {
System.out.println("!!!!!###[Schema形式环绕通知]-----异常通知");
}
return result;
}
//异常通知 捕捉特定异常 参数e 需要在配置文件中声明
public void myException(NullPointerException e) {
System.out.println("KKKKK###[Schema形式]-----异常通知:e" +e.getMessage());
}
}
在配置文件中,将刚才写的类配置为通知,并连接通知类与业务类:
【注意】配置标签与基于实现接口方式的通知略微不同;
<aop:config>
<aop:pointcut expression=" execution(public * org.motinyu.service.impl.StudentServiceImpl.addstudent(.. )) " id="pointschema"/>
<aop:aspect ref="logSchema">
<aop:before method="myBefore" pointcut-ref="pointschema"/>
<aop:after-returning method="myAfter" returning="returningValue" pointcut-ref="pointschema"/>
<aop:around method="myAround" pointcut-ref="pointschema" />
<aop:after-throwing method="myException" pointcut-ref="pointschema" throwing="e"/>
aop:aspect>
aop:config>