AOP:
1,切面 思想 :
这是一个思想,可以在代码上实现,
可以增加我们代码的扩展性,重用行,还可以提高开发效率.
底层通过动态代理实现.
spring框架优先使用 JDK的动态代理,如果不能使用会使用CGLIB实现.
2,名词解释:
目标对象 : 被代理的对象
连接点 : 被代理对象的方法
切入点(切点): 被代理对象的方法我们真正要增强方法
通知 : 我们要增强一些什么
切面 : 切入点+通知,也就是我们写的aop增强代码+配置文件
3,aop实现:
1)通知方式:
前置通知 : 目标方法之前增强 org.springframework.aop.MethodBeforeAdvice
后置通知 : 目标方法之后增强 org.springframework.aop.AfterReturningAdvice
环绕通知(**): 目标方法前后都增强 org.aopalliance.intercept.MethodInterceptor
异常抛出通知: 目标方法出现了异常后增强 org.springframework.aop.ThrowsAdvice (如果没有异常,不会执行)
1)传统方式:
(1)根据要增强的方式,选择一个接口去实现.重写里面的方法.
(2)然后编写配置文件:
定义目标:
<bean id="自己起ID名" class="目标的全路径"></bean>
定义通知:
<bean id="自己起ID名" class="我们编写的AOP代码类的全路径"></bean>
定义切入点:
<bean id="自己起ID名" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="正则表达式,类似.*Order"></property>
</bean>
定义切面:
<bean id="自己起ID名" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="上面通知的ID"/>
<property name="pointcut" ref="上面切入点的ID"/>
</bean>
定义代理:
<bean id="自己起ID名" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="上面目前的ID"/>
<property name="interceptorNames" value="上面切面的ID"/>
<!--这个配置要注意,如果要增强的目前是通过接口实现的使用以下配置-->
<property name="proxyInterfaces" value="目前实现接口的全路径"/>
<!--如果要增强的没有接口,使用以下配置,这两个配置不可以同时使用-->
<property name="proxyTargetClass">
<value>true</value>
</property>
</bean>
(3)简化配置文件:
a.导入jar:直接copy
b.配置文件支持:需要增加aop的支持,直接copy.
c.配置文件编写:
定义目标:
<bean id="自己起ID名" class="目标的全路径"></bean>
定义通知:
<bean id="自己起ID名" class="我们编写的AOP代码类的全路径"></bean>
定义AOP:
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* cn.itheima.aop.IOrderService.*(..))" id="自己起ID名" />
<!-- 定义切面 -->
<aop:advisor advice-ref="通知的ID" pointcut-ref="上面切入点的ID" />
</aop:config>
(4)切点表达式:
execution(<修饰符> <返回类型> <类路径> <方法名>(<参数列表>) <异常模式> )
execution(public * cn.itheima.aop.IOrderService.*(..))
execution(* cn.itheima.aop.IOrderService.*(..))
execution(* cn.*.IOrderService.*(..))
注意如果 第一个 * 表示的实际上是 public *
配置多个
<aop:pointcut expression="execution(* cn.*.IOrderService.*(..))||execution(* cn.itheima.aop.IOrderService.*(..))" id="自己起ID名" />
2)aspectJ方式:
(1)aspectJ的通知类型:
前置通知 Before 相当于 BeforeAdvice
后置通知 AfterReturning 相当于 AfterReturningAdvice
环绕通知 Around 相当于 MethodInterceptor
抛出通知 AfterThrowing 相当于 ThrowAdvice(如果没有异常,不会执行)
最终通知 After 不管是否异常,该通知都会执行(特有的类似于finally)
对于AOP增强一个service的方法,我起码需要几个类?
(2)实现方式有两种:
xml配置文件
注解开发
(3)配置文件方式
a.定义目标:
<bean id="自己起ID名" class="要增强的实现类"/>
b.定义通知:
这个aop的类可以不实现任何接口,就是一个普通的类.写一个方法
//方法名可以自定定义,可以有一个参数JoinPoint jp,也可以无参
public void before(JoinPoint jp) {
System.out.println("拦截的目标类:" + jp.getSignature().getDeclaringTypeName());
System.out.println("拦截的方法名称:" + jp.getSignature().getName());
System.out.println("前置通知");
}
配置上增加
<bean id="自己起ID名" class="我们编写的AOP代码类的全路径"/>
c.定义切面:
<!-- 使用aop:config来声明 使用aop:aspect来配置切面 -->
<aop:config >
<aop:aspect ref="上面声明的通知ID">
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* *.del(..))" id="自己起ID名"/>
<!-- 前置通知 可以不使用上面的切入点,自己声明 method表示具体使用哪个方法 -->
<aop:before method="before" pointcut="execution(* *.del(..))"/>
<!-- 前置通知 -->
<aop:before method="before1" pointcut-ref="引用上面声明的切入点ID"/>
</aop:aspect>
</aop:config>
d.其他通知:
后置:
// 后置通知 java代码
public void afterReturning() {
System.out.println("目标方法返回值:" + val);
System.out.println("后置通知");
}
<aop:config>
<aop:aspect ref="userServiceAdvice">
<aop:after-returning method="afterReturning" pointcut="execution(* *.del(..))"/>
</aop:aspect>
</aop:config>
环绕:
//环绕通知 java代码,他必须有一个参数 ProceedingJoinPoint pjp
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前....");
Object value = pjp.proceed(); // 执行目标行为
System.out.println("环绕后....");
return value;
}
<aop:config >
<aop:aspect ref="userServiceAdvice">
<aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
<aop:around method="around" pointcut-ref="delPointCut"/>
</aop:aspect>
</aop:config>
e:通知上的参数:
除了环绕通知以外,所有的通知都可以有一个参数JoinPoint jp
注意导包:org.aspectj.lang.JoinPoint
拦截的目标类 : jp.getSignature().getDeclaringTypeName());
拦截的方法名称 : jp.getSignature().getName());
环绕通知必须有参数:ProceedingJoinPoint pjp
执行目标行为 : Object value = pjp.proceed();
有点类似我们拦截器的invoke方法.
后置通知可以增加一个参数 Object val,
这个参数表示我们目标方法的返回值,
注意需要在配置文件上增加配置
<aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
returning="val"表示存在这个参数,参数名称为 val 要与代码里的参数名称一致
异常通知可以增加一个参数 Throwable ex
这个参数表示目前抛出的异常,
注意需要在配置文件上增加配置
<aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut" throwing="ex"/>
throwing="ex"表示存在这个参数,参数名称为 ex 要与代码里的参数名称一致
f:代理方案选择:
spring框架对于有接口的使用动态代理,没有接口的使用cglib.
我们可以强制使用cglib,需要配置 proxy-target-class="true"
<aop:config proxy-target-class="true"></aop:config>
(4)注解开发
1)要是用spring的注解,我们要在配置文件中开启包扫描的注解
<context:component-scan base-package="cn.itheima" />
同时,要开启aop的aspectj注解自动代理
<!-- 开启aspectj注解自动代理 -->
<aop:aspectj-autoproxy />
<!-- 我们也可以强制使用cglib代理 -->
<!-- <aop:aspectj-autoproxy proxy-target-class="true"/> -->
2)编写通知类:这里所有和AOP相关的类都是 org.aspectj.lang 包下的
@Component//将当前类交给spring容器管理相当于<bean id="自己起ID名" class="我们编写的AOP代码类的全路径"/>
@Aspect // 声明当前的bean就是一个切面,而不是一个普通的bean
public class CustomerServiceHelper {
@Before("execution(* *.s*(..))")
public void before() {
System.out.println("前置通知...");
}
}
@Component
@Aspect
public class CustomerServiceHelper {
// 后置通知 returning = "value" 声明参数
@AfterReturning(value = "execution(* *.update(..))", returning = "value")
public void afterReturning(JoinPoint jp, Object value) {
System.out.println("后置通知,目标方法的返回是" + value);
}
}
@Component
@Aspect
public class CustomerServiceHelper {
// 环绕通知
@Around("mypointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前...");
Object value = pjp.proceed();
System.out.println("环绕后");
return value;
}
}
|
|