Spring AOP简介
AOP(Aspect Orient Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善。它以通过预编译方式和运行期动态代理方式,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
AOP与OOP字面意思相近,但其实两者完全是面向不同领域的设计思想。实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面的运行期代理方式,理解为一个动态过程,可以在对象运行时动态织入一些扩展功能或控制对象执行。
AOP 应用场景分析?
实际项目中通常会将系统分为两大部分,一部分是核心业务,一部分是非核业务。在编程实现时我们首先要完成的是核心业务的实现,非核心业务一般是通过特定方式切入到系统中,这种特定方式一般就是借助AOP进行实现。
AOP就是要基于OCP(开闭原则),在不改变原有系统核心业务代码的基础上动态添加一些扩展功能并可以"控制"对象的执行。例如AOP应用于项目中的日志处理,事务处理,权限处理,缓存处理等等。
Spring AOP 应用原理分析
Spring AOP底层基于代理机制(动态方式)实现功能扩展:假如目标对象(被代理对象)实现接口,则底层可以采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)。
假如目标对象(被代理对象)没有实现接口,则底层可以采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。
说明:Spring boot2.x 中AOP现在默认使用的CGLIB代理,假如需要使用JDK动态代理可以在配置文件(applicatiion.properties)中进行如下配置:#cglib aop proxy
#spring.aop.proxy-target-class=true
#jdk aop proxy
spring.aop.proxy-target-class=false
Spring 中AOP 相关术语分析切面(aspect): 横切面对象,一般为一个具体类对象(可以借助@Aspect声明)。
通知(Advice):在切面的某个特定连接点上执行的动作(扩展功能),例如around,before,after等。
连接点(joinpoint):程序执行过程中某个特定的点,一般指向被拦截到的目标方法。
切入点(pointcut):对多个连接点(Joinpoint)一种定义,一般可以理解为多个连接点的集合。
Spring AOP快速实践
项目创建及配置
第一步创建maven项目或在已有项目基础上添加AOP启动依赖:
org.springframework.boot
spring-boot-starter-aop
第二步定义业务层接口package com.cy.pj.common.service;
public interface MailService {
boolean sendMail(String msg);
}
第三步定义业务层实现类@Service
public class MailServiceImpl implements MailService{
@Override
public boolean sendMail(String msg) {//ocp(开闭原则-->对扩展开放,对修改关闭)
long t1=System.currentTimeMillis();
System.out.println("send->"+msg);
long t2=System.currentTimeMillis();
System.out.println("send time:"+(t2-t1));
return true;
}
}
我们自己计算了执行时间,但是违反了ocp原则,所以需要无侵入式扩展这个记录执行时间的功能。我们在这个类中添加内部类来实现两种方式的扩展。package com.cy.pj.common.service;
import org.springframework.stereotype.Service;
@Service
public class MailServiceImpl implements MailService{
@Override
public boolean sendMail(String msg) {//ocp(开闭原则-->对扩展开放,对修改关闭)
//long t1=System.currentTimeMillis();
System.out.println("send->"+msg);
//long t2=System.currentTimeMillis();
//System.out.println("send time:"+(t2-t1));
return true;
}
}
//下面的两种设计了解?(基于原生方式实现功能扩展)
//自己动手写子类重写父类方法进行功能扩展
class TimeMailServiceImpl extends MailServiceImpl{//这种写法的原型就是CGLIB代理机制的方式(继承)
@Override
public boolean sendMail(String msg) {
long t1=System.currentTimeMillis();
boolean flag=super.sendMail(msg);
long t2=System.currentTimeMillis();
System.out.println("send time:"+(t2-t1));
return flag;
}
}
//自己写兄弟类对目标对象(兄弟类)进行功能扩展,这种方式又叫组合
class TimeMailServiceImpl2 implements MailService{//这种写法的原型就是JDK代理机制的方式(实现)
private MailService mailService;
public TimeMailServiceImpl2(MailService mailService){
this.mailService=mailService;
}
@Override
public boolean sendMail(String msg) {
long t1=System.currentTimeMillis();
boolean flag=mailService.sendMail(msg);
long t2=System.currentTimeMillis();
System.out.println("send time:"+(t2-t1));
return flag;
}
}
下来通过aop方式完成业务package com.cy.pj.common.service.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect//告诉spring我是一个切面(封装了扩展逻辑对象),这样的对象中要包含两部分内容(1.切入点,2.扩展逻辑-advice)
@Component//表示在spring中做一个注册
public class SysLogAspect {
//定义切入点
//bean表达式为spring中的一种粗粒度切入点表达式(不能精确到具体方法)
//这里的mailServiceImpl名字为spring容器中一个bean对象的名字
@Pointcut("bean(mailServiceImpl)")//多个bean的定义形式(bean(*ServiceImpl))
public void doLogPointCut(){}//这个方法仅仅是承载切入点注解的一个载体,方法体内不需要写任何内容
/*按照Aspect规范定义一个@Around环绕通知*/
//@Around("bean(mailServiceImpl)")//直接在advice注解内部定义切入点表达式
//对于@Around注解描述的方法器规范要求
//1)返回值类型为Object(用于封装目标方法的执行结果)
//2)参数类型为ProceedingJoinPoint(用于封装执行的目标方法信息)
//3)抛出的异常Throwable(用于封装执行目标方法时抛出的异常)
//4)在@Around注解描述的方法内部,可以手动调用目标方法
@Around("doLogPointCut()")//也可以在advice注解内部通过方法引用引入切入点表达式
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
long t1=System.currentTimeMillis();
Object result = joinPoint.proceed();//表示调用目标方法
long t2=System.currentTimeMillis();
System.out.