Spring AOP基础知识

Spring AOP使用动态代理技术在运行期织入增强的代码,两种代理机制包括:一是基于JDK的动态代理,另一种是基于CGLib的动态代理。之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。

1、带有横切逻辑的实例

ForumService:包含性能监视横切代码

package com.yyq.proxy;
public interface ForumService {
    void removeTopic(int topicId);
    void removeForum(int forumId);
}

ForumServiceImpl:实现类

package com.yyq.proxy;

public class ForumServiceImpl implements ForumService {
    @Override
    public void removeTopic(int topicId) {
        PerformanceMonitor.begin("com.yyq.proxy.ForumServiceImpl.removeTopic");
        System.out.println("模拟删除Topic记录:" + topicId);
        try {
            Thread.currentThread().sleep(20);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        PerformanceMonitor.end();
    }
    @Override
    public void removeForum(int forumId) {
        PerformanceMonitor.begin("com.yyq.proxy.ForumServiceImpl.removeForum");
        System.out.println("模拟删除Forum记录:" + forumId);
        try {
            Thread.currentThread().sleep(40);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        PerformanceMonitor.end();
    }
}

PerformanceMonitor:性能监视的实现类

package com.yyq.proxy;

public class PerformanceMonitor {
    private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
    public static void begin(String method) {
        System.out.println("begin monitor...");
        MethodPerformance mp = new MethodPerformance(method);
        performanceRecord.set(mp);
    }
    public static void end(){
        System.out.println("end monitor...");
        MethodPerformance mp = performanceRecord.get();
        mp.printPerformance();
    }
}

MethodPerformance:记录性能监视信息

package com.yyq.proxy;

public class MethodPerformance {
    private long begin;
    private long end;
    private String serviceMethod;
    public MethodPerformance(String serviceMethod){
        this.serviceMethod = serviceMethod;
        this.begin = System.currentTimeMillis();
    }
    public void printPerformance(){
        end = System.currentTimeMillis();
        long elapse = end - begin;
        System.out.println(serviceMethod + "花费" + elapse + "毫秒。");
    }
}

TestProxy.testForumService测试方法:

 @Test
    public void testForumService(){
        ForumService forumService = new ForumServiceImpl();
        forumService.removeForum(10);
        forumService.removeTopic(1012);
    }

输出结果:

begin monitor...

模拟删除Forum记录:10

end monitor...

com.yyq.proxy.ForumServiceImpl.removeForum花费40毫秒。

begin monitor...

模拟删除Topic记录:1012

end monitor...

com.yyq.proxy.ForumServiceImpl.removeTopic花费20毫秒。

  存在的问题:当某个方法需要进行性能监视,就必须调整方法代码,在方法体前后分别添加上开启性能监视和结束性能监视的代码。这些非业务逻辑的性能监视代码破坏了ForumServiceImpl业务逻辑的纯粹性。所以需要通过代理的方式,将业务类方法中开启和结束性能监视的这些横切代码从业务类中完全移除,并通过JDK动态代理技术或CGLib动态代理技术将横切代码动态织入到目标方法的相应位置。

2、JDK 动态代理

  JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。而Proxy利用invocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

ForumServiceImpl:移除性能监视横切代码

package com.yyq.proxy;
public class ForumServiceImpl implements ForumService {
    public void removeTopic(int topicId) {
        System.out.println("模拟删除Topic记录:" + topicId);
        try {
            Thread.currentThread().sleep(20);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        PerformanceMonitor.end();
    }

    public void removeForum(int forumId) {
        System.out.println("模拟删除Forum记录:" + forumId);
        try {
            Thread.currentThread().sleep(40);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        PerformanceMonitor.end();
    }
}

PerformanceHandler:性能监视横切代码

package com.yyq.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformanceHandler implements InvocationHandler {
    private Object target;

    public PerformanceHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        PerformanceMonitor.begin(target.getClass().getName() + "." + method.getName());
        Object obj = method.invoke(target, args);
        PerformanceMonitor.end();
        return obj;
    }
}

method.invoke()语句通过Java反射机制间接调用目标对象的方法,这样InvocationHandler的invoke()方法就将横切逻辑点和业务类方法的业务逻辑代码编织到一起了。

TestProxy.testForumService2:创建代理实例

 @Test
    public void testForumService2(){
        ForumService target = new ForumServiceImpl();
        PerformanceHandler handler = new PerformanceHandler(target);
        ForumService proxy = (ForumService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),handler);
        proxy.removeTopic(10);
        proxy.removeForum(1012);
    }

  通过Proxy的newProxyInstance()静态方法为编织了业务类逻辑和性能监视逻辑的handler创建一个符合ForumService接口的代理实例。该方法的第一个入参为类加载器;第二个入参为创建代理实例所需要实现的一组接口;第三个参数是整合了业务逻辑和横切逻辑的编织器对象。

输出结果:

begin monitor...

模拟删除Topic记录:10

end monitor...

com.yyq.proxy.ForumServiceImpl.removeTopic花费22毫秒。

begin monitor...

模拟删除Forum记录:1012

end monitor...

com.yyq.proxy.ForumServiceImpl.removeForum花费41毫秒。

3、CGLib动态代理

  使用JDK创建代理有一个限制,即它只能为接口创建代理实例。而CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

CglibProxy:为任何类创建织入性能监视横切逻辑代理对象的代理创建器

package com.yyq.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxy(Class clazz) {
        enhancer.setSuperclass(clazz);    //设置需要创建子类的
        enhancer.setCallback(this);
        return enhancer.create();    //通过字节码技术动态创建子类实例
    }
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //拦截父类所有方法的调用
        PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
        Object result = proxy.invokeSuper(obj, args);    //通过代理类调用父类中的方法
        PerformanceMonitor.end();
        return result;
    }
}

  intercept(Object obj, Method method, Object[] args, MethodProxy proxy)是CGLib定义的Interceptor接口的方法,它拦截所有目标类方法的调用,obj表示目标类的实例;method为目标类方法的反射对象;args为方法的动态入参;而proxy为代理类实例。

TestProxy.testForumService3:测试Cglib创建的代理类

 @Test
    public void testForumService3(){
        CglibProxy proxy = new CglibProxy();
        ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
        forumService.removeForum(10);
        forumService.removeTopic(1012);
    }

输出结果:

begin monitor...

模拟删除Forum记录:10

end monitor...

com.yyq.proxy.ForumServiceImpl$$EnhancerByCGLIB$$70ce1267.removeForum花费80毫秒。

begin monitor...

模拟删除Topic记录:1012

end monitor...

com.yyq.proxy.ForumServiceImpl$$EnhancerByCGLIB$$70ce1267.removeTopic花费20毫秒。

4、代理知识小结

Spring AOP的底层就是通过使用JDK动态代理或CGLib动态代理技术为目标Bean织入横切逻辑。但是以上实现方式存在三个明显需要改进的地方:

1)目标类的所有方法都添加了性能监视横切逻辑,而有时,这并不是我们所期望的,我们可能只希望对业务类中的某些特定方法添加横切逻辑;

2)我们通过硬编码的方式指定了织入横切逻辑的织入点,即在目标类业务方法的开始和结束前织入代码;

3)我们手工编写代码实例的创建过程,为不同类创建代理时,需要分别编写相应的程序代码,无法做到通用。

Spring AOP的主要工作围绕以上三点展开:Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑,通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)。此外,Spring通过Advisor(切面)将Pointcut和Advice两者组装起来。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术采用统一的方式为目标Bean创建织入切面的代理对象了。

时间: 2024-08-01 22:44:06

Spring AOP基础知识的相关文章

[Java]Spring AOP基础知识-动态代理

Spring AOP使用动态代理技术在运行期织入增强的代码,为了揭示Spring AOP底层的工作机理,有必要对涉及到的Java知识进行学习.Spring AOP使用了两种代理机制:一种是基于JDK的动态代理:另一种是基于CGLib的动态代理.之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理. 带有横切逻辑的实例 我们通过具体化代码实现上一节所介绍例子的性能监视横切逻辑,并通过动态代理技术对此进行改造.在调用每一个目标类方法时启动方法的性能监视,在目标类方法调

[Spring框架]Spring AOP基础入门总结一.

前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务

AOP基础知识及AOP切面编程之注释方法、xml配置方法

<span style="font-family: 微软雅黑; font-size: 10.5pt; letter-spacing: 0pt; ">AOP概念</span> Aspect(切面):它跟类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是对横切性关注点的抽象 joinpoint(连接点):所谓连接点就是被拦截到的点,在spring中,这些点是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器

CgLib动态代理学习【Spring AOP基础之一】

如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发现其参数类型是ClassLoader classLoader, Class<?>[] interface, InvocationHandler handler, 只支持根据接口实现代理类,如果所有代码都是自己掌控,当然没有问题.所有的业务逻辑均抽象出接口,然后所有的业务类实现接口,这样所有的业务类

[Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习. 一, AspectJ的概述: AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. Spring为了简化自身的AOP的开发,将AspectJ拿过来作为Spring自身一个AOP的开发.

spring aop基础概念

spring aop基础概念 1.什么是aop: aspect oriented programming:面向切面编程. 即通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等. 2.aop的实现方式: (1)预编译:AspectJ (2)运行期动态代理(JDK动态代理.CGLIB动态代理):SpringAOP.JbossAOP 3.aop的概念: (1)切面(aspect):一个关注点的模块化,这个关注点可能会横切多个

Spring框架基础知识

本人博客文章网址:https://www.peretang.com/basic-knowledge-of-spring-framework/ Spring框架简介 Spring , 一个开源的框架 , 由Rod Johnson创建. Spring是为了解决企业应用开发的复杂性而创建的 , 可以让简单的JavaBeans/POJO来实现之前只有EJB[1]才能完成的事情. Spring还提供了大量API简化与其他第三方框架的集成. 纵览Spring , 你会发现Spring可以解决非常多的问题 ,

Spring AOP基础实践

一.代码实践 先上代码,Spring AOP的demo,见下面的小例子. 1 package demo.spring; 2 3 import org.aspectj.lang.annotation.After; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.Before; 6 import org.aspectj.lang.annotation.Pointcut; 7 impor

spring - AOP 基础(一)

spring AOP spring支持四种拦截类型:目标方法调用前,目标方法调用后,目标方法调用前后,方法抛出异常. spring Aop采用动态代理的方式生成代理类,前面分析过动态代理和静态代理以及区别,可以点击我的博客. 一些术语解释. 连接点: 程序的某个位置,就是程序也就是类里的方法,一个类可以有多个方法,多个连接点,就是在这些方法前后异常等做文章. 切点: 可能会和连接点混淆,类里有多个连接点,那么程序或者项目执行的时候如何找到对应的连接点?切点就是做这个工作的, 切点帮你定位到对应的