关注点分离原则 Concern Separation
不同的问题交给不同的部分去解决,每部分专注解决自己的问题
-
Aspect Oriented Programming 就是其中一种关注点分离的技术
-
通用化功能的代码实现即切面 Aspect
-
Aspect 之于 AOP,就相当于Class 之于 OOP,Bean之于Spring
AOP与OOP关系图.png
-
切面Aspect:将横切关注点逻辑进行模块化封装的实体对象
-
通知 Advice:好比Class里面的方法,还定义了织入逻辑的时机
-
连接点JoinPoint:允许使用 Advice 的地方
-
Spring AOP 默认只支持方法级别的 JoinPoint
-
Pointcut:定义一系列规则对 JoinPoint 进行筛选
-
目标对象Target:符合Pointcut条件,要被织入横切逻辑的对象
Advice 的种类
-
BeforeAdvice:在JoinPoint 前被执行的 Advice
虽然 BeforeAdvice 在JointPoint 前 被执行,但是 其不能阻止 JointPoint 的执行,除非在 BeforeAdvice 中显式的抛出异常。才能阻止被代理方法的执行。不能在没有发生异常的时候 人为的决定是否继续执行 JointPoint 中的代码
-
AfterAdvice:好比 try..catch..finaly 里面的 finaly 不管被代理方法是否正常执行完毕,这个 AfterAdvice 都会被执行
-
AfterReturningAdvice:在 JointPoint 执行流程正常返回后被执行。
如果被代理方法抛出异常,这个 AfterReturningAdvice就不会被执行
-
AfterThrowingAdvice:在 JointPoint 执行过程中抛出异常才会触发。
-
AroundAdvice:在 JointPoint 前后都执行,最常用的 Advice
单个Aspect的执行顺序
1 AroundAdvice的前置执行方法
2 BeforeAdvice
—上面是入操作
3 被代理方法(JoinPoint逻辑)
—下面是出操作
4 AroundAdvice的后置执行方法
5 AfterAdvice
6 正常结束 AfterReturningAdvice,异常结束 AfterThrowingAdvice

入操作执行完成后,才会执行出操作,中间是 JointPoint逻辑,多个Aspect 通过指定执行顺序,但是大逻辑还是入操作执行完成后才能执行出操作。
多个Aspect的执行顺序

示例代码如下:
public interface HiService {
void sayHi();
String justWantToSayHi();
}
public interface HelloService {
void sayHello();
void justWantToThrowException();
}
@Service
public class HiServiceImpl implements HiService {
@Override
public void sayHi() {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Hi everyOne");
}
@Override
public String justWantToSayHi() {
return "justWantToSayHi";
}
}
@Service
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Hello everyBody");
}
@Override
public void justWantToThrowException() {
throw new RuntimeException("hello exception");
}
}
@Controller
public class HiController {
@Autowired
private HiService hiService;
public void handleRequest() {
hiService.sayHi();
hiService.justWantToSayHi();
}
}
@Controller
public class HelloController {
@Autowired
private HelloService helloService;
public void handleRequest(){
helloService.sayHello();
helloService.justWantToThrowException();
}
}
@Aspect
@Component
public class ServiceAspect {
/**
* 告诉 Spring AOP 往 哪里织入 aspect 里面的逻辑
* * cn.com.yuns.service..*.*(..)
* 第一个 * 表示 任何返回值类型的方法(任意返回值)都会触发 cn.com.yuns.service 基础包名
* .. 基础包名下的以及子包下面的,* 所有的类;.* 类里面的所有方法
* (..)不管方法传入多少参数或没有参数都是被织入的对象
*/
@Pointcut("execution(* cn.com.yuns.service..*.*(..))")
public void embed() {
}
/**
* Advice
* Service 包里的类的任何一个方法 执行之前,嵌入相关逻辑
*/
@Before("embed()")
public void before(JoinPoint joinPoint) {
System.out.println("开始调用:" + joinPoint);
}
/**
* Advice
* Service 包里的类的任何一个方法 执行之后,嵌入相关逻辑
*/
@After("embed()")
public void after(JoinPoint joinPoint) {
System.out.println("开始调用:" + joinPoint);
}
/**
* Advice
* Service 包里的类的任何一个方法 执行前后,嵌入相关逻辑
*/
@Around("embed()")
public Object around(JoinPoint joinPoint) throws Throwable {
Long startTime = System.currentTimeMillis();
Object returnValue = null;
System.out.println("开始记时:" + joinPoint);
returnValue = ((ProceedingJoinPoint) joinPoint).proceed();
System.out.println("执行成功 结束记时");
Long endTime = System.currentTimeMillis();
System.out.println("耗时:" + (endTime - startTime) + "ms");
return returnValue;
}
/**
* Advice
*
* @param joinPoint
* @param returnValue
*/
@AfterReturning(pointcut = "embed()", returning = "returnValue")
public void afterReturning(JoinPoint joinPoint, Object returnValue) {
System.out.println("无论是空还是有值都返回 " + joinPoint + ",返回值[" + returnValue + "]");
}
/**
* Advice
*
* @param joinPoint
* @param exception
*/
@AfterThrowing(pointcut = "embed()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Throwable exception) {
System.out.println("抛出异常通知 " + joinPoint + " " + exception.getMessage());
}
/**
* 注解引入
*
* @param args
*/
public static void main(String args[]) {
System.out.println("Spring 项目成功编译!!!!走注解使用Spring");
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Entrance.class);
System.out.println("轮到 AOP 登场了!!!!");
HiController hiController = (HiController) applicationContext.getBean("hiController");
HelloController helloController = (HelloController) applicationContext.getBean("helloController");
helloController.handleRequest();
hiController.handleRequest();
}
输出:
轮到 AOP 登场了!!!!
开始记时:execution(void cn.com.yuns.service.HelloService.sayHello())
开始调用:execution(void cn.com.yuns.service.HelloService.sayHello())
Hello everyBody
执行成功 结束记时
耗时:2003ms
开始调用:execution(void cn.com.yuns.service.HelloService.sayHello())
无论是空还是有值都返回 execution(void cn.com.yuns.service.HelloService.sayHello()),返回值[null]
开始记时:execution(void cn.com.yuns.service.HelloService.justWantToThrowException())
开始调用:execution(void cn.com.yuns.service.HelloService.justWantToThrowException())
开始调用:execution(void cn.com.yuns.service.HelloService.justWantToThrowException())
抛出异常通知 execution(void cn.com.yuns.service.HelloService.justWantToThrowException()) hello exception
Exception in thread "main" java.lang.RuntimeException: hello exception
at cn.com.yuns.service.impl.HelloServiceImpl.justWantToThrowException(HelloServiceImpl.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at cn.com.yuns.aspect.ServiceAspect.around(ServiceAspect.java:56)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy37.justWantToThrowException(Unknown Source)
at cn.com.yuns.controller.HelloController.handleRequest(HelloController.java:19)
at cn.com.yuns.demo.Entrance.main(Entrance.java:67)
Task :springdemo:Entrance.main() FAILED
补充:
Introduction-引入型Advice
-
为目标类引入新接口,而不需要目标类做任何实现。
-
使得目标类在使用的过程中转型成新接口对象,调用新的接口方法。
示例代码:
/**
* 包下的所有类
* 让包下的所有类 变身为 LittleUniverse
*/
@DeclareParents(value = "cn.com.yuns.controller..*",defaultImpl =cn.com.yuns.Introduction.LittleUniverseImpl.class)
public LittleUniverse littleUniverse;
/**
* 注解引入
*
* @param args
*/
public static void main(String args[]) {
System.out.println("Spring 项目成功编译!!!!走注解使用Spring");
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Entrance.class);
System.out.println("轮到 AOP 登场了!!!!");
HiController hiController = (HiController) applicationContext.getBean("hiController");
LittleUniverse littleUniverse = (LittleUniverse) hiController;
littleUniverse.burningup();
}
输出:
轮到 AOP 登场了!!!!
燃烧吧!!!!!
BUILD SUCCESSFUL in 23s
AOP需要OOP理解自己的语义,所以并不像单独使用那么灵活
织入:将Aspect 的横切关注点集成到OOP中
织入器:完成织入过程的执行者,(AspectJ 就有一个ajc编译器,完成AspectJ 对程序的织入,Spring AOP则会使用一组类来作为织入器来完成最终的织入操作)
Spring默认只支持方法级别的连接点,
如果有需要将成员变量,构造函数作为 JoinPoint,Spring不支持的功能的话,可以使用AspectJ来满足
网友评论