AOP(面向切面编程)可以看作对OOP(面向对象编程)的补充。
实际开发时,业务代码除了需要完成业务逻辑的处理,经常还伴随着一些其他的操纵,比如在进行业务处理前、后进行操作日志的记录等等,AOP的作用就是将除了业务逻辑之外的其他重复操作从业务代码中分离出来,让业务代码能专注于业务逻辑的处理。分离出来的这部分操作代码在业务代码运行时有需要的地方再动态地织入进去,让代码的可重用性变高,更简洁。
要了解AOP,最快的办法就是直接从AOP的专业术语入手,理解清楚了AOP的术语,也就能理解AOP。
其实别人写的已经很好了,只是自己为了再回顾复习又写一遍。
详细介绍:
https://zhuanlan.zhihu.com/p/25522841
AOP术语介绍:
https://blog.csdn.net/changudeng1992/article/details/80625134
以下就是AOP的专业术语:
-
通知(Advice)
就是你想要执行的方法,比如刚才提到的把日志记录的方法分离出来,但我们仍然希望在业务操作的时候执行日志记录,这个日志记录就是通知,实际上就是把这个通知推给一个目标类(或该类的方法)。 -
连接点(Joinpoint)
就是通知执行的地方。比如这个日志记录的操作是执行在存数据库的业务方法还是执行在取数据的业务方法那里。 -
切入点(Pointcut)
连接点的集合,即通常通知都不会只织入到一个地方,每个需要织入该类型通知的地方的集合就构成了切入点。 -
切面(Aspect)
切入点和通知结合起来就成了切面。
介绍到这里,就已经有了一个AOP的轮廓:
如图所示,在开发过程中,使用AOP我们可以把精力放在关注业务逻辑上(编写Service)代码,而不用在业务代码中考虑哪里插入安全、事务、日志等操作,怎么编写这些操作具体的代码。我们可以将具体的代码在执行时动态地切入进去,节省了很多精力。
-
引入(introduction)
可以为现有的类动态添加属性和方法。 -
目标(Target)
需要引入的类。 -
代理(Proxy)
Aop的实现机制,通过代理类来伪装目标类,骗过JVM的强类型检查。 -
织入(Weaving)
切面应用到目标对象的过程。
以上就是AOP的基本概念,接下来以一个简单的例子来实现AOP。
这个例子有三个类,一个动物管理员类(ZooManager),一个动物类(Animal),一个测试类。其中动物类有两个方法show()和hungry(),分别代表进行了一次马戏表演和饥饿状态,我要实现的就是当动物执行这两个方法时,动物管理员自动地进行喂养(执行Feed())方法。
有一个需要注意的地方,如果有aspectJ相关的报错,只需要在项目的pom.xml文件里添加依赖:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.example.myaop"></context:component-scan>
<!--aop的配置-->
<aop:config>
<!--配置切面-->
<aop:aspect id="myaspect" ref="zm">
<!--配置切入点-->
<aop:pointcut expression="execution(* com.example.myaop.Animal.show()) or execution(* com.example.myaop.Animal.hungry())" id="feedService"/>
<!--配置通知的时间为执行后通知-->
<aop:after method="feed" pointcut-ref="feedService"/>
</aop:aspect>
</aop:config>
</beans>
package com.example.myaop;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//注解配置注册Bean
@Component("animal")
public class Animal {
//为name属性设置初始化值"hakuzouzi"
@Value("${name:hakuzouzi}")
private String name;
private int age;
//进行一次马戏表演
public void show() {
System.out.println(name+" just played a show.");
}
//进入饥饿状态
public void hungry() {
System.out.println(name+" say: I'm hungry.");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.example.myaop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//注解配置注册Bean
@Component("zm")
public class ZooManager {
//自动装配属性值
@Autowired
private Animal animal;
//喂养
public void feed() {
System.out.println(animal.getName()+" has been fed.");
}
}
package com.example.myaop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testaop {
public static void main(String[] args) {
//获取上下文容器
ApplicationContext context = new ClassPathXmlApplicationContext("tryaop.xml");
//获得Bean
Animal animal = (Animal)context.getBean("animal");
//动态执行show和hungry方法
animal.show();
animal.hungry();
}
}
运行结果如图所示,可以看到程序 “自动”地在show()方法和hungry()方法后执行了ZooManager类的feed()方法。在实际场景中,show()方法和hungry()方法就可以换作我们的业务逻辑,而feed()方法可以换作日志记录,事务管理等操作。