AOP
AOP即为面向方面编程。
一个普通的类 -> 有特定功能的类
a.继承类 b.实现接口 c.注解 d.配置
注解、schema:JoinPoint
接口:Method method, Object[] args, Object target
都要将类放入容器中:xml放或注解扫描
首先导入jar包
aopaliance.jar
aspectjweaver.jar
(一)基于接口
1、前置(后置)
(1)编写类及方法
//实现MethodBeforeAdvice接口,普通类->前置通知֪
public class LogBefore implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知 "); System.out.println("目标对象"+target+",方法名"+method.getName()+",方法参数个 数"+args.length+"方法返回值"+returnValue);
}
}
(2)配置xml
<!-- 配置前置通知 -->
<!-- 1、将方法放入容器 -->
<!-- addStudent()所在方法 -->
<bean id="studentService" class="org.lanqiao.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
<!-- “前置通知”类 -->
<bean id="logBefore" class="org.lanqiao.aop.LogBefore">
</bean>
<!-- 2、将addStudent()和通知进行关联 -->
<aop:config>
<!-- 事务类配置切入点(业务类方法) ,注意方法名的写法!!!参数写详细类型-->
<aop:pointcut expression=
"execution(
public void org.lanqiao.service.impl.StudentServiceImpl.deleteStudentByNo(org.lanqiao.entity.Student)
)
or
execution(
public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..)
)"
id="poioncut"/>
<!--将二者连接起来 advisor:相当于链接切入点和切面的线 advice-ref:前置方法ID-->
<aop:advisor advice-ref="logBefore" pointcut-ref="poioncut"/>
</aop:config>
2、异常通知
(1)编写类及方法
异常通知的实现类必须编写该方法:
public void afterThrowing([Method, args, target], ThrowableSubclass)即
a.public void afterThrowing(Method, args, target, ThrowableSubclass)或
b.public void afterThrowing( ThrowableSubclass)
public class LogException implements ThrowsAdvice {
//必须为public
public void afterThrowing(Method method, Object[] args ,Object target, Exception ex)
{
System.out.println("目标对象"+target+",方法名"+method.getName()+",方法参数个 数"+args.length+"异常类型"+ex.getMessage());
}
}
(2)xml配置相似
3、环绕通知
在目标方法的前后、异常发生时、最终等各个地方都可以进行的通知,最强大的一个通知;
可以获取目标方法的全部控制权(目标方法是否执行、执行之前、执行之后、参数、返回值等)
在使用环绕通知时,目标方法的一切信息 都可以通过invocation参数获取到。
环绕通知底层是通过拦截器实现的。
(1)编写类及方法
public class LogAround implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = null ;
//前置通知
try {
//中间,控制目标方法的执行,没有这句话则实际操作不会进行
Object result = invocation.proceed() ;
//后置通知
System.out.println("目标对象"+invocation.getThis()+"方法 名,"+invocation.getMethod().getName()+",方法参数个数"+invocation.getArguments().length+"返回值,"+result);
}catch(Exception e) {
//异常通知
}
return result;
}
}
(2)xml配置相似
(二)基于注解
先导入jar包,与实现接口的方式相同。
(1)xml配置
<!-- 开启注解对AOP的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置新包的扫描器 -->
<context:component-scan base-package="org.lanqiao.aop">
</context:component-scan>
扫描器会将指定的包中的@Componet @Service @Respository @Controller修饰的类产生的对象增加到IOC容器
(2)编写类及方法
org/lanqiao/aop/LogAspectAnnotation.java
//将通知类纳入容器,相当于在xml配置<bean id="logAnnotation" class="org.lanqiao.aop.LogAspectAnnotation">
@Component("logAnnotation")
@Aspect //声明此类是一个通知֪
public class LogAspectAnnotation {
//前置通知֪
@Before("execution(public * addStudent(..))") //定义切点
public void myBefore(JoinPoint jp) {
System.out.println("目标对象"+jp.getTarget()+",方法名"+jp.getSignature().getName() +"参数列表"+Arrays.toString(jp.getArgs())+",参数个数"+ jp.getArgs().length );
}
//后置通知֪
@AfterReturning( pointcut= "execution(public * addStudent(..))" ,returning="returningValue" ) //声明返回值
public void myAfter(JoinPoint jp,Object returningValue) {
System.out.println("返回值"+returningValue );
}
//环绕通知
@Around("execution(public * addStudent(..))")
public void myAround(ProceedingJoinPoint jp ) {
//前置通知֪
System.out.println("这里是前置通知");
try {
//中间
jp.proceed() ;
//后置通知֪
System.out.println("这里是后置通知");
}catch(Throwable e) {
//异常通知֪
System.out.println("这里是异常通知");
}finally {
//最终通知֪
System.out.println("这里是最终通知");
}
}
//异常通知
@AfterThrowing(pointcut="execution(public * addStudent(..))",throwing="e")//声明异常为e
//指定只会捕获空指针异常
public void myException(JoinPoint pj, NullPointerException e) {
System.out.println("异常为"+e.getMessage());
}
//最终通知֪
@After("execution(public * addStudent(..))")
public void myAfter() {
System.out.println("666");
}
}
注意:后置通知返回值及异常通知的异常需声明
(三)基于Schema配置
a.编写一个普通类
有一些与注解相似
public class LogSchema {
//前置通知֪
public void myBefore(JoinPoint jp) {
System.out.println("目标对象"+jp.getTarget()+",方法名"+jp.getSignature().getName() +"参数列表"+Arrays.toString(jp.getArgs())+",参数个数"+ jp.getArgs().length );
}
//后置通知֪,returning="returningValue" ) //声明返回值
public void myAfter(JoinPoint jp,Object returningValue) {
System.out.println("返回值"+returningValue );
}
//环绕通知
public void myAround(ProceedingJoinPoint jp ) {
//前置通知֪
System.out.println("这里是前置通知");
Object result = null ;
try {
//中间
result=jp.proceed() ;
//后置通知֪
System.out.println("这里是后置通知");
}catch(Throwable e) {
//异常通知֪
System.out.println("这里是异常通知");
}finally {
//最终通知֪
System.out.println("这里是最终通知");
}
return result;
}
//异常通知
//指定只会捕获空指针异常
public void myException(JoinPoint pj, NullPointerException e) {
System.out.println("异常为"+e.getMessage());
}
}
b.将该类通过配置,转为一个“通知”
<!-- 将准备转为通知的类纳入ioc容器 -->
<bean id="logSchema" class="org.lanqiao.aop.LogSchema"></bean>
<!-- addStudent()所在方法 -->
<bean id="studentService" class="org.lanqiao.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
<aop:config>
<!-- 切入点(连接线的一端:业务类的具体方法) -->
<aop:pointcut expression="execution(public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..))" id="pcShema"/>
<!-- schema方式连接线连接-->
<aop:aspect ref="logSchema">
<!-- 前置 -->
<aop:before method="before" pointcut-ref="pcShema"/>
<!-- 后置,有返回值需声明-->
<aop:after-returning method="afterReturning" returning="returnValue" pointcut-ref="pcShema"/>
<!-- 异常,有异常需声明 -->
<aop:after-throwing method="whenException" pointcut-ref="pcShema" throwing="e"/>
<!-- 环绕,有返回值???声明吗?-->
<aop:around method="around" pointcut-ref="pcShema" />
</aop:aspect>
</aop:config>