【框架】[Spring]纯Java的方式实现AOP切面(拦截)技术

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【框架】[Spring]纯Java的方式实现AOP切面(拦截)技术

AOP理论知识介绍:

面向切面编程英文名为:Aspect Oriented Programming。

是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

其实我们把它理解成动态代理就好理解了!!!

下面是一些理论知识:

AOP可以说是OOP(面向对象编程)的补充和完善。

在OOP设计中有可能导致代码的重复不利于模块的重用性,例如日志功能。

日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能关系不大。

但是在OOP中这些业务要和核心业务代码在代码这一级集成。

还有些如安全性、事务等也是如此。

能不能把这些与核心业务无关但系统中需要使用的业务(称为切面)单独编写成一个模块,在主要核心业务代码中不调用,而是在配置文件中做些配置,配置核心业务需要使用到得切面部分,在系统编译时才织入到业务模块中。

切面(Aspect):

简单的理解就是把那些与核心业务无关的代码提取出来,进行封装成一个或几个模块用来处理那些附加的功能代码。

(如日志,事务,安全验证)我们把这个模块的作用理解为一个切面。

其实切面就是我们写一个类,这个类中的代码原来是在业务模块中完成的,现在单独成一个或几个类。在业务模块需要的时候才织入。

织入(Weaving):

把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时,类加载时和运行时完成。Spring和其它纯Java AOP框架一样,在运行时完成织入。

切入点(Pointcut):

也就是切点。

本质上是一个捕获连接点的结构。在AOP中,可以定义一个pointcut,来捕获相关方法的调用

通知(Advice):

在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

我的理解:

在本篇博客,不直接讲解用Spring的xml文件配置实现AOP,而是用纯Java的方式来实现AOP切面拦截。

既然AOP技术其实是动态代理的加强,你会发现这个功能很强大的。

通过用纯Java的方式写出实现AOP之后,你会发现后面用xml配置实现AOP,其实只是换了一种方式而已,本质上是一样的。

实例:

准备必要的Jar包:

首先还是,准备好包:

下载链接:

http://repo.springsource.org/libs-release-local/org/springframework/spring/

image.png

还需要准备一个 org/apache/commons/logging 包-Spring依赖的记录日志的包。

下载链接:

http://commons.apache.org/proper/commons-logging/download_logging.cgi

既然是用纯Java实现AOP,所以不需要用到XMl,所以也不要配置文件了。

Person.java

package cn.hncu.javaImpl;
public class Person {
    public void run(){
        System.out.println("我在run...");
    }
    public void run(int i){
        System.out.println("我在run"+i+"...");
    }
    public void say(){
        System.out.println("我在say...");
    }
}

AopJavaImplDemo

package cn.hncu.javaImpl;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
/**
 * 纯Java的方式实现切面(拦截)技术
 * @author 陈浩翔
 * 2016-9-1
 */
public class AopJavaImplDemo {
    //如果你对动态代理有过了解了,对下面的代码会很好理解的
    @Test
    public void demo1(){
        //准备好需要被代理的原型对象
        Person p = new Person();
        ProxyFactory factory = new ProxyFactory();//ProxyFactoryBean的功能比ProxyFactory强
        factory.setTarget(p);//给代理工厂一个原型对象
        //构造切面
        //切面=切点 + 通知
        //切点
        JdkRegexpMethodPointcut cut = new JdkRegexpMethodPointcut();
        //cut.setPattern("cn.hncu.javaImpl.Person.run");//可以直接给方法全名
        //或者给正则表达式
        cut.setPattern(".*run.*");//.号匹配除"\r\n"之外的任何单个字符。*号代表零次或多次匹配前面的字符或子表达式
        //通知
        Advice advice = new MethodInterceptor() {
            //哈哈,看到这个是不是和动态代理中的那个方法很像
            @Override
            public Object invoke(MethodInvocation methodInv) throws Throwable {
                System.out.println("前面拦拦...");
                Object resObj = methodInv.proceed();//放行
                System.out.println("后面拦拦...");
                return resObj;
            }
        };
        //切面 = 切点 + 通知
        Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
        factory.addAdvisor(advisor);//给代理工厂一个切面
        Person p2 = (Person) factory.getProxy();//从代理工厂中获取一个代理后的对象
        p2.run();
        p2.say();//不会拦
        p2.run(3333);
    }
}

演示结果:

image.png

Spring AOP 中 advice 的类型演示

通知的类型:

前置通知(Before advice):

在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

返回后通知(After returning advice):

在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

环绕通知(Around Advice):

包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

在这里,只演示这3中通知。

@Test
    public void demo2(){
        ProxyFactoryBean factory = new ProxyFactoryBean();
        factory.setTarget(new Person());//给代理工厂一个原型对象
        //切面 = 切点 + 通知
        //切点
        JdkRegexpMethodPointcut cut = new JdkRegexpMethodPointcut();
        cut.setPatterns(new String[]{".*run.*",".*say.*"});//可以配置多个正则表达式
        //通知 前切面---不需要放行,原方法也能执行
        Advice before = new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object obj)
                    throws Throwable {
                System.out.println("before...拦截");
            }
        };
        Advice after = new AfterReturningAdvice() {
            @Override
            public void afterReturning(Object returnValue, Method method,
                    Object[] args, Object target) throws Throwable {
                System.out.println("afterReturning...拦截");
            }
        };
//      Advice throwsAdvice = new A();
//      Advice afterAdvice = new AfterAdvice() {
//      }; 
        Advice around = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("MethodInterceptor...前面拦截");
                Object returnValue = invocation.proceed();//放行
                System.out.println("MethodInterceptor...后面拦截");
                return returnValue;
            }
        };
        //切面 = 切点 + 通知
        Advisor beforeAdvisor = new DefaultPointcutAdvisor(cut, before);
        Advisor afterAdvisor = new DefaultPointcutAdvisor(cut,after);
        Advisor aroundAdvisor = new DefaultPointcutAdvisor(cut, around);
//      Advisor throwsAdviceAdvisor = new DefaultPointcutAdvisor(cut, throwsAdvice);
        // 给代理工厂一个切面 ---注意,添加的顺序的拦截动作执行的顺序是有关系的!!!
        factory.addAdvisors(beforeAdvisor,afterAdvisor,aroundAdvisor);
        Person p2 = (Person) factory.getObject();//从代理工厂中获取一个代理后的对象
        p2.run(2222);
    }

演示结果:

给代理工厂切面的顺序

使用factory.addAdvisors(beforeAdvisor,afterAdvisor,aroundAdvisor);

image.png

使用factory.addAdvisors(beforeAdvisor,aroundAdvisor,afterAdvisor);

image.png

使用factory.addAdvisors(aroundAdvisor,beforeAdvisor,afterAdvisor);

image.png

其实写了这么多,都还没有用到Spring真正的AOP使用方法,这个只是让我们接下来学习更加简单!用配置文件new的对象,只是换种方法罢了。

记住3步:

1、需要一个代理工厂!

2、切面=切点(即我们需要拦截哪里)+通知(拦截后怎么做)

3、把切面加入代理工厂

就OK!

在下一篇博客,我会继续写AOP切面技术的。(用Spring配置xml的方法)

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
23天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
23天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
23天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
23天前
|
缓存 Java 数据库
【Java面试题汇总】Spring篇(2023版)
IoC、DI、aop、事务、为什么不建议@Transactional、事务传播级别、@Autowired和@Resource注解的区别、BeanFactory和FactoryBean的区别、Bean的作用域,以及默认的作用域、Bean的生命周期、循环依赖、三级缓存、
【Java面试题汇总】Spring篇(2023版)
|
2天前
|
存储 算法 安全
深入理解Java中的集合框架
【9月更文挑战第34天】本文将带你走进Java的集合框架,探索其背后的设计哲学和实现细节。我们将从集合的基本概念出发,逐步深入到具体的接口和类的实现,最后通过一个实际的例子来展示如何在Java程序中高效地使用集合。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深度理解。
9 1
|
13天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
572 6
|
9天前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
15 1
|
10天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
11天前
|
前端开发 Java Spring
关于spring mvc 的 addPathPatterns 拦截配置常见问题
关于spring mvc 的 addPathPatterns 拦截配置常见问题
|
12天前
|
算法 Java
Java项目不使用框架如何实现限流?
Java项目不使用框架如何实现限流?
20 2
下一篇
无影云桌面