标签:ann 支持 express student type 函数 返回值 类型 tostring
异常:
java.lang.IllegalArgumentException:error at ::0 formal unbound in pointcut(返回值参数出错)
AOP:面向方面编程
a.切面(Aspect):一个横切功能的模块化,这个功能可能会横切多个对象(业务)。(一个方法就是一个“切面”)
b.切入点(Pointcut):可以插入“横切逻辑”的方法。
c.通知(Advice):
1.前置通知(Before Advice):在切入点XXX()方法执行之前,插入通知。
2.后置通知(After Returning Advice):在切入点XXX()方法执行完毕之后,插入通知。
3.异常通知(After Throwing Advice):当切入点XXX()方法抛出异常时,插入的通知。
4.最终通知(After Finally Advice):当切入点XXX()方法执行完毕时,插入的通知(不论是正常返回还是异常退出)。
5.环绕通知(Around Advice):可以贯穿切入点XXX()方法执行的整个过程。
普通类 --》特定功能的类
继承类、实现接口、注解、配置
普通类 --->“通知”
一、实现接口
通知类型 需要实现的接口 接口中的方法
前置通知 org.springframework.aop.MethodBeforeAdvice before()
后置通知 org.springframework.aop.MethodReturningAdvice afterReturning()
异常通知 org.springframework.aop.ThrowsAdvice 无
环绕通知 org.springframework.aop.MethodInterceptor invoke()
前置通知实现步骤:
1.jar
aopaliance.jar
aspectjweaver.jar
2.配置
3.编写
aop:每当执行add()之前 自动执行的一个方法log()
add();业务方法
package com.lv.jee.spring.comment; public class Student { private int stuNo; private String stuName; private int stuAge; public int getStuNo() { return stuNo; } public void setStuNo(int stuNo) { this.stuNo = stuNo; } public String getStuName() { return stuName; } public void setStuName(String stuName) { this.stuName = stuName; } public int getStuAge() { return stuAge; } public void setStuAge(int stuAge) { this.stuAge = stuAge; } }
package com.lv.jee.spring.comment; import org.springframework.stereotype.Component; import com.lv.jee.spring.dao.IStudentDao; /* * <bean id="studentDao" class="com.lv.jee.spring.comment.StudentDaoImpl" ></bean> * */ @Component("studentDao") public class StudentDaoImpl implements IStudentDao{ public void addStudent(Student student) { System.out.println("增加学生。。。。"); } }
log(); 自动执行的通知,即aop前置通知
expression="execution(...)"
在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"
package com.lv.jee.spring.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /** * 前置通知 * @author Bin * @date2019年3月21日 */ public class LogBefore implements MethodBeforeAdvice{ //前置通知的具体内容 @Override public void before(Method method, Object[] args, Object target) throws Throwable { // TODO Auto-generated method stub System.out.println("前置通知"); } }
<!-- 通知所在的类 -->
<!-- ==========连接线的一方=========== -->
<bean id="logBefore" class="com.lv.jee.spring.aop.LogBefore">
</bean>
<!-- 前置通知 -->
<!-- 将方法所在类和通知进行关联 -->
<aop:config>
<!-- 配置切入点(在哪里执行通知) -->
<!-- ==========连接线的另一方=========== -->
<!--
<aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.addStudent(com.lv.jee.spring.comment.Student))" id="poioncutadd"/>
<aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.deleteStudentByNo(int))" id="poioncutdel"/>
-->
<aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.deleteStudentByNo(int)) or execution(public void com.lv.jee.spring.service.StudentServiceImpl.addStudent(com.lv.jee.spring.comment.Student))" id="poioncut"/>
<!-- 相当于链接切入点和切面的线 -->
<!-- ==========连接线=========== -->
<!--
<aop:advisor advice-ref="logBefore" pointcut-ref="poioncutadd"/>
<aop:advisor advice-ref="logBefore" pointcut-ref="poioncutdel"/>
-->
<aop:advisor advice-ref="logBefore" pointcut-ref="poioncut"/>
</aop:config>
例如定义切入点表达式 execution(* com.sample.service.impl..*.*(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型,*号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
后置通知:(方法等正常执行完)
a.通知类 普通类实现接口
b.业务类、业务方法
c.配置(连接线)(1.将业务类、通知 纳入SpringIoC容器 2.定义切入点(一端)、定义通知类(另一端),通过pointcut-ref将两端连接起来)
package com.lv.jee.spring.aop; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; /* *|后置通知,实现接口 */ public class LogAfter implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { // TODO Auto-generated method stub System.out.println("后置通知:目标对象"+target+", 调用的方法名"+method.getName()+",方法的参数个数:"+args.length+",方法的返回值:"+returnValue); } }
<!-- 将通知纳入SpringIoC容器 -->
<bean id="logAfter" class="com.lv.jee.spring.aop.LogAfter">
</bean>
<!-- 后置通知 -->
<aop:config>
<!-- 切入点(连接线的一端:业务类的具体方法) -->
<aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.addStudent(com.lv.jee.spring.comment.Student))" id="poioncut2"/>
<!-- 连接线 -->
<aop:advisor advice-ref="logAfter" pointcut-ref="poioncut2"/>
<!--
<aop:advisor advice-ref="com.lv.jee.spring.aop.LogAfter" pointcut-ref="poioncut2"/>
-->
</aop:config>
异常通知:(方法等正常执行完)
根据异常通知接口的定义可以发现,异常通知的实现类必须编写以下方法:
public void afterThrowing([Method, args, target], ThrowableSubclass);
a.public void afterThrowing(ThrowableSubclass);
b.public void afterThrowing(Method, args, target, ThrowableSubclass);
package com.lv.jee.spring.aop; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; public class LogException implements ThrowsAdvice { //异常通知的具体方法 public void afterThrowing(Method method,Object[] args,Object target, Throwable ex) { System.out.println("异常通知:目标对象:"+target+",方法名:"+method.getName()+",方法的参数个数:"+args.length+",异常类型"+ex.getMessage()); } }
<!-- 异常通知 -->
<bean id="logException" class="com.lv.jee.spring.aop.LogException">
</bean>
<aop:config>
<aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.addStudent(com.lv.jee.spring.comment.Student))" id="poioncut3"/>
<aop:advisor advice-ref="logException" pointcut-ref="poioncut3"/>
</aop:config>
环绕通知:在目标方法的前后、异常发生时、最终等各个地方都可以进行通知。
最强大的一个通知:可以获取目标方法的全部控制权(目标方法是否执行、执行之前、执行之后、参数、返回值等)。
在使用环绕通知时,目标方法的一切信息,都可以通过invocation参数获取到。
环绕通知底层是通过拦截器实现的。
package com.lv.jee.spring.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class LogAround implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // TODO Auto-generated method stub //方法体1 Object result = null; try { //方法体2 System.out.println("用环绕通知实现的【前置通知】"); System.out.println("目标对象target:"+invocation.getThis()+", 调用的方法名:"+invocation.getMethod().getName()+ ",方法的参数个数:"+invocation.getArguments().length); //invocation.proceed(); 之前的代码:前置通知 result = invocation.proceed(); //控制着目标方法的执行,addStudent //result就是目标方法addStudent()方法的返回值 //invocation.proceed(); 之前的代码:后置通知 System.out.println("用环绕通知实现的【后置通知】"); System.out.println("目标对象target:"+invocation.getThis()+", 调用的方法名:"+invocation.getMethod().getName()+ ",方法的参数个数:"+invocation.getArguments().length+",方法的返回值:"+result); } catch (Exception e) { // TODO: handle exception //方法体3 //异常通知 System.out.println("用环绕通知实现的【异常通知】"); } return result; //目标方法的返回值 } }
<!-- 环绕通知 -->
<bean id="logAround" class="com.lv.jee.spring.aop.LogAround">
</bean>
<aop:config>
<!--
<aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.addStudent(com.lv.jee.spring.comment.Student))" id="poioncut4"/>
-->
<aop:pointcut expression="execution(public * com.lv.jee.spring.service.StudentServiceImpl.addStudent(..))" id="poioncut4"/>
<aop:advisor advice-ref="logAround" pointcut-ref="poioncut4"/>
</aop:config>
二、基于注解形式的AOP:
1.jar
aopaliance.jar
aspectjweaver.jar
2.配置
3.编写
1.将业务类、通知 纳入SpringIoC容器
纳入SpringIoC容器(注解或者Bean)
2.开启注解对AOP的支持--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
通知:
import org.aspectj.lang.annotation.Aspect;
@Aspect //声明该类是一个通知
public class LogAspectAnnotation {
}
注意:通过注解形式 将对象增加到 IoC容器时,需要设置扫描器
<context:component-scan base-package="com.lv.jee.spring.aop.annotation"></context:component-scan>
扫描器---会将指定的包中的:
@Component @Service @Respository @Controller修饰的类产生的对象 增加到IoC容器中。
通过注解形式 实现的AOP,如果想获取 目标对象的一些参数,则需要使用一个对象:JoinPoint
注解形式的返回值:
a.在注解的参数列表里声明返回值的参数名
b.注解形式实现AOP时,通知的方法的参数不能多、少
package com.lv.jee.spring.aop.schema; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class LogSchema { //后置通知----JoinPoint适用于注解、Schema public void afterReturningSchema(JoinPoint jp,Object returningValue) throws Throwable { // TODO Auto-generated method stub System.out.println("¥¥¥¥¥¥Schema后置通知"); } //前置通知 public void beforeSchema() throws Throwable { // TODO Auto-generated method stub System.out.println("¥¥¥¥¥¥¥Schema前置通知"); } //异常通知 public void afterThrowingSchema(JoinPoint jp,NullPointerException e) { System.out.println("¥¥¥¥¥¥¥Schema异常通知:"+e.getMessage()); } //环绕通知 //注意:环绕通知会返回目标方法的返回值,返回值类型Object public Object myAround(ProceedingJoinPoint jp){ System.out.println("环绕通知-------------前置通知"); Object result = null; try { result = jp.proceed(); System.out.println("环绕通知-------------后置通知"); } catch (Throwable e) { //这里的Exception要修改成Throwable // TODO: handle exception System.out.println("环绕通知-------------异常通知"); } finally { System.out.println("环绕通知-------------最终通知"); } return result; } }
package com.lv.jee.spring.aop.annotation; import java.util.Arrays; 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") 等价于<bean id="" class=""></bean> @Component("logAnnotation") //纳入SpringIoC容器 //依赖扫描器 @Aspect //声明该类为通知类 //依赖开启 public class LogAspectAnnotation { //没有指定包和类的话,addStudent(..)是任意包和类里的方法 @Before("execution(public * com.lv.jee.spring.service.StudentServiceImpl.addStudent(..))") //属性:定义切点 public void myBefore(JoinPoint jp) { System.out.println("#【注解形式--前置通知】:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+Arrays.toString(jp.getArgs())); } //addStudent(..)是任意包和类里的方法 //后置方法 @AfterReturning(pointcut="execution(public * addStudent(..))",returning="returningValue")//在前面添加returning="returningValue" public void myAfter(JoinPoint jp,Object returningValue) { //returningValue是返回值,但需要告诉Spring System.out.println("#【注解形式--后置通知】:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+Arrays.toString(jp.getArgs())+",返回值:"+returningValue); } //环绕通知 @Around("execution(public * com.lv.jee.spring.service.StudentServiceImpl.addStudent(..))") //属性:定义切点 public void myAround(ProceedingJoinPoint jp) { System.out.println("#【注解形式--环绕通知】:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+Arrays.toString(jp.getArgs())); //方法执行之前:前置通知 System.out.println("<[环绕]方法执行之前:前置通知>"); try { //方法执行时 jp.proceed(); //方法执行之后:后置通知 System.out.println("<[环绕]方法执行之后:后置通知>"); } catch (Throwable e) { // TODO: handle exception //方法执行发生异常时:异常通知 System.out.println("<[环绕]方法执行发生异常时:异常通知>"); } finally { //最终通知 System.out.println("<[环绕]最终通知>"); } } //异常通知:如果只捕获特点类型的已存在异常,则可以通过第二参数实现:e @AfterThrowing(pointcut="execution(public * com.lv.jee.spring.service.StudentServiceImpl.addStudent(..))",throwing="e") //属性:定义切点 public void myAfterThrowing(JoinPoint jp,NullPointerException e) { System.out.println("#【注解形式--异常通知】:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+Arrays.toString(jp.getArgs())); } //最终通知 @After("execution(public * com.lv.jee.spring.service.StudentServiceImpl.addStudent(..))") //属性:定义切点 public void myAfter(JoinPoint jp) { System.out.println("#【注解形式--最终通知】:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+Arrays.toString(jp.getArgs())); } }
三、通过配置将类-->通知(基于Schema配置)
类似于实现接口的方式
Schema方式通知:
a.编写一个普通类
b.将该类通过配置,转为一个“通知”
JoinPoint适用于注解、Schema
//环绕通知
public void myAround(ProceedingJoinPoint jp) {}
<!-- 开启注解对AOP的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- Component扫描 --> <!-- <context:component-scan base-package="com.lv.jee.spring.aop.annotation"> </context:component-scan> --> <bean id="" class="com.lv.jee.spring.aop.annotation.LogAspectAnnotation"> </bean> <!-- Schema配置 --> <!-- 将准备转为通知的类纳入SpringIoC容器 --> <bean id="logSchema" class="com.lv.jee.spring.aop.schema.LogSchema"> </bean> <aop:config> <!-- 切入点(连接线的一端:业务类的具体方法) --> <aop:pointcut expression="execution(public void com.lv.jee.spring.service.StudentServiceImpl.addStudent(com.lv.jee.spring.comment.Student))" id="poioncut5"/> <!-- 连接线 <aop:advisor advice-ref="logSchema" pointcut-ref="poioncut5"/> --> <!-- --> <aop:aspect ref="logSchema"> <!-- 连接线:连接业务 --> <aop:before method="beforeSchema" pointcut-ref="poioncut5"/> <!-- 连接线:连接业务 --> <aop:after-returning method="afterReturningSchema" pointcut-ref="poioncut5" returning="returningValue"/> <!-- 连接线:连接业务 --> <aop:after-throwing method="afterThrowingSchema" pointcut-ref="poioncut5" throwing="e"/> <aop:around method="myAround" pointcut-ref="poioncut5"/> </aop:aspect> </aop:config>
标签:ann 支持 express student type 函数 返回值 类型 tostring
原文地址:https://www.cnblogs.com/22428-bin/p/10662118.html