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

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

转载请注明出处:http://blog.csdn.net/qq_26525215


本文源自大学之旅_谙忆的博客

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的方法)

本文章由[谙忆]编写, 所有权利保留。

转载请注明出处:http://blog.csdn.net/qq_26525215


本文源自大学之旅_谙忆的博客

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
3天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
4天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
7天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
37 1
|
11天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
36 2
|
12天前
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
47 2
|
13天前
|
JavaScript 前端开发 Java
【颠覆传统】Spring框架如何用WebSocket技术重塑实时通信格局?揭秘背后的故事与技术细节!
【9月更文挑战第4天】随着Web应用对实时交互需求的增长,传统的HTTP模型已无法满足现代应用的要求,特别是在需要持续、双向通信的场景下。WebSocket协议由此诞生,提供全双工通信渠道,使服务器与客户端能实时互发消息。作为Java开发中最受欢迎的框架之一,Spring通过其WebSocket模块支持这一协议,简化了WebSocket在Spring应用中的集成。
31 0
|
27天前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
2月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
2月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
95 0