使用AOP的目的:
- 根据业务的需要,把分散的代理抽取出来,称为一个切面类。然后根据需要织入到目标对象的方法中,从而可以
很灵活的增强原来目标方法的功能。比如事务的管理,就是这样的一种思想。 - 像这样通过注解或者配置方式进行处理,是因为这样的实现更加简单,否则要完成这样的任务是非常困难的。所以spring就提供了专门处理这种任务的方式,这就是所谓的AOP
什么时候用AOP
- 比如日志的统一管理、权限的管理等都可能需要使用
通过AspectJ注解方式去增强目标对象的流程
(1)定义切面类、(2)添加注解、(3)编写增强代码、(4)确定增强位置(编写表达式)
- 添加注解:@Aspect和@Component,它们的作用是告诉spring框架,这是类是一个切面。同时该类由spring容器创建、管理
- 编写增强的代码:一般放到方法中,增强类型有前置、后置、环绕、异常、最终等5种增强
- 增强位置:@Before、@AtferReturning、@Around、@AfterThrowing、@After,添加的时候需要
- 编写表达式,表达式是用来描述目标对象的哪些目标方法会被对应的增强所扩展
注意:平时并不是所有的增强类型都需要写,是根据业务的需要处理的
在spring的配置文件种添加<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
配置,启动@aspectJ,同时还需要扫描切面类,目的是让切面类添加到spring容器
一定要在pom.xml添加对应aop的支持
场景乱入
在很远很远的一个地方,有一个桃源小镇。小镇的村民们男耕女织各司其职,可谓是安居也乐业。我们的主角Spring是一个年轻美少男,大家都叫他轻量级框架。
不知道为什么Spring最近总显得闷闷不乐,作为spring的好兄弟Aop看到了,便走向前去。
AOP:“spring大哥你为什么显得这么闷闷不乐呀”。
spring:“好兄弟我遇到了一个大难题。我想吃虎皮鸡蛋(剥壳油炸),但我觉得剥鸡蛋壳很麻烦,我不喜欢剥鸡蛋壳我想不剥鸡蛋皮就能对鸡蛋壳里的蛋清和蛋黄进行油炸。这可真让框架苦恼”。
AOP:“大哥别慌我能帮你搞定你的难题。我最近去蓝翔学了一手精妙的烹饪技术,再加上我天赋异禀能够面向切面,我直接就能不剥壳把鸡蛋取出来,然后在用我的厨艺给你加工加工。”
Spring:“OH~AOP兄弟。你可真是上帝派来的天使。那下面你该怎么做呢?”
AOP:“Spring大哥别慌请移步到厨房,且看我慢慢给你操作”。
AOP:“首先我先找到你的鸡蛋,把它设为目标对象(Target
)然后开启我的魔法引擎,对着鸡蛋在嘴里默念一声@Aspect
(切面),这样我就可以把蛋壳、蛋皮、蛋清、蛋黄分别看成不同的部分(JointPoint
连接点),但我只需要要单单把蛋清拿出来油炸一下就好了,所以我单单取出蛋清(PointCut
切点),然后把它放到油锅里炸一下(Advice
增强),Spring大哥你听见了吗?蛋清和热油碰撞发出吱吱的声响还有诱人的香气(weaving
织入),用不了一会就能大功告成了。”
Spring:“谢谢好兄弟,真不知如何感谢你。不如今晚到我那里去吃两杯头孢泡的药酒。”
AOP:“不了Spring 大哥 我就想单纯的写个笔记。我们下次再约好了。”
Advice 增强处理位置
@Before
在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)@AfterReturning
在一个 join point 正常返回后执行的 advice@AfterThrowing
当一个 join point 抛出异常后执行的 advice@After(final)
无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.Around
在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.@introduction
为原有的对象增加新的属性和方法
项目演示:
pom依赖
<!--AOP增强的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
Application.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:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!--
扫描包的作用:
1)如果在类上面和属性上面添加的注解Spring容器就会自动的创建bean和注入Bean
2)大家可以把注解看出一种标志,spring扫描包的时候,如果发现包上面和属性上面有注解
那么Spring容器就会把有注解的Bean进行创建和装配
-->
<context:component-scan base-package="com.cn.dao,com.cn.service"></context:component-scan>
<context:component-scan base-package="com.cn.dao,com.cn.aspect"></context:component-scan>
<!--启动@AspectJ的注解支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
切面类:
package com.java.aspect;
import com.java.bean.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 作用:定义的切面类
* 用法:在类的上面添加@Aspect,那么该类就是一个切面类
* JoinPoint:可以获得目标方法的相关的信息,比如参数和返回值,方法的名称等
* ProceedingJoinPoint:除了具备JoinPoint的功能外,它还 提供了其他方法比如 proceed(),
* 可以用来调用目标的功能代码
*/
@Aspect
@Component
public class UserServiceLogger {
//日志对象
private static final Logger logger=
LoggerFactory.getLogger(UserServiceLogger.class);
//表达式描述的是对 返回public访问修改符 返回值是boolean类型的 在com.java.service下面的子包中,
// 满足描述的特征的方法进行前置增强。如果不满足特征是不会进行增强的
//try块
@Before("execution(public boolean com.java.service..*.*(..))")
public void before(JoinPoint jp){
logger.info("前置增强==========");
//logger.info(jp.getSignature().getName());
}
//try块
@AfterReturning(pointcut = "execution(public boolean com.java.service..*.*(..))",returning = "returnValue")
public void afterReturing(JoinPoint jp,boolean returnValue){
logger.info("后置增强==========");
logger.info(jp.getSignature().getName());
Object param=jp.getArgs()[0];
logger.info("参数:"+param.toString());
//可以在后置增强中获得目标方法的返回值的
logger.info("返回值:"+returnValue+"");
}
//在catch块中插入代码
@AfterThrowing(value = "execution(public boolean com.java.service..*.*(..))",throwing = "e")
public void afterThrowing(JoinPoint jp,RuntimeException e){
logger.info("异常增强");
logger.info("异常是:"+e);
}
//在finally块
@After("execution(public boolean com.java.service..*.*(..))")
public void after(JoinPoint jp){
logger.info("最终增强");
}
@Around("execution(public boolean com.java.service..*.*(..))")
public Object around(ProceedingJoinPoint jp){
Object object=null;
try {
logger.info("环绕前置");
//执行业务代码
object=jp.proceed();
logger.info("环绕后置");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return object;
}
}