Spring AOP 实现原理
# AOP介绍
# 什么是AOP
- 在软件行业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的 统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要组成内容,是函数式编程的一 种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率;
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码;
- 经典应用:事务管理、性能监视、安全检查、缓存、日志等;
- Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码;
- AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译 器,在编译时提供横向代码的织入。
用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为切面(Aspect),减少系统的重复 代码,降低模块间的耦合度,同时提高系统的可维护性。
# AOP重要概念
# Join point (连接点)
连接点是在应用执行过程中能够插入切面(Aspect)的一个点。这些点可以是调用方法时,抛出异常时,它是一个虚拟的概念。在Spring AOP中,连接点总是表示
方法的执行。org.aspectj.lang.JoinPoint
接口表示目标类连接点对象,它定义下面这些主要方法:
方法 | 说明 |
---|---|
Object[] getArgs() | 获取连接点方法运行时的入参列表。 |
Signature getSignature() | 获取连接点的方法签名对象。 |
Object getTarget() | 获取连接点所在的目标对象。 |
Object getThis() | 获取代理对象。 |
org.aspectj.lang.ProceedingJoinPoint
继承了JoinPoint
接口,新增了两个方法(用于执行连接点方法):
方法 | 说明 |
---|---|
Object proceed() throws Throwable | 通过反射执行目标对象连接点处的方法。 |
Object proceed(Object[] var1) throws Throwable | 使用新的入参(替换掉原来的入参),通过反射执行目标对象连接点处的方法。 |
# Advice (通知)
通知是指一个切面在特定的连接点要做的事情。通知分为方法执行前通知,方法执行后通知,环绕通知等。许多AOP框架(包括 Spring)都将通知建模为拦截器, 在连接点周围维护一系列拦截器(形成拦截器链),对连接点的方法进行增强。Spring 通知有以下类型:
类型 | 说明 |
---|---|
前置通知(Before) | 在目标方法被调用之前调用通知功能; |
后置通知(After) | 在目标方法完成之后调用通知,此时不会关心方法的输出是什么; |
返回通知(After-returning) | 在目标方法成功执行之后调用的通知; |
异常通知(After-throwing) | 在目标方法抛出异常后调用通知; |
环绕通知(Around) | 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。 |
# Pointcut (切点)
一个匹配连接点(Join point)的谓词表达式。通知(Advice)与切点表达式关联,并在切点匹配的任何连接点(Join point)(例如,执行具有特定名称的方法) 上运行。切点是匹配连接点(Join point)的表达式的概念,是AOP的核心,并且Spring默认使用AspectJ作为切入点表达式语言。
支持在切点定义中加入以下运算符进行符合运算:
运算符 | 说明 |
---|---|
&& | 与运算 |
! | 非运算 |
| | | 或运算 |
切点访问修饰符与类访问修饰符的性质是相同的,它可以决定切点可以在哪些类中可使用。因为命名切点仅利用方法名及访问修饰符的信息,所以一般定义方法的 返回类型为void,并且方法体为空。
/**
* @Pointcut: 切点命名注解
* within(net.deniro.spring4.aspectj.*): 切点表达式
* private: 切点可访问修饰符
* method1: 切点名称
*/
@Pointcut("within(net.deniro.spring4.aspectj.*)")
private void method1() {
}
2
3
4
5
6
7
8
9
10
# Aspect (切面)
它是一个跨越多个类的模块化的关注点,它是通知(Advice)和切点(Pointcut)合起来的抽象,它定义了一个切点(Pointcut)用来匹配连接点(Join point) 也就是对需要拦截的那些方法进行定义;它定义了一系列的通知(Advice)用来对拦截到的方法进行增强。
# Target object (目标对象)
被一个或者多个切面(Aspect)通知的对象,也就是需要被AOP进行拦截对方法进行增强(使用通知)的对象,也称为被通知的对象。由于在AOP里面使用运行时代理, 所以目标对象一直是被代理的对象。
# AOP proxy (AOP 代理)
为了实现切面(Aspect)功能使用AOP框架创建一个对象,在Spring框架里面一个AOP代理要么指JDK动态代理,要么指CgLIB代理。
# Weaving (织入)
是将切面应用到目标对象的过程,这个过程可以是在编译时(例如,使用AspectJ编译器),类加载时,运行时完成。Spring AOP和其他纯Java AOP框架一样,是 在运行时织入。
一个连接点可以同时匹配多个切点,而切点所对应的增强在连接点上织入顺序的规则如下:
- 如果同一个切面类中声明的增强,则按照增强在切面勒种定义的顺序进行织入;
- 如果增强位于不同的切面类中,并且这些切面类都实现了
org.springframework.core.Ordered
接口,则由Ordered方法的顺序号决定,小的先织入; - 如果增强位于不同的切面类中,但这些切面类没有实现
org.springframework.core.Ordered
接口,织入的顺序是不确定的。
两个切面类A与B,都实现了Ordered接口,A的顺序号为1,B的顺序号为2.那么增强的织入顺序如下图:
# Advisor
这个概念是Spring 1.2的AOP支持中提出的,一个Advisor相当于一个小型的切面,不同的是它只有一个通知(Advice),Advisor在事务管理里面会经常遇到。
# Spring 框架的AOP
Spring 框架的一个关键组件是面向切面编程(AOP)框架。面向切面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的 功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。在软件开发过程中有各种各样的很好的切面的例子,如日志记录、审计、声明 式事务、安全性和缓存等。
在OOP中,关键单元模块度是类,而在AOP中单元模块度是切面。依赖注入帮助应用程序对象相互解耦合,AOP可以帮助它们所影响的对象中对横切关注点解耦。
Spring AOP模块提供拦截器来拦截一个应用程序,例如,当执行一个方法时,可以在方法执行之前或之后添加额外的功能。
# AOP 术语
项 | 描述 |
---|---|
Aspect | 一个模块具有一组提供横切需求的APIs。例如,一个日志模块为了记录日志将被AOP方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point | 在应用程序中它代表一个点 |
Advice | 这是实际行动之前或之后执行的方法。在程序执行期间通过Spring AOP框架实际被调用的代码。 |
Pointcut | 这是一组一个或多个连接点,通知应该被执行,可以使用表达式或模式指定切入点。 |
Introduction | 引用允许添加新方法或属性到现有的类中。 |
Target object | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving | 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
# 通知的类型
Spring 方面可以使用下面提到的五种通知工作:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在方法调用之前和之后,执行通知。 |
# 实现自定义切面
Spring 支持@AspectJ annotation style
的方法和基于模式
的方法来实现自定义切面。这两种方法在下面两个子节进行详细解释。
方法 | 描述 |
---|---|
XML Schema based | 切面是使用常规类以及基于配置的XML来实现的。 |
@AspectJ based | @AspectJ 引用一种声明方面的风格作为带有Java 5注释的常规Java类注释。 |
# Spring 中基于AOP的XML架构
为了使用aop命名空间标签,需要导入spring-aop
JAR 包,如下:
<?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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- bean definition & AOP specific configuration -->
</beans>
2
3
4
5
6
7
8
9
10
还需要在应用程序的CLASSPATH
中使用以下AspectJ库文件。注:aspectjweaver.jar已包含其他包。
- aspectjrt.jar
- aspectweaver.jar
- aspectj.jar
- aopalliance.jar
# 声明一个 aspect
一个aspect
是使用元素声明的,支持的bean是使用ref
属性引用的,如下:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
2
3
4
5
6
7
8
这里,aBean
将被配置和依赖注入。
# 声明一个切入点
一个切入点有助于确定连接点(即方法)。在处理基于配置的XML架构时,切入点将会按照如下所示定义:
<aop:config>
<aop:aspect id="myAspect" ref="aBean"></aop:aspect>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))">
...
</aop:pointcut>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
2
3
4
5
6
7
8
9
10
下面的示例定义了一个名为businessService
的切入点,该切入点将与com.tutorialspoint
包下的Student
类中的getName()
方法相匹配:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.tutorialspoint.Student.getName(..))" />
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
2
3
4
5
6
7
8
9
10
# 声明通知
可以在aop:aspect中使用aop:{通知类型名}元素声明任意五种类型的通知,如下:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))" />
<!-- a before advice definition -->
<aop:before pointcut-ref="businessService"
method="doRequiredTask" />
<!-- an after advice definition -->
<aop:after pointcut-ref="businessService"
method="doRequiredTask" />
<!-- an after-returning advice definition -->
<!-- The doRequiredTask method must have parameter named retVal -->
<aop:after-returning pointcut-ref="businessService"
returning="retVal"
method="doRequiredTask" />
<!-- an after-throwing advice definition -->
<!-- The doRequiredTask method must have parameter named ex -->
<aop:after-throwing pointcut-ref="businessService"
throwing="ex"
method="doRequiredTask" />
<!-- an around advice definition -->
<aop:around pointcut-ref="businessService"
method="doRequiredTask" />
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
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
可以对不同的通知使用相同的doRequiredTask
或者不同的方法,这些方法将会作为aspect模块的一部分来定义。
# 基于AOP的XML架构的示例
接下来,编写一个示例,可以实现几个通知:
步骤 | 描述 |
---|---|
1 | 创建一个名为SpringExample 的项目,并且在所建项目的src 文件夹下创建一个名为com.tutorialspoint 的包。 |
2 | 使用Add External JARs 选项添加所需的Spring库文件 |
3 | 在项目中添加Spring AOP指定的库文件aspectjrt.jar,aspectjweaver.jar,和aspectj.jar。 |
4 | 在com.tutorialspoint 包下创建Java类Logging , Student ,和MainApp 。 |
5 | 在src 文件夹下创建Beans配置文件Beans.xml。 |
6 | 最后一步是创建所有Java文件和Bean配置文件的内容,并且按如下解释的那样运行程序。 |
- Logging.java
package com.tutorialspoint;
public class Logging {
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
public void beforeAdvice() {
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
public void afterAdvice() {
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
public void afterReturningAdvice(Object retVal) {
System.out.println("Returning:" + retVal.toString());
}
/**
* This is the method which I would like to execute
* if there is an exception raised.
*/
public void afterThrowingAdvice(IllegalArgumentException ex) {
System.out.println("There has been an exception: " + ex.toString());
}
}
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
- Student.java
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age: " + age);
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name: " + name);
return name;
}
public void printThrowException() {
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
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
- MainApp.java
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- Beans.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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll"
expression="execution(* com.tutorialspoint.*.*(..))" />
<aop:before pointcut-ref="selectAll" method="beforeAdvice" />
<aop:after pointcut-ref="selectAll" method="afterAdvice" />
<aop:after-returning pointcut-ref="selectAll"
returning="retVal"
method="afterReturningAdvice" />
<aop:after-throwing pointcut-ref="selectAll"
throwing="ex"
method="afterThrowingAdvice" />
</aop:aspect>
</aop:config>
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11" />
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging" />
</beans>
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
- 执行结果
Going to setup student profile.
Name: Zara
Student profile has been setup.
Returning: Zara
Going to setup student profile.
Age: 11
Student profile has been setup.
Returning: 11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
......
other exception content
2
3
4
5
6
7
8
9
10
11
12
13
14
如果需要在一个特殊的方法之前或者之后执行通知,可以将切入点定义中的星号(*)替换为真实类和方法名称。
<?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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll"
expression="execution(* com.tutorialspoint.Student.getName(..))" />
<aop:before pointcut-ref="selectAll" method="beforeAdvice" />
<aop:after pointcut-ref="selectAll" method="afterAdvice" />
<aop:after-returning pointcut-ref="selectAll"
returning="retVal"
method="afterReturningAdvice" />
<aop:after-throwing pointcut-ref="selectAll"
throwing="ex"
method="afterThrowingAdvice" />
</aop:aspect>
</aop:config>
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11" />
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging" />
</beans>
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
输出结果:
Going to setup student profile.
Name: Zara
Student profile has been setup.
Age: 11
Exception raised
......
other exception content
2
3
4
5
6
7
# Spring 中基于AOP的@AspectJ
同上一章节,依旧需要引入aspect相关JAR包。
# 声明一个aspect
Aspects类和其他任何正常的bean一样,除了它们用@Aspect
注解外,如下所示:
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AspectModule {
}
2
3
4
5
6
7
其在XML中的配置如下:
<bean id="myAspect" class="org.xyz.AspectModule">
<!-- configure properties of aspect here as normal -->
</bean>
2
3
# 声明一个切入点
切入点的声明有两个部分:
- 一个切入点表达式决定了我们感兴趣的哪个方法会真正执行;
- 一个切入点标签包含一个名称和任意数量的参数。方法的真正内容是不相干的,并且实际上它应该是空的。
切入点示例:
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.xyz.myapp.service.*.*(..))")
private void businessService() {}
2
3
4
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.tutorialspoint.Student.getName(..))")
private void getName() {}
2
3
4
# 声明通知
可使用@{ADVICE-NAME}
注解声明五个通知中的任意一个,如下:
@Before("businessService()")
public void doBeforeTask() {
...
}
@After("businessService()")
public void doAfterTask() {
...
}
@AfterReturning(pointcut = "businessService()", returning="retVal")
public void doAfterReturningTask(Object retVal) {
...
}
@AfterThrowing(pointcut = "businessService()", throwing="ex")
public void doAfterThrowingTask(Exception ex) {
...
}
@Around("businessService()")
public void doAroundTask() {
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
可以为任意一个通知定义切入点内联,示例:
@Before("execution(* com.xyz.myapp.service.*.*(...))")
public doBeforeTask() {
...
}
2
3
4
# 基于@AspectJ的AOP示例
步骤如下:
步骤 | 描述 |
---|---|
1 | 创建一个名为SpringExample的项目,并且在所创建项目的src文件夹下创建一个名为com.tutorialspoint 的包。 |
2 | 使用Add External JARs选项添加所需的Spring库文件 |
3 | 在项目中添加Spring AOP指定的库文件aspectjrt.jar,aspectjweaver.jar和aspectj.jar。 |
4 | 在com.tutorialspoint 包下创建Java类Logging ,Student 和MainApp 。 |
5 | 在src文件夹下创建Beans配置文件Beans.xml 。 |
6 | 最后一步是创建所有Java文件和Bean配置文件的内容。 |
- Logging.java
package com.tutorialspoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
@Aspect
public class Logging {
@Pointcut("execution (* com.tutorialspoint.*.*(..))")
private void selectAll() {}
@Before("selectAll()")
public void beforeAdvice() {
System.out.println("Going to setup student profile.");
}
@After("selectAll()")
public void afterAdvice() {
System.out.println("Student profile has been setup.");
}
@AfterReturning(pointcut = "selectAll()", returning="retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("Returning:" + retVal.toString());
}
@AfterThrowing(pointcut = "selectAll()", throwing = "ex")
public void afterThrowingAdvice(IllegalArgumentException ex) {
System.out.println("There has been an exception: " + ex.toString());
}
}
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
- Student.java
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age: " + age);
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name:" + name);
return name;
}
public void printThrowException() {
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
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
- MainApp.java
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- Beans.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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy />
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11" />
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging" />
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 输出结果
Going to setup student profile.
Name: Zara
Student profile has been setup.
Returning: Zara
Going to setup student profile.
Age: 11
Student profile has been setup.
Returning: 11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
......
other exception content
2
3
4
5
6
7
8
9
10
11
12
13
14
# Spring AOP 应用场景
- Spring 声明式事务管理配置;
- Controller层的参数校验;
- 使用Spring AOP实现MySQL数据库读写分离案例;
- 在执行方法前,判断是否具有权限,Authentication权限检查;
- 对部分函数的调用进行日志记录:监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员;
- 信息过滤,页面转发等功能;
- Cache缓存
- Context passing内容传递;
- Error handling错误处理;
- Lazy loading延迟加载;
- Debugging调试
- logging, tracing, profiling and monitoring 日志记录,跟踪,优化,校准;
- Performance optimization 性能优化,效率检查;
- Persistence 持久化;
- Resource pooling 资源池
- Synchronization 同步;
- Transactions 事务管理;
- Filter的实现和struts2的拦截器的实现都是AOP思想的体现。
Spring 不尝试提供最为完善的AOP实现,它更侧重于提供一种和Spring IOC容器整个的AOP实现,用于解决实际的问题,在Spring中无缝的
整合了Spring AOP、Spring IOC和AspectJ。在使用Spring AOP的时候只是简单的配置一下(通过XML或注解进行配置),Spring AOP在内部ProxyFactory
来创建代理对象,然后调用目标方法。
动态代理或者设计模式很重要!Spring AOP用到了动态代理,Spring事务管理用到了动态代理,MyBatis数据连接池用到了动态代理, MyBatis创建Mapper用到了动态代理等。
- AOP面向切面编程基于IOC,是对OOP的补充;
- AOP利用一种"横切"技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为
Aspect
,即切面。简单地说, 就是将那些业务无关,却为业务模块所共同调用的逻辑或责任封装起来,比如日志记录,便于减少系统的重复代码,降低模块间的耦合,有利于未来的扩展和维护。 - AOP代表的是一个横向的关系,将"对象"比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向切面编程的方法,就是将这个圆柱体以切面形式剖开,
选择性地提供业务逻辑。剖开的切面,就是所谓的
Aspect
,然后再以巧妙的手段将这些剖开的切面复原,不留痕迹。 - 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,
引入特定语法创建
Aspect
,从而使得编译器可以在编译期间织入代码; - Spring实现AOP:JDK动态代理和CGLIB代理。JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象
的代理,其核心的两个类是
InvocationHandler
和Proxy
。CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类 扩展的子类。CGLIB是高效的代码生成包,底层依靠ASM(开源的Java字节码编辑类库)操作字节码实现,性能比JDK好;需要引入asm.jar
和cglib.jar
。