16. 用XML 配置的方式 进行 AOP 切面编程
大概分为下面几部:
① 导入 AOP 相关坐标
② 创建目标接口和目标类(内部有切点)
③ 创建切面类(内部有增强方法)
④ 将目标类和切面类的对象创建权交给 spring
⑤ 在 applicationContext.xml 中配置织入关系
⑥ 测试代码
实现:
先导入 Spring的gav 和 织入(切面类织入)的gav:
<!--导入spring的context坐标,context依赖aop--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!-- aspectj的织入 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency>
说一下这个 aspectjweaver ,这玩意 是 第三方的,比Spring的配置应用性能比 AOP 还要好,官方也是主张推荐使用他,因为Spring不会放弃好的框架的 啊哈哈哈!
创建目标接口和目标类(内部有切点):
package com.bihu.aop; public interface TargetInterface { public void method(); }
package com.bihu.aop; public class Target implements TargetInterface { @Override public void method() { System.out.println("method Running·····"); } }
然后我们需要 创建切面类(内部有增强方法)
package com.bihu.aop; public class MyAspect { //前置增强方法 public void before(){ System.out.println("前置代码增强..."); } }
然后我们将这个权限配置给Spring 有到了配置啦:
<bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean>
一个是 :目标方法(内部有切点)的bean
一个是:切面(内部有增强方法) 的Bean
这两个bean 作用:
目标方法的bean 是 在测试的时候注入
切面方法的bean 是 在配置切面类的时候引入
看代码要细心 是什么? 为什么? 怎么做 ? 三问.
然后又得配置:
在 applicationContext.xml 中配置织入关系
注意的是 配置的标签是 aop,记得要导入头即可:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean> <aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <!--配置Target的method方法执行时要进行myAspect的before方法前置增强--> <aop:before method="before" pointcut="execution(public void com.bihu.aop.Target.method())"/> </aop:aspect> </aop:config> </beans>
最后我们测试一下:
AopTest.java :
import com.bihu.aop.TargetInterface; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:springContext.xml") public class AopTest { @Autowired private TargetInterface target; @Test public void test1(){ target.method(); } }
注意了!!! 用这种测试的话 ,要满足:
1. Juint 测试要 4.12 及以上版本
2. 要Spring-tets 这个 GAV坐标即可。
运行:
当然有前置增强 也有后置增强(这里演示的是最终通知 具体看 下面 通知的类型):
<aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <!--配置Target的method方法执行时要进行myAspect的before方法前置增强--> <aop:before method="before" pointcut="execution(public void com.bihu.aop.Target.method())"/> <aop:after method="after" pointcut="execution(public void com.bihu.aop.Target.method())"/> </aop:aspect> </aop:config>
运行:
以上代码讲解如下:
其实上面要讲的只有 XML 配置 AOP 这段:
XML 配置 AOP 详解:
1. 切点表达式的写法:
表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
其中:
访问修饰符可以省略
返回值类型、包名、类名、方法名可以使用星号* 代表任意
包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
其实那些条件都是匹配的,例如下面:
execution(public void com.xxx.aop.Target.method())
execution(void com.xxx.aop.Target.*(..))
execution(* com.xxx.aop.*.*(..))
execution(* com.xxx.aop..*.*(..))
execution(* *..*.*(..))
第一个 全部都写出来了,那就精准定位到 指定包下 指定的类 指定方法
第二个 返回值是void 表示 只有返回值类型是 void 的方法, 方法是 * 表示全部方法,参数是.. 表示任意参数,换句话说: 这个类下面的所有返回值为void 的 方法都要增强
第三个 返回值类型是* 表示返回值类型是什么都可以, 包是* 说明 类 和 方法是 * 说明 com.xxx.aop 下面的所有包和所有方法,参数名是.. 证明有无参数的方法都要被增强, 其实这个就是 aop包下的所有类中的所有方法。
第四个 返回值类型是* 表示返回值类型是什么都可以, 包是* 说明 类 和 方法是 * 说明 com.xxx.aop 下面的所有包和所有方法,参数名是.. 证明有无参数的方法都要被增强, 其实这个就是 aop包 以及 aop的 子包 下的 的所有类中的所有方法。【这个包是两个点的!!!注意看! 】
第五个 ,第五个不用说 凡是方法我都增强
2.通知的类型:
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
所以上面演示的 是 前置通知 和 最终通知
我们重新写上代码演示一下:
以这些代码为前提:
目标方法.java
package com.bihu.aop; public class Target implements TargetInterface { @Override public void method() { System.out.println("method Running·····"); } }
目标接口.java
package com.bihu.aop; public interface TargetInterface { public void method(); }
测试类 AopTest.java :
import com.bihu.aop.TargetInterface; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:springContext.xml") public class AopTest { @Autowired private TargetInterface target; @Test public void test1(){ target.method(); } }
切面(内部编写增强方法:)
package com.bihu.aop; public class MyAspect { }
1 .前置通知就不说了 ,就是最先开始的那个。
2 .后置通知就是 执行 切点方法之后执行的:
这里代码给出前后通知的代码:
切面.java:
package com.bihu.aop; public class MyAspect { //前置通知 public void before(){ System.out.println("前置通知 - 增强"); } //后置通知 public void after_returning(){ System.out.println("后置通知 - 增强"); } }
配置.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean> <aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <!--配置Target的method方法执行时要进行myAspect的before方法前置增强--> <aop:before method="before" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:after method="after_returning" pointcut="execution(* com.bihu.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
可以看到 配置了 切面是 myAspect类 然后切点是 aop包下所有的方法,你完全可以自定义的。
测试代码.java:
import com.bihu.aop.TargetInterface; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:springContext.xml") public class AopTest { @Autowired private TargetInterface target; @Test public void test1(){ target.method(); } }
可以看到 这里用的是 Spring整合Junit测试,但是我是看不懂的 当时没学,但是会用即可了啊
运行结果:
下面讲解 怀绕 ,环绕通知比较特殊,他是结合前后,所以他要一个切点对象做参数:
//Tips 因为环绕和前、后通知做的事情差不多,所以这里注释掉前后通知。
切面.java:
package com.bihu.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { //环绕通知 //需要一个切点对象(Proceeding:执行 JoinPoint:连接点) 正在执行的切点 = 切点 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强"); Object proceed = pjp.proceed(); //执行切点方法(抛出异常) System.out.println("环绕后增强"); return proceed;//然后返回对象即可 } }
配置.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean> <aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <!--配置Target的method方法执行时要进行myAspect的before方法前置增强--> <!-- <aop:before method="before" pointcut="execution(* com.bihu.aop.*.*(..))"/>--> <!-- <aop:after method="after_returning" pointcut="execution(* com.bihu.aop.*.*(..))"/>--> <aop:around method="around" pointcut="execution(* com.bihu.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
运行结果:
所以你看 其实这个环绕就是 集合了 前通知 和 后通知一起。 注意写增强方法的时候 加上参数即可 ,还有返回那个对象。
异常抛出通知
这个我们在目标方法 作死一下即可 1/0 这样吧 。
package com.bihu.aop; public class Target implements TargetInterface { @Override public void method() { System.out.println("method Running·····"); System.out.println(1/0); } }
然后在 配置切面:
package com.bihu.aop; public class MyAspect { public void afterThrowing(){ System.out.println("目标方法afterThrowing出现了异常! 请检测代码!");
}
}
配置xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean> <aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <aop:after-throwing method="afterThrowing" pointcut="execution(* com.bihu.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
然后运行:
可以发现 先运行了方法 在出错的 ,所以呢 代码也是一样的。
最终增强也一样:用于配置最终通知。无论增强方式执行是否有异常都会执行 。反正我都执行。是在方法
下面除了异常 其他都演示一遍:
你会发现 执行顺序不一样:
切面:
package com.bihu.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { //前置通知 public void before() { } //后置通知 public void afterReturning() { } //环绕通知 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强"); Object proceed = pjp.proceed(); System.out.println("环绕后增强"); return proceed; } //最终通知 public void after() { System.out.println("最终增强"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean> <aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <aop:before method="before" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:after-returning method="afterReturning" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:around method="around" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:after method="after" pointcut="execution(* com.bihu.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
目标:
package com.bihu.aop; public class Target implements TargetInterface { @Override public void method() { System.out.println("method Running·····"); } }
配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="target" class="com.bihu.aop.Target"></bean> <bean id="myAspect" class="com.bihu.aop.MyAspect"></bean> <aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <aop:before method="before" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:after-returning method="afterReturning" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:around method="around" pointcut="execution(* com.bihu.aop.*.*(..))"/> <aop:after method="after" pointcut="execution(* com.bihu.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
注意看运行:
可以看到 :
如果我们用了 环绕,他会替代掉前通知和后通知,因为:
注意看图 所以都替代掉了
那为为什么最终通知不是最后呢?
因为我也不,我这样写 他就这样来....懂即可
但是: 前 后 通知 + 最终就是这样的运行结果:
意义不大 懂即可
更多你可以自己去尝试 但是环绕一般不和前后一起用
本文来自博客园,作者:咸瑜,转载请注明原文链接:https://www.cnblogs.com/bi-hu/p/15039243.html