一、AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。
二、AOP在Spring中的作用
提供声明式服务(最重要的服务是声明式事务管理)
允许用户实现自定义切面
三、AOP:在不改变原有代码的情况下,增加新的功能。
传统的编程模式:
|
AOP的编程模式:横向的编程
|
AOP就是通过代理(Proxy)的方式来实现的。 |
四、AOP的好处(就是使用代理的好处)
使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情。
公共的业务由代理来完成 --- 实现了业务的分工。
公共业务发生扩展时变得更加集中和方便。
|
五、名词解释
关注点:增加的某个业务。如:日志、安全、缓存、事务、异常处理等。
- 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式)或者基于@Aspect注解的方式来实现。
- 连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
- 通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
- 切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
- 引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
- 目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
- AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
- 织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
通知类型:
- 前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
- 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
- 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
- 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
- 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
|
五、使用Spring实现AOP
第一种实现方式:通过Spring API来实现
导包:
<
dependency
>
<
groupId
>
org.springframework
</
groupId
>
<
artifactId
>
spring-webmvc
</
artifactId
>
<
version
>
4.2.1.RELEASE
</
version
>
</
dependency
>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
目标类:
public
class
UserServiceImpl
implements
UserService {
public
void
add() {
System.
out
.println(
"增加用户"
);
}
public
void
update() {
System.
out
.println(
"修改用户"
);
}
public
void
delete() {
System.
out
.println(
"删除用户"
);
}
public
void
search() {
System.
out
.println(
"查询用户"
);
}
}
通知:
前置通知:
public
class
Log
implements
MethodBeforeAdvice {
/**
*
@param
method 被调用的方法对象
*
@param
args 被调用的方法的参数
*
@param
target 被调用的方法的目标对象
*/
public
void
before(Method
method, Object[]args, Objecttarget)
throws
Throwable {
System.
out
.println(
target
.getClass().getName() +
"的"
+
method
.getName() +
"方法被执行"
);
}
}
后置通知:
public
class
AfterLog
implements
AfterReturningAdvice {
/**
* 目标方法执行后执行的通知
*
@param
returnValue 被调用的方法对象的返回值
*
@param
method 被调用的方法对象
*
@param
args 被调用的方法对象的参数
*
@param
target 被调用的方法对象的目标对象
*/
public
void
afterReturning(Object
returnValue, Methodmethod, Object[]args, Objecttarget)
throws
Throwable {
System.
out
.println(
target
.getClass().getName() +
"的"
+
method
.getName()
+
"被成功执行,返回值是:"
+
returnValue
);
}
}
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"
>
<
bean
id
=
"userService"
class
=
"com.liujie.service.impl.UserServiceImpl"
></
bean
>
<
bean
id
=
"log"
class
=
"com.liujie.log.Log"
></
bean
>
<
bean
id
=
"afterLog"
class
=
"com.liujie.log.AfterLog"
></
bean
>
<
aop:config
>
<
aop:pointcut
expression
=
"execution(* com.liujie.service.impl.*.*(..))"
id
=
"pointcut"
/>
<
aop:advisor
advice-ref
=
"log"
pointcut-ref
=
"pointcut"
/>
<
aop:advisor
advice-ref
=
"afterLog"
pointcut-ref
=
"pointcut"
/>
</
aop:config
>
</
beans
>
测试:
public
class
Test {
public
static
void
main(String[]
args
) {
ApplicationContext
context
=
new
ClassPathXmlApplicationContext(
"beans.xml"
);
UserService
userService
= (UserService)
context
.getBean(
"userService"
);
userService
.add();
}
}
AOP的重要性:很重要。
Spring AOP就是将公共的业务(如:日志、安全等)和领域业务结合。当执行领域业务时,将会把公共业务加进来。实现公共业务的重复
利用。领域业务更纯粹。程序员专注于领域业务。
其本质还是动态代理。
|
第二种实现方式:自定义类来实现
public
class
UserServiceImpl
implements
UserService {
public
void
add() {
System.
out
.println(
"增加用户"
);
}
public
void
update() {
System.
out
.println(
"修改用户"
);
}
public
void
delete() {
System.
out
.println(
"删除用户"
);
}
public
void
search() {
System.
out
.println(
"查询用户"
);
}
}
public
class
Log {
public
void
before() {
System.
out
.println(
"---------方法执行前--------"
);
}
public
void
after() {
System.
out
.println(
"---------方法执行后--------"
);
}
}
<?
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
=
"userService"
class
=
"com.liujie.service.impl.UserServiceImpl"
></
bean
>
<
bean
id
=
"log"
class
=
"com.liujie.log.Log"
></
bean
>
<
aop:config
>
<
aop:aspect
ref
=
"log"
>
<
aop:pointcut
expression
=
"execution(* com.liujie.service.impl.*.*(..))"
id
=
"pointcut"
/>
<
aop:before
method
=
"before"
pointcut-ref
=
"pointcut"
/>
<
aop:after
method
=
"after"
pointcut-ref
=
"pointcut"
/>
</
aop:aspect
>
</
aop:config
>
</
beans
>
public
class
Test {
public
static
void
main(String[]
args
) {
ApplicationContext
context
=
new
ClassPathXmlApplicationContext(
"beans.xml"
);
UserService
userService
= (UserService)
context
.getBean(
"userService"
);
userService
.add();
}
}
第三种实现方式:通过注解来实现
@Aspect
public
class
Log {
@Before
(
"execution(* com.liujie.service.impl.*.*(..))"
)
public
void
before() {
System.
out
.println(
"---------方法执行前--------"
);
}
@After
(
"execution(* com.liujie.service.impl.*.*(..))"
)
public
void
after() {
System.
out
.println(
"---------方法执行后--------"
);
}
@Around
(
"execution(* com.liujie.service.impl.*.*(..))"
)
public
Object around(ProceedingJoinPoint
joinPoint
)
throws
Throwable {
System.
out
.println(
"还绕前"
);
System.
out
.println(
"签名:"
+
joinPoint
.getSignature());
//执行目标方法
Object
result
=
joinPoint.proceed();
System.
out
.println(
"还绕后"
);
return
result
;
}
}
<?
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
=
"userService"
class
=
"com.liujie.service.impl.UserServiceImpl"
></
bean
>
<
bean
id
=
"log"
class
=
"com.liujie.log.Log"
></
bean
>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</
beans
>
使用第一种方式实现异常通知的AOP:
Spring提供类型检查的异常通知(typed throws advice),这意味着org.springframework.aop.
ThrowsAdvice接口不包含任何方法:它只是一个标记接口用来标识所给对象实现了一个或者多个针对特定类型的异常通知方法。这些方法应当满足下面的格式:
afterThrowing([Method, args, target],subclassOfThrowable)
只有最后一个参数是必须的。根据异常通知方法对方法及参数的需求,方法的签名可以有一个或者四个参数。
public
class
ExceptionLog
implements
ThrowsAdvice {
public
void
afterThrowing(Exception
ex
)
throws
Throwable {
}
}
|
下面是一些异常通知的例子。
当一个RemoteException(包括它的子类)被抛出时,下面的通知会被调用:
public
class
RemoteThrowsAdvice
implements
ThrowsAdvice {
public
void
afterThrowing(RemoteException
ex
)
throws
Throwable {
// Do something with remote exception
}
}
当一个ServletException被抛出,下面的通知将被调用。 和上面的通知不同,它声明了4个参数,因此它可以访问被调用的方法,方法的参数以及目标对象:
public
class
ServletThrowsAdviceWithArguments
implements
ThrowsAdvice {
public
void
afterThrowing(Method
m
, Object[]
args
, Object
target
, ServletException
ex
) {
// Do something with all arguments
}
}
最后一个例子说明怎样在同一个类里使用两个方法来处理 RemoteException和ServletException。可以在一个类里组合任意数量的异常通知方法。
public
static
class
CombinedThrowsAdvice
implements
ThrowsAdvice {
public
void
afterThrowing(RemoteException
ex
)
throws
Throwable {
// Do something with remote exception
}
public
void
afterThrowing(Method
m
, Object[]
args
, Object
target
, ServletException
ex
) {
// Do something with all arguments
}
}
|