认识一下AOP
学习Spring的时候,我们都知道其两大特性一个是IOC,一个是AOP,中文名称分别为控制反转和面向切面。控制反转的含义我们大致能理解,面向切面比较抽象,只听说过面向对象OOP,AOP的作用是什么呢?
引文1和2 3进行了详细的理论和实践解释,本文主要通过引文中的内容,完成从项目创建到AOP实现测试整个过程,可参看参考文献后,再具体的查看本文的完整实现代码。
构建Springboot项目工程
进入https://start.spring.io/,选择项目所需的jar,主要选择Aspects,官方给出的解释是Create your own Aspects using Spring Aop and AspectJ.可以使用Spring AOP或者 AspectJ创建我们自己的切面程序。
然后打包下载。
下载完成后的项目直接用IDEA打开即可。
编写基础功能代码
由于是测试,不再书写DAO,只编写Service,将Spring变成Java命令行程序,然后直观的记录AOP的使用过程。
假设整个系统学生管理系统,我们编写提供学生信息查询的方法。
学生类:
package zmqc.iceyung.bean;
public class Student {
private int id;
private int age;
private String name;
private String sex;
public Student(){}
public Student(int id,int age,String name,String sex){
this.id = id;
this.age = age;
this.name = name;
this.sex = sex;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
service:
//************************************************service接口
package zmqc.iceyung.service;
import zmqc.iceyung.bean.Student;
import java.util.List;
public interface IStudentService {
List<Student> getAllStudents();
}
//************************************************service实现类
package zmqc.iceyung.service;
import org.springframework.stereotype.Service;
import zmqc.iceyung.annotation.MyAop;
import zmqc.iceyung.bean.Student;
import java.util.ArrayList;
import java.util.List;
@Service
public class StudentService implements IStudentService{
@Override
public List<Student> getAllStudents() {
Student stu1 = new Student(1,20,"小明","男");
Student stu2 = new Student(1,18,"小红","女");
List<Student> list = new ArrayList<>();
list.add(stu1);
list.add(stu2);
return list;
}
}
主类,实现CommandLineRunner接口,此时的Springboot就不再是web项目,而是java命令行项目:
package zmqc.iceyung;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import zmqc.iceyung.service.IStudentService;
@EnableAspectJAutoProxy
@SpringBootApplication
public class IceyungApplication implements CommandLineRunner{
@Autowired
private IStudentService studentService;
@Override
public void run(String... args) throws Exception {
System.out.println(studentService.getAllStudents());
}
public static void main(String[] args) {
SpringApplication.run(IceyungApplication.class, args);
}
}
运行时,显示:
[Student{id=1, age=20, name=‘小明’, sex=‘男’}, Student{id=1, age=18, name=‘小红’, sex=‘女’}]
添加自定义注解,并编写自己的AOP程序
编写自己的AOP程序,AOP程序的拦截是基于切点的,即下面的@Pointcut("@within(zmqc.iceyung.annotation.MyAop)"),该部分为切入点表达式,关于Spring中实现AOP的方式,和切点表达式可以参看引文1和2 4
具体实现如下:
package zmqc.iceyung.aspects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
//切面
@Component
@Aspect
public class MyAop1 implements Ordered {
//切点,内部为切入点表达式
@Pointcut("@within(zmqc.iceyung.annotation.MyAop)")
private void pointCutMethod() {
}
//声明前置通知
@Before("pointCutMethod()")
public void doBefore(JoinPoint point) {
System.out.println("doBefore");
return;
}
//声明后置通知
@AfterReturning(pointcut = "pointCutMethod()", returning = "returnValue")
public void doAfterReturning(JoinPoint point,Object returnValue) {
System.out.println("doAfterReturning");
}
//声明例外通知
@AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println("doAfterThrowing");
}
//声明最终通知
@After("pointCutMethod()")
public void doAfter() {
System.out.println("doAfter");
}
//声明环绕通知
@Around("pointCutMethod()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
Object obj = pjp.proceed();
System.out.println("doAround");
return obj;
}
@Override
public int getOrder() {
return 1;
}
}
上述切点表达式代表了该AOP程序需要拦截的是注解MyAop,所以需要我们自定义注解,可参见引文5
package zmqc.iceyung.annotation;
import java.lang.annotation.*;
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAop {
String value();
}
当添加完我们需要的AOP程序后,在service方法中添加注解@MyAop,如下:
@MyAop("获得全部的学生信息")
@Override
public List<Student> getAllStudents() {
Student stu1 = new Student(1,20,"小明","男");
Student stu2 = new Student(1,18,"小红","女");
List<Student> list = new ArrayList<>();
list.add(stu1);
list.add(stu2);
return list;
}
此时再运行程序,打印结果如下:
doBefore
doAround
doAfter
doAfterReturning
[Student{id=1, age=20, name='小明', sex='男'}, Student{id=1, age=18, name='小红', sex='女'}]
注意:若将@MyAop放在类上,则此时的切点表达式应该是@within,具体看参考引文4
总结什么是AOP
AOP并不是通过繁琐的重复编码来完成非核心的一类问题,而是采用代理等方式,在需要添加管理类时通过类代理的模式进行,让整个的实现过程更加的方便快捷。常见的有日志系统、权限系统、事务等。
项目在线地址:https://gitee.com/iceyung/SpringAOPDemo.git
SpringAOP的应用实例与总结,https://www.cnblogs.com/boywwj/p/7502185.html ↩︎ ↩︎
spring多个AOP执行先后顺序,https://blog.csdn.net/qqxhwwqwq/article/details/51678595 ↩︎ ↩︎
关于 Spring AOP (AspectJ), 你该知晓的一切https://blog.csdn.net/javazejian/article/details/56267036/ ↩︎
Spring AOP 所有切入点指示符详解(execution,within,this,target,args,@within,@target,@args,@annotation),https://blog.csdn.net/qq_23167527/article/details/78623639 ↩︎ ↩︎
springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)https://www.cnblogs.com/wenjunwei/p/9639909.html ↩︎