一、AOP介绍:
1、背景:
在面向对象编程的系统中,会使用对象来封装一些对象。并通过对象的协作进行完成系统的功能。而这些操作都是通过“复制黏贴式”的开发。面向对象在处理这样的业务时由于其封装性导致代码并无法被公用。需要有一种技术来实现方法的扩展(功能增强)。AOP就正是这样的一种技术,它能够配合OOP进行这些通用功能的分离和织入,对业务代码不会造成污染。
2、概念:
AOP Aspect-Oriented Programming 面向切面编程。它是一种编程思想能使软件简单,容易维护,更加容易被复用的一种编程方式;AOP只是OOP编程方式的一种补充,并非什么新的技术。
3、过程解析:
AOP编程方式解决问题的关键在于对关注点的抽象。对系统中的一些分散在多个不相关业务模块中的共同问题(关注点),将这些关注点从各个业务模块中抽象出来是AOP编程的精髓。如日志记录功能,事务管理功能,方法的性能分析等功能从系统业务模块的分离。
对于OOP和AOP的的主要开发流程做一个对比:A B C
OOP 编程基本流程
1)归纳分析系统,抽象领域模型;
2)使用class 来封装领域模型,实现类的功能;
3)把各个类相互协作,组装形成一个完整的功能。
AOP 编程基本流程
1)归纳分析系统中的关注点;
2)分离方面对象,使用OOP 将这些方面封装成一个公用的类;
3)织入到业务功能,按配置织入规则将切面对象(织入或集成)到系统功能中,形成一个增强后的功能。
4、使用场景
用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。
二、名词
AOP中的一些关键的专业术语:
1、关注点:
就是系统中要解决的问题。比如在电子商务系统中,安全验证、日志记录等都属于关注点(Core Concerns);
2、切面(Aspect):
用来组织多个Advice,负责管理切入点和增强的关系;
3、增强(Advice
3.1、定义:
定义何时在切入点执行“织入关注点”;如,在执行方法前执行before,在执行方法后执行after等等。
3.2、Advice的类型:
(1)前置通知(Before Advice):在连接点(Join point)之前执行的通知。
(2)后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
(3)环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
(4)返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)
(5)抛出异常后通知(AfterThrowing advice):在方法抛出异常退出时执行的通知
3.3、Advice的执行顺序:
(1)没有异常情况下的执行顺序:
around before advice
before advice
target method 执行
after advice
around after advice
afterReturning advice
(2)出现异常情况下的执行顺序:
around before advice
before advice
target method 执行
after advice
around after advice
afterThrowing advice
java.lang.RuntimeException:异常发生
4、连接点(Joinpoint)
明确的某一个被织入代码的点,如方法调用,异常抛出,方法返回等等时间点;
5、切入点(cutpoint)
可以织入增强方法的连接点。当连接点满足被增强的要求时,会在该连接点添加增强,此时的连接点就成为切入点。
6、织入(weaving)
织入是指把解决横切问题的切面模板,与系统中的其它核心模块通过一定策略或规则组合到一起的过程;具体的技术方案有:a.动态代理;b.类加载器织入c.编译器织入:AspectJ主要就是是使用这种织入方式
三、AOP的原理
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
1、静态代理:
AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
2、动态代理
Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理,,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
2.1、JDK动态代理
(基于接口的动态代理技术,使用Proxy类(java.lang.reflect.Proxy ))调用newProxyInstance方法
static | 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。 |
参数:①目标对象类加载器、②目标对象相同的接口字节码对象数组、③实现这个接口InvocationHandler,创建代理对象,写增强方法。
JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理对象; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
2.2、 cglib动态代理(基于父类的动态代理技术)
如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
//目标对象
final Target target=new Target();
//返回值就是动态生成的代理对象 基于cglib
//1 创建增强器
Enhancer enhancer=new Enhancer();
//2 设置父类(目标)
enhancer.setSuperclass(Target.class);
//3 设置回调
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前的方法");//执行前置
Object invoke = method.invoke(target, args);//执行目标
System.out.println("执行后的方法");//执行后置
return invoke;
}
});
//4 创建代理对象
Target proxy = (Target) enhancer.create();
3、AOP指定使用cglib
在使用AOP进行切面编程的时候,我们底层会自动调用JDK动态代理的实现模式,根据接口进行编程,如果想要AOP底层调用CGLIB动态代理的模式,那么需要在核心配置文件xml中写下以下:
<aop:scoped-proxy proxy-target-class="false"></aop:scoped-proxy>
这个标签可以把AOP的底层的动态代理改成CGLIB
当然如果在实现切面编程的时候,指定的不是接口.calss,使用的是实现类,那么底层将会默认调用CGLIB。
注意事项:如果系统调用CGLIB动态代理的时候,一定要把实现类的实现接口语句去掉,否则他会报错
4、静态代理与动态代理比较
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
四、Spring AOP:
1、Spring对AOP的支持:
Spring中AOP默认是使用动态代理的方式创建AOP代理的。Spring的AOP代理是由IOC容器负责创建和管理,这样的好处是可以使用容器中的任何一个bean作为“方面”目标。Spring的AOP的设计目标是将AOP的实现和IOC容器进行整合,帮助解决在企业开发中遇到的此类问题。Spring中的AOP也支持AspectJ(面向方面的真正始祖级框架)。
在Spring的AOP开发中,需要做以下三件事情:
(1)定义普通的业务类
(2)定义切入点,对多个关注点进行横切
(3)定义增强,在普通的业务功能中织入关注点的处理
2、Spring依然提供两种对AOP的配置:
(1)XML的方式
(2)使用注解进行标注的方式
五、使用
可以拦截自定义的class/method,也可以拦截系统的class/method,如
@Aspect
@Component
@Slf4j
public class PreAuthenticateAspect {
@Pointcut("@annotation(org.springframework.security.access.prepost.PreAuthorize) || within(@org.springframework.security.access.prepost.PreAuthorize *)")
public void execute() {}
@Before("execute()")
public void doBeforeAuthCheck(JoinPoint joinPoint) throws Throwable {
//......拦截Spring security的@PreAuthorize注解鉴权,对需要鉴权的接口做一些统一处理,比如header处理等等
//如果当前登录用户是admin,.......
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
}
}