首先这个配置模式估计现在已经不用了,因为我在我们公司的项目里面并没有看到这么配置AOP相关的东西。不过,这个就和学习spring的控制反转(IOC)和依赖注入(DI)一样,刚刚开始的时候,都是从简单的xml配置学起、然后再进一步简化:最开始也是在xml文件里面配置很多的bean,每个model都得配置一个bean标签,直到后来的只要一句话
<context:component-scan base-package="com.lxk.hello" />;
就可以搞定spring的依赖注入,也就是使用注解相关的配置了。
这个AOP配置也是这么个道理。所以先从这个麻烦但是基础的配置模式看起。好理解这个AOP。
对很多相同的操作提取到切面上去操作。这个只是把切面内部的方法写好,下面的配置文件说明这个切面切哪里也就是切入点在哪,怎么切,是前置,环绕,后置,还是异常等等。
package com.lxk.spring.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import java.util.ArrayList; import java.util.List; /** * 切面(spring aop 就不需要拦截器啦) * (模拟hibernate里面保存数据要打开事物,然后各种增删改之后,再提交事物。) */ public class Transaction { public void beginTransaction() {//前置通知 //打开事物 System.out.println("begin Transaction"); } /** * @param joinPoint 通过joinPoint可以得到目标类和目标方法的一些信息 * @param val 目标方法的返回值 * 和<aop:after-returning returning="val"/>中returning的值保质一致 */ public void commit(JoinPoint joinPoint, Object val) {//后置通知 String methodName = joinPoint.getSignature().getName(); System.out.println(methodName); System.out.println(joinPoint.getTarget().getClass().getName()); //提交事物 System.out.println("commit"); List<Person> personList = (ArrayList<Person>) val; for (Person person : personList) { System.out.println(person.getPname()); } } public void finalMethod() { System.out.println("最终通知"); } public void aroundMethod(ProceedingJoinPoint joinPoint) {//环绕通知 try { System.out.println("around method"); joinPoint.proceed();//调用目标类的目标方法 } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 异常通知 */ public void throwingMethod(Throwable except) { System.out.println(except.getMessage()); } }
xml的配置文件:
<?xml version="1.0" encoding="UTF-8"?> <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/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- 1、目标类 2、切面 3、进行aop的配置 (目标接口没有:因为引入到容器是为了实例化对象,接口是不能实现对象的。) (也没有拦截器的引入,有的只是aop的配置,如上的3、) --> <!-- 目标类 --> <bean id="personDao" class="com.lxk.spring.aop.PersonDaoImpl"/> <!-- 切面的声明 --> <bean id="transaction" class="com.lxk.spring.aop.Transaction"/> <!-- aop配置 --> <aop:config> <!-- 配置aop的切入点 id 是切入点的标识 expression 为切入点的表达式 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) modifiers-pattern 修饰符 可选 public private protected ret-type-pattern 返回类型 必选 * 代表任意类型 declaring-type-pattern 方法的声明类型 name-patterm 方法名称类型 set* 以set开头的所有的方法名称 update* 以update开头的所有的方法名称 param-pattern 参数匹配 (..) 任意多个参数,每个参数任意多个类型 (*,String) 两个参数 第一个是任意类型,第二个是String (String,*,Integer) 三个参数,第一个是String类型,第二个是任意类型,第三个是Integer类型 throws-pattern 异常的匹配模式 例子: execution(* cn.itcast.spring.aop.xml.AService.*(..)); cn.itcast.spring.aop.xml.AService下的所有的方法 execution(public * cn.itcast.oa..*.*(..)) 返回值为任意类型,修饰符为public,在cn.itcast.oa包及子包下的所有的类的所有的方法 exectuion(* cn.itcast.oa..*.update*(*,String)) 返回值是任意类型,在cn.itcast.oa包及子包下所有的以update开头的参数为两个,第一个为任意类型 第二个为String类型的所有类的所有的方法 --> <aop:pointcut expression="execution(* com.lxk.spring.aop.PersonDaoImpl.*(..))" id="perform"/> <!-- 配置切面(切面里面配置通知)—— ref 指向声明切面的类 --> <aop:aspect ref="transaction"> <!-- 前置通知pointcut-ref 引用一个切入点 --> <aop:before method="beginTransaction" pointcut-ref="perform"/> <!-- 后置通知 * returning 目标方法的返回值 * 如果目标方法中有可能存在异常,异常确实发生了,这个时候,后置通知将不再执行 --> <!--<aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>--> <!-- 最终通知 * 不能得到目标方法的返回值 * 无论目标方法是否有异常,最终通知都将执行 * 资源的关闭、连接的释放写在最终通知里 --> <!--<aop:after pointcut-ref="perform" method="finalMethod"/>--> <!-- 环绕通知 * ProceedingJoinPoint的proceed方法就是目标对象的目标方法 * 环绕通知可以控制目标对象目标方法执行 --> <!-- <aop:around method="aroundMethod" pointcut-ref="perform"/> --> <!-- 异常通知 在异常通知中获取目标方法抛出的异常 --> <!--<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="except"/>--> </aop:aspect> </aop:config> </beans>
pointcut-ref设置切入点,也就是被选中的bean里面的方法执行的时候,就可以执行对应的切面的方法啦。
也就是说,什么样的地方,会执行切面内部的代码操作。这里设置的是这个impl类里面的所有方法被调用的时候,就会执行切面的方法。
下面有before after around 等等几种类型的切面方法,后面的method 指出这个前置通知还是后置通知,还是什么通知执行的方法的名称。对应于切面类里面的方法。这个是必须存在的,不然关联不到,那就不行啦。
实际开发用的不是这么个配置姿势,实际开发配置页和spring注解实现一样,也就一两行的配置。
Spring AOP 中的 execution切入点指示符。执行表达式格式和实际代码对照,以及参数的理解。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
?,问号表示可选项。可填可不填。declaring-type是方法全名,只到类,不含方法名,具体就是类路径。看上面的图的对应关系可知。