SpringAOP入门-面向切面编程
先解释一下到底什么是AOP吧。
如图,系统中有若干个模块,每个模块对应某功能,而还有些特定的功能是每个模块几乎都会用到的,比如日志这种,想要降低日志模块和每个模块的耦合,那么AOP就是必须的了。在这里,所谓的Aspect(切面)就是日志模块,而其他模块就是被切的。
AOP:面向切面编程
切面:系统里面一个特定的模块,贯穿整个系统。比如日志。
配置方式:java注解或者用xml配置。
-
利用JAVA注解实现AOP
1、配置Spring依赖(Spring版本最好选择4.0.0到5.0.0之间)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
2、创建(生成)Spring配置文件
如果用的是IntelliJ IDEA,可以快捷生成spring的配置文件,方法如下:
右键项目名-->NEW-->XML Configuration File-->Spring Config
生成了Spring配置文件后,添加关于AOP的文件头:
<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">
添加自动代理:
<aop:aspectj-autoproxy expose-proxy="true"/>
创建三个类,分别是:
1、将被切入的类,也就是普通的Service
public class CustomerService { private String name; private String url; public void setName(String name) { this.name = name; } public void setUrl(String url) { this.url = url; } public void printName() { System.out.println("Customer name : " + this.name); } public void printURL() { System.out.println("Customer website : " + this.url); } public void printThrowException() { throw new IllegalArgumentException(); } }
2、切面类,用于切入
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyAspect { @Pointcut("execution(* cn.jdfo.CustomerService.printName())") public void pointCut(){} @Before("pointCut()") public void logBefore(){ System.out.println("before"); } }
切面类用了aspectj的注解,还有After、AfterRunning、AfterReturning、Around这些。
Pointcut是一个切入点,上面的logBefore方法就是在printName方法结束之前执行。
3、程序入口
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "spring.xml" }); CustomerService cust = (CustomerService) appContext.getBean("customerService"); System.out.println("*************************"); cust.printName(); System.out.println("*************************"); cust.printURL(); System.out.println("*************************"); try { cust.printThrowException(); } catch (Exception ignored) { } } }
这个程序在JAVA8中运行正常,但是在JAVA9运行会出现错误:
java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut pointCut
还不清楚发生这个错误的原因。
另:ApplicationContext可以通过多种方式获得,这里用的是用于测试的一种,直接生成一个应用环境,而在实际应用中通常通过实现一个接口:ApplicationAware(好像这样的)。详见:
[实际项目动态获取Bean] http://blog.****.net/m0_38024433/article/details/79399316接下来是通过xml配置实现AOP
xml配置:
<bean id="mysAspect" class="cn.jdfo.MySAspect"/> <aop:config> <aop:pointcut id="apointcut" expression="execution(* cn.jdfo.CustomerService.*(..))"/> <aop:aspect id="aaspect" ref="mysAspect"> <aop:before method="logBefore" pointcut-ref="apointcut"/> <aop:after method="logAfter" pointcut-ref="apointcut"/> </aop:aspect> </aop:config>
MySAspect类:
public class MySAspect { public void logBefore(){ System.out.println("before"); } public void logAfter(){ System.out.println("after"); } }
这个用JAVA9就没有问题,运行结果如下:
************************* before Customer name : Jdfohewk after ************************* before Customer website : http://www.baidu.com after ************************* before after
两种方法相比较,Spring AOP兼容性更好,而AspectJ暂时还不能很好地支持JAVA9,代码复杂程度二者差不多。所以我选择用Spring AOP...