这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战
AOP是什么
AOP是面向切面编程(Aspect Oriented Programming) Aspect表示给业务方法增加的功能叫做切面,切面一般不是主要的业务功能,一般都是可以服用的一些功能,如:日志输出,权限检查等等不是业务逻辑的功能。AOP就是要在不影响逻辑的情况下把这些功能加进去。在开发时我们要找出切面的功能,选择切面执行的时间,执行的位置。
AOP的作用
- 让切面的功能可以复用
- 让我们开发时只专注业务逻辑的开发,提升开发效率
- 解耦合我们的业务功能和非业务的功能
- 可以给原有业务添加功能时,只需修改切面,而不去动我们的业务代码
AOP的术语
- Aspect :切面 增加的非业务的功能
- JoinPoint:连接点 连接切面的业务方法,执行业务代码时执行我们的切面
- PointCut : 切入点 切面执行的位置
- Target : 目标对象 要增加切面的功能的对象
- Advice : 增强,表示切面执行在业务代码的执行时间 串起来就是在Advice的时间在PointCut的地点执行Aspect,AOP是一个动态的思想,是在程序执行期间进行的操作,程序执行时创基代理,使用代理对象去执行方法,就增加上了切面的功能。
什么时候用AOP
给代码增加功能但不改变原代码
AOP的技术实现
一个是Spring的实现了AOP中的部分功能,但是比较繁琐笨重,另一个是AspectJ,独立的框架专门做AOP的,专业的人做专业的事
使用AspectJ框架实现AOP
Advice
AspectJ表示切面执行的时间使用Advice可以使用注解来实现,有五个通知注解表示切面的五个执行时间
- @Before 前置通知
- @AfterReturing 后置通知
- @Around 环绕通知
- @AfterThrowing 异常通知
- @After 最终通知
PointCut
切面执行的位置,使用AspectJ的切入点表达式,切入点表达式的语法如下
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern] 访问权限类型
- ret-type-pattern 返回值类型
- declaring-type-pattern 包名类名
- name-pattern(param-pattern) 方法名(参数类型和参数个数)
- throws-pattern 抛出异常类型
- ?表示可选的部分
execution(访问权限 方法返回值 包名类名 方法声明(参数) 异常类型)除去带问号的可以省略的可以简写为 execution( 方法返回值 方法声明(参数))
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
@Before
定义方法 定义切面的具体功能前置通知方法的定义 1) 方法是public 2) 方法是void 3) 方法名称自定义 4) 方法可以有参数,如果是PointCut也可以没有
@Before; 前置通知
属性: value 切入点表达式。切面的执行位置
位置: 方法之上
特点
1 执行时间,在目标方法之前执行
2 不会要意向目标方法的原有功能
3 不会修改原方法的执行结果
public interface doService {
public void doSome(String name,Integer id);
}
public class doSomeImpl implements doService {
@Override
public void doSome(String name,Integer id) {
System.out.println("做了业务");
}
}
写了很多种形式的表达式
//@Before(value = "execution(public void org.example.Service.impl.doSomeImpl.doSome(String,Integer))")
//@Before("execution(void doSome(String,Integer))")
// @Before("execution(void *..do*(..))")
//@Before("execution(void org..*(..))")
@Before("execution(void *..*(..))")
public void beforeAdvice(){
//切面功能,开始执行时间
System.out.println("@Before 前置切面功能 在切点之前执行"+new Date());
}
<bean id="doSomeImpl" class="org.example.Service.impl.doSomeImpl"></bean>
<bean id="myHander" class="org.example.hander.myHander"></bean>
<!--
声明自动代理生成器用来创建目标对象的代理,调用AspectJ的功能,寻找所有的目标增强对象
把每个目标对象加入到切面类的功能生成代理对象,去执行功能
-->
<aop:aspectj-autoproxy />
测试类
@Test
public void shouldAnswerWithTrue()
{
//配置文件地址
String configPath="ApplicationConttext.xml";
//2创建对象容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
doService service= (doService) applicationContext.getBean("doSomeImpl");
System.out.println(service.getClass().getName());
service.doSome("1",1);
}
完美的把切面功能加进去了,对象类型是一个com.sun.proxy.$Proxy6代理类
JoinPoint
用来获取正在执行的方法信息
public void beforeAdvice(JoinPoint jp){
System.out.println("正在执行的方法"+ jp.getSignature());
//方法名称
System.out.println("正在执行的方法名称"+ jp.getSignature().getName());
//参数
System.out.println("正在执行方法的参数");
Object[] args = jp.getArgs();
for (Object arg : args) {
System.out.println(arg);
}
System.out.println("正在执行方法的参数");
//切面功能,开始执行时间
System.out.println("@Before 前置切面功能 在切点之前执行"+new Date());
}
@AfterRetueuing
后置通知,可以拿到方法的返回值,对结果进行操作
@AfterReturning(value = "execution(* *..doOther(..)))",returning = "res")
public void beforeAdvice(Object res){
//切面功能,开始执行时间
System.out.println("@AfterReturning 前置切面功能 在切点之后执行,能拿到你的执行结果"+res);
}
@Override
public String doOther(String name, Integer id) {
System.out.println("other");
return name+id;
}
@Test
public void shouldAnswerWithTrue()
{
//配置文件地址
String configPath="ApplicationConttext.xml";
//2创建对象容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
doService service= (doService) applicationContext.getBean("doSomeImpl");
service.doOther("张三身高",188);
}
明天继续肝,坚持不断更