一、AOP概念

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码.

Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。

二、AOP中常用的术语

  1. 连接点(Joinpoint): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,也可以理解连接点为:目标类上哪些有可能被增强的方法。
  2. 切点(Pointcut):可以理解为查询条件。一个target(目标类)的所有方法都是连接点,切点可以通过查询条件定位特定的连接点。
  3. 增强(Advice):织入目标类连接点上的一段程序代码。既包含连接点上的执行逻辑(横切逻辑、增强逻辑)又包含定位连接点的方位信息,before、after、around等。增强默认织入目标类的所有方法中。
  4. 目标对象(Target):增强逻辑织入的目标类。
  5. 代理(Proxy):一个类被AOP植入增强后,被产生一个结果代理类。
  6. 织入(Weaving):将通知(增强)应用到连接点上,生成代理的过程。
  7. 切面(Aspect):由切点和增强组成。
  8. 引介(Introduction):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
Spring AOP通过Pointcut指定在哪些类的以及方法上织入横切逻辑,通过Advice描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)。
此外,spring通过Advisor将advice和pointcut组装起来。也可以通过AspectJ描述切面信息,如下图所示

三、常见增强类型

前置增强
       BeforeAdvice  代表前置增强,因为spring只支持方法级的增强,所以MethodBeforeAdvice 是目前可用前置增强,表示在目标方法执行前实施增强。
后置增强
          AfterAdvice 代表后增强,表示目标方法在执行后实施增强 
环绕增强
          MethodInterceptor 代表环绕增强,表示目标方法执行前后实施增强
异常抛出增强
          ThrowsAdvice 代表抛出异常增强,表示目标方法抛出异常后实施增强
引介增强
          IntroductionInterceptor 代表引介增强,表示在目标类中添加一些新的方法和属性

四、支撑Spring AOP的底层Java技术:动态代理(JDK,CGLIB)

概念:
当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象。按照代理对象的创建时期不同,可以分为两种:
静态代理:程序员事先写好代理对象类,在程序发布前就已经存在了;(继承了接口,重新实现新的类xxxproxy)
动态代理:应用程序发布后,通过动态创建代理对象。
其中动态代理又可分为:JDK/Cglib 动态代理。

静态代理、动态代理等概念参考:https://www.cnblogs.com/boboxing/p/8126046.html

4.1 JDK动态代理

此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。

代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。

JDK动态代理只能针对实现了接口的类生成代理。

代码实例:
UserService.java:

public interface UserService {
public void save(); public void update(); public void delete(); public void find();
}

UserServiceImpl.java:

public class UserServiceImpl implements UserService {

    @Override
public void save() {
System.out.println("保存用户...");
} @Override
public void update() {
System.out.println("修改用户...");
} @Override
public void delete() {
System.out.println("删除用户...");
} @Override
public void find() {
System.out.println("查询用户...");
} }

MyJdbProxy.java:

/**
* 使用JDK的动态代理实现代理机制
*
*/
public class MyJdbProxy implements InvocationHandler{ private UserService userService; public MyJdbProxy(UserService userService){
this.userService = userService;
} public UserService createProxy(){
// 生成UserSErvice的代理:
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), userService.getClass()
.getInterfaces(), this);
return userServiceProxy;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 判断是否是save方法:
if("save".equals(method.getName())){
// 增强:
System.out.println("权限校验===========");
return method.invoke(userService, args);
}
return method.invoke(userService, args);
} }

SpringDemo.java 测试类:

public class SpringDemo1 {

    @Test
// 没有代理的时候的调用方式
public void demo1() {
// 创建目标对象
UserService userService = new UserServiceImpl(); userService.save();
userService.update();
userService.delete();
userService.find();
} @Test
// 使用代理
public void demo2() {
// 创建目标对象
UserService userService = new UserServiceImpl();
UserService proxy = new MyJdbProxy(userService).createProxy(); proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}

4.2 CGLib动态代理

CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所以弗雷方法的调用并顺势织入横切逻辑。

如果目标对象没有实现接口,则默认会采用CGLIB代理;

如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

代码实例:
CustomerService.java:

public class CustomerService {
public void save(){
System.out.println("保存客户...");
}
public void update(){
System.out.println("修改客户...");
}
public void delete(){
System.out.println("删除客户...");
}
public void find(){
System.out.println("查询客户...");
}
}

MyCglibProxy.java:

/**
* 使用Cglib产生代理
*
*/
public class MyCglibProxy implements MethodInterceptor{ private CustomerService customerService; public MyCglibProxy(CustomerService customerService){
this.customerService = customerService;
} public CustomerService createProxy(){
// 创建核心类:
Enhancer enhancer = new Enhancer();
// 设置父类:
enhancer.setSuperclass(customerService.getClass());
// 设置回调:
enhancer.setCallback(this);
// 创建代理:
CustomerService customerServiceProxy = (CustomerService) enhancer.create();
return customerServiceProxy;
} @Override
public Object intercept(Object proxy, Method method, Object[] arg,
MethodProxy methodProxy) throws Throwable {
if("delete".equals(method.getName())){
Object obj = methodProxy.invokeSuper(proxy, arg);
System.out.println("日志记录==========");
return obj;
}
return methodProxy.invokeSuper(proxy, arg);
}
}

SpringDemo.java 测试类:

public class SpringDemo2 {

    @Test
public void demo1(){
CustomerService customerService = new CustomerService();
customerService.save();
customerService.update();
customerService.delete();
customerService.find();
} @Test
public void demo2(){
CustomerService customerService = new CustomerService();
// 产生代理:
CustomerService proxy = new MyCglibProxy(customerService).createProxy();
proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}

五、Spring的传统的AOP:基于ProxyFactoryBean的方式的代理与BeanPostProcesser自动代理

5.1 使用ProxyFactoryBean配置代理

需要描述Advice、target、以及代理类的信息(增强、target-ref)

根据Advice代理,采用了JDK动态代理(对接口代理)

<bean id="greetingAdvice" class="com.smart.advice.GreetingBeforeAdvice" />
<bean id="target" class="com.smart.advice.NaiveWaiter" />
<bean id="waiter"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.smart.advice.Waiter"
p:target-ref="target"
p:interceptorNames="greetingAdvice"
/>
根据Advisor代理,proxyTargetClass设置为true,采用了CGLib动态代理技术(创建子类来代理target对象)
<bean id="waiterTarget" class="com.smart.advisor.Waiter" />
<bean id="sellerTarget" class="com.smart.advisor.Seller" />
<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
<!-- 正则表达式方法名匹配切面 -->
<bean id="regexpAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="greetingAdvice">
<property name="patterns">
<list>
<value>.*greet.*</value>
</list>
</property>
</bean>
<bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="regexpAdvisor" p:target-ref="waiterTarget"
p:proxyTargetClass="true" />

基于ProxyFactoryBean的方式生成代理的过程中不是特别理想:

* 配置繁琐,不利为维护.

* 需要为每个需要增强的类都配置一个ProxyFactoryBean.

5.2 使用BeanPostProcessor自动创建代理

自动代理基于BeanPostProcessor完成的代理.

* 在类的生成过程中就已经是代理对象.

基于ProxyFactoryBean方式代理:

* 先有目标对象,根据目标对象产生代理.

可通过Advisor定义的切面信息或AspectJ定义的切面信息自动生成代理。不用逐个定义target类,它会将容器中的Advisor自动织入匹配的目标类中。
通过Advisor定义的切面自动生成代理 (在xml文件中定义Advisior):
<bean id="waiter" class="com.smart.advisor.Waiter" />
<bean id="seller" class="com.smart.advisor.Seller" />
<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
<bean id="regexpAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:patterns=".*greet.*" p:advice-ref="greetingAdvice" />
<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" p:proxyTargetClass="true" />

六、AspectJ概述

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

Spring为了简化自身的AOP的开发,将AspectJ拿过来作为Spring自身一个AOP的开发.

AspectJ支持的增强类型在第三部分中已经提到。AspectJ切点表达式函数为该框架的核心部分,在这就不对语法进行详细说明,需要使用的时候现查即可。

如上图所示,首先在一个类上面声明@Aspect 通过该注解将类标识为一个切面类。

@Before部分是增的强类型。后面execution是目标切点表达式。beforeGreeting是增强所使用的横切逻辑函数。

通过AspectJ定义的切面自动生成代理 (在java文件中定义AspectJ)所以说AspectJ是语言级别的AOP实现。
package com.smart.aspectj.example;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class PreGreetingAspect{
@Before("execution(* greetTo(..))")
public void beforeGreeting(){
System.out.println("How are you");
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<aop:aspectj-autoproxy/>
<!--bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/-->
<bean id="waiter" class="com.smart.NaiveWaiter" />
<bean class="com.smart.aspectj.example.PreGreetingAspect" />
</beans>

七、在Spring Boot中使用AOP

在spring boot中使用aop就不用再xml文件中配置了代理bean等信息了,在pom.xml添加好aop的依赖。直接在切面类前声明Configuration或Component如下图:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration; @Configuration
//证明是一个切面
@Aspect
public class ControllerAOP {
//环绕aop
//execution表达式 此表达式表示扫描controller下所有类的所有方法都执行此aop
@Around("execution (* com.beyondli.controller..*.*(..))")
public Object testAop(ProceedingJoinPoint pro) throws Throwable {
//获取request请求提(需要时备用)
//HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//进入方法前执行的代码
System.out.println("beginAround");
//执行调用的方法
Object proceed = pro.proceed();
//方法执行完成后执行的方法
System.out.println("endAround");
return proceed;
} }

由AspectJ定义的切面会自动扫描满足切点表达式的目标类并代理。

参考:

1:https://www.cnblogs.com/boboxing/p/8126046.html

2:https://blog.csdn.net/hehexiaoyou/article/details/23430357

3:https://baike.baidu.com/item/Aspectj/4830848

Spring AOP 整理笔记的更多相关文章

  1. Spring AOP整理

    示例展示 AOP(Aspect Oriented Programming),是面向切面编程的技术.AOP基于IoC基础,是对OOP的有益补充.AOP之所以能得到广泛认可,主要是因为它将应用系统拆分分了 ...

  2. Spring AOP学习笔记

      Spring提供了一站式解决方案:          1) Spring Core  spring的核心功能: IOC容器, 解决对象创建及依赖关系          2) Spring Web ...

  3. Spring AOP学习笔记(1)-概念

    1.Aspect 横切在多个类的一个关注点,在Spring AOP中,aspect实现是一个规则的类或@Aspect标注的规则类.例如:事务管理 2.Join point 程序执行过程中的一个点,例如 ...

  4. Spring AOP学习笔记01:AOP概述

    1. AOP概述 软件开发一直在寻求更加高效.更易维护甚至更易扩展的方式.为了提高开发效率,我们对开发使用的语言进行抽象,走过了从汇编时代到现在各种高级语言繁盛之时期:为了便于维护和扩展,我们对某些相 ...

  5. Spring AOP学习笔记02:如何开启AOP

    上文简要总结了一些AOP的基本概念,并在此基础上叙述了Spring AOP的基本原理,并且辅以一个简单例子帮助理解.从本文开始,我们要开始深入到源码层面来一探Spring AOP魔法的原理了. 要使用 ...

  6. Spring AOP学习笔记03:AOP的核心实现之获取增强器

    上文讲了spring是如何开启AOP的,简单点说就是将AnnotationAwareAspectJAutoProxyCreator这个类注册到容器中,因为这个类最终实现了BeanPostProcess ...

  7. Spring AOP学习笔记04:AOP核心实现之创建代理

    上文中,我们分析了对所有增强器的获取以及获取匹配的增强器,在本文中我们就来分析一下Spring AOP中另一部分核心逻辑--代理的创建.这部分逻辑的入口是在wrapIfNecessary()方法中紧接 ...

  8. Spring AOP学习笔记05:AOP失效的罪因

    前面的文章中我们介绍了Spring AOP的简单使用,并从源码的角度学习了其底层的实现原理,有了这些基础之后,本文来讨论一下Spring AOP失效的问题,这个问题可能我们在平时工作中或多或少也会碰到 ...

  9. Spring AOP 整理

    在 xml中加 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.o ...

随机推荐

  1. WebApi 之HelpPage帮助页

    1.创建解决方案 2.选择类型-Web API 3.设置项目生成XML路径 同时修改HelpPageConfig,代码调用XML文件位置 3.编写WebApi接口代码 4.启动项目 查看接口 5.测试 ...

  2. C# 替换Word文本—— 用文档、图片、表格替换文本

    编辑文档时,对一些需要修改的字符或段落可以通过查找替换的方式,快速地更改.在C# 在word中查找及替换文本一文中,主要介绍了在Word中以文本替换文本的方法,在本篇文章中,将介绍如何用一篇Word文 ...

  3. Android6.0 源码修改之Settings音量调节界面增加通话音量调节

    前言 今天客户提了个需求,因为我们的设备在正常情况下无法调节通话音量,只有在打电话过程中,按物理音量加减键才能出现调节通话音量seekBar,很不方便,于是乎需求就来了.需要优化两个地方 1.在正常情 ...

  4. GitLab11.3.9 使用 Crowd3.3.2 的帐号实现 SSO 单点登录,以及GitLab配置腾讯企业邮箱

    GitLab11.3.9 的安装方法: 点击查看.   Crowd3.3.2 的安装方法:点击查看.   需要先在 Crowd 创建应用程序,参考 <Docker 创建 Crowd3.3.2 以 ...

  5. Centos7搭建虚拟用户FTP

    yum install -y vsftpd #安装ftp服务 useradd -s /sbin/nologin virftp #创建用户,用于ftp服务 vim /etc/vsftpd/vsftpd_ ...

  6. SQL Server获取连续区间的日期

    个人理解的方法有三种 通过系统表master..spt_values获取 用WHILE循环获取 游标获取 CET递归(感谢评论区博友) 方法一:通过系统表master..spt_values获取 1. ...

  7. Windows Server 2016-DNS 新增或改进功能

    本章节补充介绍在 Windows Server 2016 中域名系统 (DNS) 服务器新增或已更改的功能相关信息,具体内容如下: 功能 新增或改进 描述 DNS 策略 新增 您可以配置 DNS 策略 ...

  8. python3 树莓派 + usb摄像头 做颜色识别 二维码识别

    今天又啥也没干 我完蛋了哦  就是没办法沉下心来,咋办....还是先来条NLP吧.. 七,凡事必有至少三个解决方法 对事情只有一个方法的人,必陷入困境,因为别无选择. 对事情有两个方法的人也陷入困境, ...

  9. 使用FakeAPP进行AI换脸必看!!

    C盘生于容量小于5G的千万别用啊!!笔者本人因为C盘只剩了3G,根本用不上这个,最后会把大小为4G的core文件必须移植到C盘当中,俺的CUDA也白安装了,而且还不小心安装成CUDA8了,应该用9好么 ...

  10. ThinkPHP5.1 + tufanbarisyildirim 解析apk

    摘要 对于apk,我可以说只会安装,并不知道其中有什么内容需要记录下来.这次公司做一个关于电视机顶盒的项目.对于这个陌生的项目,刚开始真是一脸懵逼,完全不知道如何下手. 因为这类的项目完全没有接触过, ...