基于XML的AOP开发
-
pom文件
<?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>org.example</groupId> <artifactId>spring_aop_xml</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <!--导入spring的context坐标,context依赖aop--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- aspectj的织入(切点表达式需要用到该jar包) --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!--spring整合junit--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
-
创建目标接口和目标实现类
package com.code.service; public interface AccountService { //目标方法:(切入点:要进行拦截增强的方法) public void transfer(); }
package com.code.service.impl; import com.code.service.AccountService; public class AccountServiceImpl implements AccountService { /* 目标方法:(切入点:要进行拦截增强的方法) */ @Override public void transfer() { System.out.println("转账方法执行了...."); //int i = 1/0; } }
-
创建通知类
public class MyAdvice { public void before() { System.out.println("前置通知..."); } }
-
Spring配置文件,将目标类和通知类对象创建权交给spring
<?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"> <!--目标类交给IOC容器--> <bean id="accountServcie" class="com.code.service.impl.AccountServiceImpl"></bean> <!--通知类交给IOC容器--> <bean id="myAdvice" class="com.code.advice.MyAdvice"></bean> <!-- execution([修饰符] 返回值类型 包名.类名.方法名(参数)) execution(public void com.code.service.impl.AccountServiceImpl.transfer(java.lang.String)) - 访问修饰符可以省略 execution(void com.code.service.impl.AccountServiceImpl.transfer(java.lang.String)) - 返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意 execution(* *.*.*.*.*.*()) - 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类 execution(* *..*.*()) - 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表 execution(* *..*.*(..)) --> <!--AOP配置--> <aop:config> <!--抽取的切点表达式--> <aop:pointcut id="accountService" expression="execution(* com.code.service.impl.AccountServiceImpl.*(..))"/> <!--配置切面:切入点+通知--> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut-ref="accountService"/> <aop:after-returning method="afterReturning" pointcut-ref="accountService"/> <aop:after-throwing method="afterThrowing" pointcut-ref="accountService"/> <aop:after method="after" pointcut-ref="accountService"/> <aop:around method="around" pointcut-ref="accountService"/> </aop:aspect> </aop:config> </beans>
-
切点表达式抽取:当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替pointcut 属性来引用抽取后的切点表达式
<aop:config> <!--抽取的切点表达式--> <aop:pointcut id="myPointcut" expression="execution(* com.code.service..*.*(..))"> </aop:pointcut> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut-ref="myPointcut"></aop:before> </aop:aspect> </aop:config>
-
通知的配置语法:
<aop:通知类型 method=“通知类中方法名” pointcut=“切点表达式"></aop:通知类型>
名称 标签 说明 前置通知 aop:before 用于配置前置通知。指定增强的方法在切入点方法之前执行 后置通知 aop:afterReturning 用于配置后置通知。指定增强的方法在切入点方法之后执行 异常通知 aop:afterThrowing 用于配置异常通知。指定增强的方法出现异常后执行 最终通知 aop:after 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行 环绕通知 aop:around 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行
基于注解的AOP开发
-
pom文件
<dependencies> <!--导入spring的context坐标,context依赖aop--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- aspectj的织入 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!--spring整合junit--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
-
创建目标接口和目标实现类
public interface AccountService { public void transfer(); }
public class AccountServiceImpl implements AccountService { @Override public void transfer() { System.out.println("转账业务..."); } }
-
创建通知类:
public class MyAdvice { public void before() { System.out.println("前置通知..."); } }
-
将目标类和通知类对象创建权交给spring
@Service public class AccountServiceImpl implements AccountService {} @Component public class MyAdvice {}
-
在通知类中使用注解配置织入关系,升级为切面类
@Component @Aspect public class MyAdvice { @Pointcut("execution(* com.code..*.*(..))") public void myPoint(){} @Before("MyAdvice.myPoint()") public void before() { System.out.println("前置通知..."); } }
-
在配置文件中开启组件扫描和 AOP 的自动代理
<!--组件扫描--> <context:component-scan base-package="com.code"/> <!--aop的自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
通知类型:通知的配置语法:@通知注解(“切点表达式")
名称 标签 说明 前置通知 @Before 用于配置前置通知。指定增强的方法在切入点方法之前执行 后置通知 @AfterReturning 用于配置后置通知。指定增强的方法在切入点方法之后执行 异常通知 @AfterThrowing 用于配置异常通知。指定增强的方法出现异常后执行 最终通知 @After 用于配置最终通知。无论切入点方法执行时是否有异常,都会 执行环绕通知 @Around 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行 -
当前四个通知组合在一起时,执行顺序:@Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)
-
纯注解配置
package com.code.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan("com.code") @EnableAspectJAutoProxy //开启AOP的自动代理 替代了<aop:aspectj-autoproxy> public class SpringConfig { }