三.AOP
1.简介
Aspect Orientied Programming
面向切面编程
使得应用易于扩展
开闭原则
将应用中所需要使用的交叉业务逻辑提取出来封装成切面
由AOP容器在适当的时机
将这些切面织入到具体的业务逻辑中
2.目的
解耦合
将具体的核心业务逻辑与交叉业务逻辑相分离 切面的复用
一个切面被多次使用 独立模块化
例如原本交叉业务逻辑做的打印操作将打印操作统一更换为日志操作只要切面改了,所有的业务均跟着改变独立模块化的前提是切面的复用 在不改变原有功能的基础上,增加新的功能
3.动态代理
AOP是基于动态代理来实现的
动态代理是根据目标类的类加载器、代理的接口、交叉业务逻辑
生成对应的目标类的代理类
public class LogInvocationHandler implements InvocationHandler {
private Object target
;
public LogInvocationHandler(Object target
) {
this.target
= target
;
}
@Override
public Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{
System
.out
.println("在"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())+"执行了"+method
.getName()+"方法");
return method
.invoke(target
,args
);
}
}
public static void main(String
[] args
) {
SomeService someService
= (SomeService
) Proxy
.newProxyInstance(
SomeServiceImpl
.class.getClassLoader(),
SomeServiceImpl
.class.getInterfaces(),
new LogInvocationHandler(new SomeServiceImpl())
);
someService
.doSome();
System
.out
.println("---------------------------");
someService
.doOther();
System
.out
.println("----------------------------------------");
OtherService otherService
= (OtherService
) Proxy
.newProxyInstance(
OtherServiceImpl
.class.getClassLoader(),
OtherServiceImpl
.class.getInterfaces(),
new LogInvocationHandler(new OtherServiceImpl())
);
otherService
.doSome();
System
.out
.println("----------------------");
otherService
.doOther();
}
4.AOP1.X
倾入性
4-1 POM依赖
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-aop
</artifactId>
<version>${spring.version}
</version>
</dependency>
4-2 通知类型
通知类型描述接口
前置通知在执行核心业务逻辑之前执行MethodBeforeAdvice后置通知在执行核心业务逻辑之后执行AfterReturningAdvice异常通知在执行核心业务逻辑出现异常后执行ThrowsAdvice环绕通知包含以上三种MethodInterceptor
4-3 前置通知
public class LogAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method
, Object
[] args
, Object target
) throws Throwable
{
System
.out
.println("在"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())+"执行了"+target
+"中的"+method
.getName()+"方法");
}
}
4-4 后置通知
public class WelcomeAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue
, Method method
, Object
[] args
, Object target
) throws Throwable
{
System
.out
.println(target
+"中的"+method
.getName()+"方法执行完成,返回值为:"+returnValue
);
}
}
4-5 异常通知
public class ExceptionAdvice implements ThrowsAdvice {
public void afterThrowing(Method method
, Object
[] args
, Object target
, Exception ex
){
System
.out
.println(target
+"中的"+method
.getName()+"方法执行出现了异常,异常为:"+ex
);
}
}
4-6 环绕通知
public class AroundAdvice implements MethodInterceptor {
@Override
public Object
invoke(MethodInvocation invocation
) throws Throwable
{
Object targte
= invocation
.getThis();
Method method
= invocation
.getMethod();
Object
[] args
= invocation
.getArguments();
Object returnValue
= null
;
try {
System
.out
.println("环绕通知之前置通知");
long begin
= System
.currentTimeMillis();
returnValue
= invocation
.proceed();
long end
= System
.currentTimeMillis();
System
.out
.println("环绕通知之后置通知,执行方法共花费了"+(end
-begin
)+"毫秒");
} catch (Throwable ex
) {
System
.out
.println("环绕通知之异常通知");
}
return returnValue
;
}
}
4-7 配置文件
<bean id="someServiceTarget" class="aop03.SomeServiceImpl"></bean>
<bean id="otherServiceTarget" class="aop03.OtherServiceImpl"></bean>
<bean id="logAdvice" class="aop03.LogAdvice"></bean>
<bean id="welcomeAdvice" class="aop03.WelcomeAdvice"></bean>
<bean id="exceptionAdvice" class="aop03.ExceptionAdvice"></bean>
<bean id="aroundAdvice" class="aop03.AroundAdvice"></bean>
<bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someServiceTarget"></property>
<property name="proxyInterfaces" value="aop03.SomeService"></property>
<property name="interceptorNames">
<list>
<value>aroundAdvice
</value>
</list>
</property>
</bean>
<bean id="otherService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="otherServiceTarget"></property>
<property name="proxyInterfaces" value="aop03.OtherService"></property>
<property name="interceptorNames">
<list>
<value>logAdvice
</value>
<value>welcomeAdvice
</value>
</list>
</property>
</bean>
4-8 通知的优先级
在AOP1.X中,通知的优先级是根据配置的位置而定的
谁的配置在前,谁的优先级高
优先级高的前置通知执行的时机更前
优先级高的后置通知执行的时机更后
5.AOP2.X
5-1 特征
非倾入性完全基于配置引入了新的命名空间引入了切点表达式
5-2 POM依赖
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-aop
</artifactId>
<version>${spring.version}
</version>
</dependency>
<dependency>
<groupId>aopalliance
</groupId>
<artifactId>aopalliance
</artifactId>
<version>1.0
</version>
</dependency>
<dependency>
<groupId>org.aspectj
</groupId>
<artifactId>aspectjweaver
</artifactId>
<version>1.9.4
</version>
</dependency>
<dependency>
<groupId>org.aspectj
</groupId>
<artifactId>aspectjrt
</artifactId>
<version>1.9.4
</version>
</dependency>
5-3 通知类型
前置通知
在执行核心业务逻辑之前执行 后置通知
正常返回通知
当方法正常执行结束,没有遇到异常的时候执行 异常通知
当方法运行出现异常的时候执行 后置通知
不管方法是否遇到异常均会执行 环绕通知
包含以上所有
5-4 无参通知
public class LogAdvice {
public void a(){
System
.out
.println("前置通知");
}
public void b(){
System
.out
.println("正常返回通知");
}
public void c(){
System
.out
.println("异常通知");
}
public void d(){
System
.out
.println("后置通知");
}
}
<bean id="someService" class="aop04.SomeServiceImpl"></bean>
<bean id="otherService" class="aop04.OtherServiceImpl"></bean>
<bean id="logAdvice" class="aop04.LogAdvice"></bean>
<aop:config>
<aop:pointcut id="pc1" expression="within(aop04.SomeServiceImpl)"></aop:pointcut>
<aop:aspect ref="logAdvice">
<aop:before method="a" pointcut-ref="pc1"/>
<aop:after-returning method="b" pointcut-ref="pc1"/>
<aop:after-throwing method="c" pointcut-ref="pc1"/>
<aop:after method="d" pointcut-ref="pc1"/>
</aop:aspect>
</aop:config>
5-5 切点表达式
within
匹配某个类中的所有方法语法:within(包名.类名) execution
匹配你想要的一切可以是类,可以是方法语法:execution(返回值类型 包名.类名.方法名(参数列表))支持通配符用法
*
用法一:匹配0或者多个字符用法二:匹配一个单词 ..
表示匹配0或者多个参数 支持连接条件
连接条件可以使用within,也可以使用executionand:且的意思,多个条件必须同时满足or:或的意思,多个条件只要满足任意一个即可not:非的意思,匹配不满足条件的方法
使用not前面必须有空格
<aop:pointcut id="pc1" expression="within(aop04.SomeServiceImpl)"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(void aop04.SomeServiceImpl.doSome())"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(java.lang.String aop04.SomeServiceImpl.doSome(java.lang.String))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.SomeServiceImpl.do*(*))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.SomeServiceImpl.do*(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.*.*(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.SomeServiceImpl.doSome(..))"></aop:pointcut>
<aop:pointcut id="pc2" expression="execution(* aop04.OtherServiceImpl.doOther(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.SomeServiceImpl.doSome(..)) or execution(* aop04.OtherServiceImpl.doOther(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.SomeServiceImpl.*(..)) or execution(* aop04.OtherServiceImpl.doOther(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.SomeServiceImpl.*())"></aop:pointcut>
<aop:pointcut id="pc1" expression="within(aop04.SomeServiceImpl) and not execution(* aop04.SomeServiceImpl.doSome(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression=" not execution(* aop04.OtherServiceImpl.doOther(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="within(aop04.SomeServiceImpl) or within(aop04.OtherServiceImpl) and not execution(* aop04.OtherServiceImpl.doOther(..))"></aop:pointcut>
<aop:pointcut id="pc1" expression="execution(* aop04.*.*(..)) and not execution(* aop04.*.doSome(..))"></aop:pointcut>
5-6 有参通知
在AOP2.X中,如何获取与目标方法相关的信息
此时需要使用有参通知来实现
使用有参通知的时候,其参数必须是:JoinPoint
public class LogAdvice {
public void before(JoinPoint jp
){
Object target
= jp
.getThis();
Signature signature
= jp
.getSignature();
Object
[] args
= jp
.getArgs();
System
.out
.println("前置通知:"+target
+"中的"+signature
.getName()+"方法即将执行");
}
public void afterReturning(JoinPoint jp
, Object returnValue
){
System
.out
.println("正常返回通知:"+jp
.getSignature().getName()+"方法执行完成,方法返回值为:"+returnValue
);
}
public void afterThrowing(JoinPoint jp
, Exception e
){
System
.out
.println("异常通知:"+jp
.getSignature().getName()+"方法执行出现了异常,异常为:"+e
);
}
public void after(JoinPoint jp
){
System
.out
.println("后置通知:"+jp
.getSignature().getName()+"方法执行结束");
}
}
<bean id="someService" class="aop05.SomeServiceImpl"></bean>
<bean id="logAdvice" class="aop05.LogAdvice"></bean>
<aop:config>
<aop:pointcut id="pc1" expression="within(aop05.SomeServiceImpl)"/>
<aop:aspect ref="logAdvice">
<aop:before method="before" pointcut-ref="pc1"/>
<aop:after-returning method="afterReturning" pointcut-ref="pc1" returning="returnValue"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pc1" throwing="e"/>
<aop:after method="after" pointcut-ref="pc1"/>
</aop:aspect>
</aop:config>
5-7 环绕通知
环绕通知对方法存在三个要求
方法必须有返回值
返回值类型必须是Object表示的就是目标方法的返回值 方法必须有参数
参数为:proceedingJoinPoint该参数是JoinPoint的子类可以获取与目标方法相关的信息可以用于执行目标方法 方法必须抛出Throwable
public class AroundAdvice {
public Object
around(ProceedingJoinPoint jp
) throws Throwable
{
Object returnValue
= null
;
System
.out
.println("环绕通知之前置通知");
long begin
= System
.currentTimeMillis();
try {
returnValue
= jp
.proceed();
long end
= System
.currentTimeMillis();
System
.out
.println("环绕通知之正常返回通知,执行方法共花费了"+(end
-begin
)+"毫秒");
} catch (Throwable throwable
) {
System
.out
.println("环绕通知之异常通知");
} finally {
System
.out
.println("环绕通知之后置通知");
}
return returnValue
;
}
}
<bean id="someService" class="aop05.SomeServiceImpl"></bean>
<bean id="aroundAdvice" class="aop05.AroundAdvice"></bean>
<aop:config>
<aop:pointcut id="pc1" expression="within(aop05.SomeServiceImpl)"/>
<aop:aspect ref="aroundAdvice">
<aop:around method="around" pointcut-ref="pc1"/>
</aop:aspect>
</aop:config>
5-8 优先级
默认情况下,AOP2.X的优先级与AOP1.X一致
根据配置的位置而定
谁的配置在前,谁的优先级高
优先级高的前置通知执行时机更前
优先级高的后置通知执行时机更后
在AOP2.X可以手动配置优先级
在切面中进行配置,在aop:aspect标签中通过order属性定义优先级
值越小,优先级越高
<aop:aspect ref="logAdvice" order="2">
<aop:before method="before" pointcut-ref="pc1"/>
<aop:after-returning method="afterReturning" pointcut-ref="pc1" returning="returnValue"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pc1" throwing="e"/>
<aop:after method="after" pointcut-ref="pc1"/>
</aop:aspect>
<aop:aspect ref="aroundAdvice" order="1">
<aop:around method="around" pointcut-ref="pc1"/>
</aop:aspect>