基于xml的AOP开发
1. 开发的步骤
(1)导入AOP相关坐标
在pom.xml中
(2)创建目标接口和目标类(内部有切点)
(3)创建切面类(内部有增强方法)
(4)将目标类和切面类的对象创建权交给spring
(5)在applicationContext.xml中配置织入关系
(6)测试代码
案例:
(1)导入AOP相关坐标,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.company</groupId> <artifactId>spring_aop</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.9.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> </project>
(2)创建目标接口(TargetInterface)和目标类(Target),切点是save()方法
TargetInterface.java
package com.company.aop; public interface TargetInterface { void save(); }
Target.java
package com.company.aop; public class Target implements TargetInterface { public void save() { System.out.println("save running ..."); } }
(3)创建切面类,增强方法是before()方法
package com.company.aop; public class MyAspect { public void before() { System.out.println("前置增强........"); } }
(4)将目标对象和切面对象的创建权交给spring,在applicationContext.xml中配置,Spring的很多功能首要条件就是这些bean要放到容器中,spring才能在容器中拿出对象,进行相应的操作。
(5)在applicationContext.xml中配置织入关系,method="before"是myAspect的before()方法
完整版的applicationContext.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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--目标对象--> <bean id="target" class="com.company.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class="com.company.aop.MyAspect"></bean> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置,后置)--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <aop:before method="before" pointcut="execution(public void com.company.aop.Target.save())"></aop:before> </aop:aspect> </aop:config> </beans>
(6)测试 ,AopTest.java
package com.company.test; import com.company.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:applicationContext.xml") public class AopTest { @Autowired private TargetInterface targetInterface; @Test public void test1() { targetInterface.save(); } }
运行结果:
2. 切点表达式的写法
<aop:before method="before" pointcut="execution(public void com.company.aop.Target.save())"></aop:before>
(1)访问修饰符可以省略
(2)返回值类型、包名、类名、方法名可以使用星号*代表任意
(3)包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
(4)参数列表可以使用两个点..表示任意个数,任意类型的参数列表
例如:
excution(public void com.company.aop.Target.method())
excution(void com.company.aop.Target.*(..)) *表示任意方法,(..)表示参数任意个
excution(* com.company.aop.*.*(..)) aop包下的任意类,任意方法,这种形式最常用
excution(* com.company.aop..*.*(..)) aop包及其子包下的任意类,任意方法
excution(* *..*.*(..))
上面的可以改成这样
<aop:before method="before" pointcut="execution(* com.company.aop.*.*(..))"></aop:before>
3. 通知的类型
3.1. 后置通知
在原来的切面类中加如后置增强方法
MyAspect.java
package com.company.aop; public class MyAspect { public void before() { System.out.println("前置增强........"); } public void afterReturning() { System.out.println("后置增强"); } }
再在applicationContext.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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--目标对象--> <bean id="target" class="com.company.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class="com.company.aop.MyAspect"></bean> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置,后置)--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <aop:before method="before" pointcut="execution(* com.company.aop.*.*(..))"></aop:before> <aop:after-returning method="afterReturning" pointcut="execution(* com.company.aop.*.*(..))"></aop:after-returning> </aop:aspect> </aop:config> </beans>
执行AopTest.java
运行结果:
3.2. 环绕通知
在原来的切面类中加入环绕通知方法
MyAspect.java
package com.company.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { public void before() { System.out.println("前置增强........"); } public void afterReturning() { System.out.println("后置增强"); } // ProceedingJoinPoint 正在执行的连接点 === 切点 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前增强....."); // 切点方法 Object proceed = proceedingJoinPoint.proceed(); System.out.println("环绕后增强....."); return proceed; } }
再在applicationContext.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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--目标对象--> <bean id="target" class="com.company.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class="com.company.aop.MyAspect"></bean> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置,后置)--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <!-- <aop:before method="before" pointcut="execution(* com.company.aop.*.*(..))"></aop:before>--> <!-- <aop:after-returning method="afterReturning" pointcut="execution(* com.company.aop.*.*(..))"></aop:after-returning>--> <aop:around method="around" pointcut="execution(* com.company.aop.*.*(..))"></aop:around> </aop:aspect> </aop:config> </beans>
执行AopTest.java
运行结果:
3.3 异常抛出通知
在MyAspect.java中加入异常抛出增强方法
package com.company.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { public void before() { System.out.println("前置增强........"); } public void afterReturning() { System.out.println("后置增强"); } // ProceedingJoinPoint 正在执行的连接点 === 切点 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前增强....."); // 切点方法 Object proceed = proceedingJoinPoint.proceed(); System.out.println("环绕后增强....."); return proceed; } public void afterThrowing() { System.out.println("异常抛出增强....."); } }
异常抛出的是目标对象中的异常,因此在Target中构造异常
Target.java
package com.company.aop; public class Target implements TargetInterface { public void save() { int i = 1/0; System.out.println("save running ..."); } }
再在applicationContext.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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--目标对象--> <bean id="target" class="com.company.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class="com.company.aop.MyAspect"></bean> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置,后置)--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <!-- <aop:before method="before" pointcut="execution(* com.company.aop.*.*(..))"></aop:before>--> <!-- <aop:after-returning method="afterReturning" pointcut="execution(* com.company.aop.*.*(..))"></aop:after-returning>--> <aop:around method="around" pointcut="execution(* com.company.aop.*.*(..))"/> <aop:after-throwing method="afterThrowing" pointcut="execution(* com.company.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
执行AopTest.java
运行结果:
3.4 最终通知
在MyAspect.java中加入最终增强方法
package com.company.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { public void before() { System.out.println("前置增强........"); } public void afterReturning() { System.out.println("后置增强"); } // ProceedingJoinPoint 正在执行的连接点 === 切点 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前增强....."); // 切点方法 Object proceed = proceedingJoinPoint.proceed(); System.out.println("环绕后增强....."); return proceed; } public void afterThrowing() { System.out.println("异常抛出增强....."); } public void after() { System.out.println("最终增强....."); } }
再在applicationContext.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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--目标对象--> <bean id="target" class="com.company.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class="com.company.aop.MyAspect"></bean> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置,后置)--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <!-- <aop:before method="before" pointcut="execution(* com.company.aop.*.*(..))"/>--> <!-- <aop:after-returning method="afterReturning" pointcut="execution(* com.company.aop.*.*(..))"/>--> <aop:around method="around" pointcut="execution(* com.company.aop.*.*(..))"/> <aop:after-throwing method="afterThrowing" pointcut="execution(* com.company.aop.*.*(..))"/> <aop:after method="after" pointcut="execution(* com.company.aop.*.*(..))"/> </aop:aspect> </aop:config> </beans>
运行结果:
4.切点表达式抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--目标对象--> <bean id="target" class="com.company.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class="com.company.aop.MyAspect"></bean> <!--配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置,后置)--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!--抽取切点表达式--> <aop:pointcut id="myPointcut" expression="execution(* com.company.aop.*.*(..))"/> <aop:around method="around" pointcut-ref="myPointcut"/> <aop:after method="after" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> </beans>