提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、什么是AOP
1.对AOP的解释
什么是AOP呢,下面选用了一段比较专业的话来解释:
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
专业的术语总是晦涩难懂,那么我们如何自己去理解呢?这里举了一个简单的例子:
要理解切面编程,就需要先理解什么是切面。用刀把一个西瓜分成两瓣,切开的切口就是切面;炒菜,锅与炉子共同来完成炒菜,锅与炉子就是切面。web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
具体含义
AOP(Aspect Oriented Programming): 面向切面编程;
Aspect: 切面,给你的目标类增加的功能,就是切面。像日志,事务都是切面。切面的特点:一般都是非业务方法,独立使用的;
Oriented: 对着,面向;
Programming: 编程;
AOP:面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式。Aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。
在spring框架中实现了两种动态代理:
- jdk动态代理,使用jdk中的Proxy, Method, Invocai tonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口;
- cglib动态代理:第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的;
2.怎么理解面向切面编程
- 需要在分析项目功能时,找出切面;
- 合理的安排切面执行的时间(在目标方法前,还是目标方法后);
- 合理的安排切面的执行位置,在哪个类,哪个方法增加增强功能;
3.AOP的作用
- 在目标类不修改源代码的情况下,增加功能;
- 减少重复的代码;
- 专注业务功能的实现
- 解耦合:业务功能和日志,事务这些非业务功能的耦合;
4.什么时候考虑使用AOP技术
- 当你要给一个系统中存在的类修改功能,但是原有类的功能不完善,但是你还有源代码,使用aop就增加功能;
- 你要给项目中的多个类,增加一个相同的功能,使用aop
- 给业务方法增加事务,日志输出
5.AOP的实现框架
- Spring实现了AOP,实现方式时接口;
- aspectj框架:
1)使用注解可以实现AOP功能
2)使用XML配置文件中的标签实现AOP功能
二、AOP的使用
1.aspectj框架的使用
1.表示切面的执行时间,使用的通知注解
- @Before:前置通知,在目标方法之前先执行切面的功能;
- @After:最终通知,在目标方法之后执行切面的功能;
- @AfterRerurnning:后置通知,在目标方法返回后执行切面的功能;
- @AfterThrowing:异常通知,在目标发生异常时执行切面的功能;
- @Around:环绕通知,在目标方法执行前后分别调用一次切面的功能;
2.表示切面位置的切入点的表达式:
execution(访问修饰符返回值包名 类名 方法名称(方法的参数)异常)
2.实战举例
1.由于这个项目是搭建在Maven上的,先上pom.xml文件
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--aspectj依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
这是在Spring框架上实现AOP用的aspectj依赖
2.创建目标类:接口和他的实现类,要做的是给类中的方法增加功能,我这里比较简单就是做的一个Student实体类
import org.springframework.stereotype.Component;
@Component
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void doSome(){
System.out.println("执行doSome!!");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3.切面类:普通类
1)在类的上面加入@Aspect
2)在类中定义方法,方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Aspect: 是aspectj框架中的注解
* 作用:表示当前类是切面类
* 切面类:用给业务方法增加功能的类,在这个类中有切面的功能代码
*/
@Aspect
@Component
public class MyAspect {
@Before(value = "execution(* com.wenhua.ba03.Student.*(..))")
public void myBefore(){
System.out.println("前置通知:在目标方法之后执行的");
}
@After(value = "execution(* com.wenhua.ba03.Student.*(..))")
public void myAfter(){
System.out.println("后置通知:在目标方法之后执行的");
}
@AfterReturning(value = "execution(* com.wenhua.ba03.Student.*(..))")
public void myAfterReturning(){
//Object res:是目标方法执行后的返回值,根据返回值做你的切面处理
System.out.println("最终通知:在目标方法返回之前执行");
}
@Around(value = "execution(* com.wenhua.ba03.Student.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("执行之前");
joinPoint.proceed();
System.out.println("执行之后");
}
}
4.创建spring的配置文件:声明对象,把对象交给容器统一管理声明对象可以使用注解,或者xml配置文件<bean>
1)声明目标对象<bean>
2)声明切面对象<bean>
3)声明aspectj框架中的自动代理生成器标签。<aop:aspectj-autoproxy />
自动代理生成器:用来完成代理对象的自动创建功能的。
由于我这里使用的注解创建对象所以需要加入<context:component-scan base-package=“com.wenhua.ba03”/>,而不需要使用<bean>标签来创建对象
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--声明组件扫描器(context:component-scan)组件就是Java对象
base-package:指定注解在你的项目中的包名。
component-scan工作方式:spring会去扫描遍历base-package指定的包,
把包中和子包中的所有类,找到类中的注解,按照注解功能创建对象,或给属性赋值。
-->
<context:component-scan base-package="com.wenhua.ba03"/>
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
创建代理对象实在内存中实现的,修改目标对象的内存中结构。创建为代理
所以目标对象就是被修改后的代理对象
aspectj-autoproxy:会吧把spring容器中的所有的目标对象,一次性都生成代理对象
-->
<aop:aspectj-autoproxy/>
</beans>
5.创建测试类,从spring容器中获取目标对象(实际就是代理对象)通过执行代理方法,实现aop的功能增强。
public class MyTest03 {
@Test
public void test01(){
String config = "ba03/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
Student student = applicationContext.getBean(Student.class);
student.doSome();
}
}
输出: