AOP中的概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.(类)
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring AOP中一个连接点代表一个方法的执行(方法)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.(范围)
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(具体实现)
Target(目标对象):代理的目标对象 (目标对象)
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
使用Spring进行面向切面(AOP)编程
要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
</beans>
Spring提供了两种切面声明方式
1.基于XML配置方式声明切面。
2.基于注解方式声明切面。
基于注解方式声明切面
1、启用注解 <aop:aspectj-autoproxy/>
2、采用Aspect定义切面
3、在Aspect定义Pointcut和Advice
4、Aspect类和目标对象配置到Ioc容器中,目标对象要实现接口
注意:在这种方法定义中,切入点的方法是不被执行的,它存在的目的仅仅是为了重用切入点
即Advice中通过方法名引用这个切人点
@Aspect
public class LogPrint {
// 定义Pointcut,Pointcut的名称就是anyMethod,此方法不能有返回值和参数,该方法只是
//一个 标识Pointcut的内容是一个表达式,描述那些对象的那些方法(订阅Joinpoint)
@Pointcut("execution(* cn.itcast.service..*.*(..))")
private void anyMethod() {}//声明一个切入点
// 定义Advice,标识在那个切入点何处织入此方法
@Before("anyMethod() && args(userName)")
//前置通知,拦截Pointcut指定范围的方法并且方法的参数只有1个String类型的参
//数 ,userName为方法传过来的参数
public void doAccessCheck(String userName) {}
@AfterReturning(pointcut="anyMethod()",returning="revalue")
//后置通知,拦截Pointcut指定的范围的方法并且方法的返回值是1个String类型的,
//revalue 为方法传过来的返回值
public void doReturnCheck(String revalue) {}
@AfterThrowing(pointcut="anyMethod()", throwing="ex")
//例外通知,ex为抛出异常的信息
public void doExceptionAction(Exception ex) {}
@After("anyMethod()")
//最终通知
public void doReleaseAction() {}
@Around("anyMethod()")
//环绕通知 此方法的除方法名和方法参数名可变,其他默认的。只有返回pjp.proceed(),
//拦截到的方法才会执行
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入方法");
return pjp.proceed();
System.out.println("结束方法");
}
基于基于XML配置方式声明切面
public class LogPrint {
public void doAccessCheck() {}定义前置通知
public void doReturnCheck() {}定义后置通知
public void doExceptionAction() {}定义例外通知
public void doReleaseAction() {}定义最终通知
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();环绕通知
}
}
<bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
<bean id="log" class="cn.itcast.service.LogPrint"/>
<aop:config>
<aop:aspect id="myaop" ref="log">
<aop:pointcut id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/>
<aop:before pointcut-ref="mycut" method="doAccessCheck"/>
<aop:after-returning pointcut-ref="mycut" method="doReturnCheck "/>
<aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
<aop:after pointcut-ref="mycut" method=“doReleaseAction"/>
<aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
</aop:aspect>
</aop:config>
execution表达式的例子。
任意公共方法的执行:
execution(public * *(..))
任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
Aspect默认情况下不用实现接口,但对于目标对象,在默认情况下必须实现接口
如果没有实现接口必须引入CGLIB库.
我们可以通过Advice中添加一个JoinPoint参数,这个值会由spring自动传入,从JoinPoint中可以取得拦截到方法的参数值、方法名等等
public class SecurityHandler {
private void checkSecurity(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //取得方法参数
for (int i=0; i<args.length; i++) {
System.out.println(args[i]);
}
System.out.println(joinPoint.getSignature().getName()); //取得方法名称
System.out.println("----------checkSecurity()---------------");
}
}