SpringAOP面向切面详解(带实例)

了解AOP的相关术语

1.通知(Advice):

通知定义了切面是什么,以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。

2.连接点(Joinpoint):

程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。

3.切入点(Pointcut):

通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定(切面在哪个方法的前或后做出的那个方法点)

4.切面(Aspect):

通知和切入点共同组成了切面:时间、地点和要发生的“故事”,事务管理是J2EE应用中一个很好的横切关注点例子,切面用Spring的Advisor或拦截器实现

5.引入(Introduction):

引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)

6.目标(Target):

即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)

7.代理(proxy):

应用通知的对象,详细内容参见设计模式里面的代理模式

8.织入(Weaving):

把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器

(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码

(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

存在的实现方式

1.经典的基于代理的AOP
2.@AspectJ注解驱动的切面
3.纯POJO切面
4.注入式AspectJ切面

实现步骤

1.创建通知:实现这几个接口,把其中的方法实现了
2.定义切点和通知者:在Spring配制文件中配置这些信息
3.使用ProxyFactoryBean来生成代理

范例

例子我是基于maven和Spring注解的方式,用POJO实现

工程的主要文件的配置

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>imooc</groupId>
<artifactId>springmaven</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>springmaven Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<finalName>SSHMJ-FRANK</finalName>
</properties>
<repositories>
<repository>
<id>mav</id>
<name>sss</name>
<url>http://mvnrepository.com</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-asm</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.9</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-struts</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>

<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>2.6.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
</dependencies>


<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.4</version>
</plugin>
</plugins>
</pluginManagement>
<finalName>springmaven</finalName>
</build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<!-- 工程名 -->
<display-name>springmaven</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- spring配置文件的配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- springmvc的配置环境 -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">

<context:component-scan base-package="com.service" />
<aop:aspectj-autoproxy />
<aop:config proxy-target-class="true">
<aop:aspect ref="preDo">
<aop:pointcut expression="execution(* com.service.ToDo.toEat(..))"
id="register" />
<aop:before method="toPre" pointcut-ref="register" />
</aop:aspect>
</aop:config>

</beans>

spring-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<!-- don't handle the static resource -->
<mvc:default-servlet-handler />

<!-- if you use annotation you must configure following setting -->
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>

IToDo.java

切入点(Pointcut)接口

1
2
3
4
5
6
package com.service.imp;

public interface IToDo {

public String toEat();
}

ToDo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.service;

import org.springframework.stereotype.Service;

import com.service.imp.IToDo;
@Service
public class ToDo implements IToDo {

@Override
public String toEat() {
System.out.println("吃苹果");
return "吃苹果";
}

}

IPreDo.java

1
2
3
4
5
6
package com.service.imp;

public interface IPreDo {

public String toPre();
}

application.java

用于测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package springmaven;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.service.imp.IToDo;

public class application {
public static void main(String[] args) {
ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");
IToDo tdo = (IToDo)appCtx.getBean("toDo");
tdo.toEat();
}
}

工程图片

工程

返回的结果

1
2
洗手
吃苹果

主要的配置讲解

1
2
3
4
5
6
7
8
<aop:aspectj-autoproxy />
<aop:config proxy-target-class="true">
<aop:aspect ref="preDo">
<aop:pointcut expression="execution(* com.service.ToDo.toEat(..))"
id="register" />
<aop:before method="toPre" pointcut-ref="register" />
</aop:aspect>
</aop:config>

<aop:aspectj-autoproxy />:会自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面,我这里没有使用注解的方式,使用了xml配置的方式。

<aop:config>:就是用来配置aspectJ切面

proxy-target-class:设置代理模式。当poxy-target-class="true"时,表示使用CGLib动态代理技术织入增强。设置为false时,表示使用jdk动态代理织入增强,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

<aop:aspect ref="preDo">:设置切面,ref是切面Bean的id名

<aop:pointcut expression="execution(* com.service.ToDo.toEat(..))" id="register" />:这里设置切入点,expression设置切面植入的切入点的方法地址

<aop:before method="toPre" pointcut-ref="register" >:在执行切入点方法之前执行切面方法,method为切面中的执行方法,pointcut-ref与切点的id一致就可以了

<aop:advisor> 定义一个AOP通知者
<aop:after> 后通知
<aop:after-returning> 返回后通知
<aop:after-throwing> 抛出后通知
<aop:around> 周围通知
<aop:aspect>定义一个切面
<aop:before>前通知
<aop:config>顶级配置元素,类似于这种东西
<aop:pointcut>定义一个切点

参考资料:

Spring实现AOP的4种方式

proxy-target-class

迹_Jason wechat
分享快乐,感受科技的温度
坚持原创技术分享,您的支持将鼓励我继续创作!