zoukankan      html  css  js  c++  java
  • Spring AOP的底层实现原理

    Spring的两大核心之一就是AOP,AOP:面向切面编程。在说原理之前,得先知道一些 AOP的专业术语。

    AOP的专业术语

    连接点(JoinPoint):增强执行的位置(增加代码的位置),Spring只支持方法;
    切点(PointCut):具体的连接点;一般可能通过一个表达式来描述;
    增强(Advice):也称为消息,指的是增加的额外的代码,Spring中,增强除了包含代码外,还包含位置信息;


    Spring中一共有四种增强:

    • MethodBeforeAdvice:前置增强
    • MethodInterceptor:环绕增强
    • ThrowsAdvice:异常增强
    • AfterReturingAdvice:返回值增强

    引介(Introduction):特殊的增强,动态为类增加方法
    织入(Weaving):将增强加入到目标类的过程,织入分为三种时期

    • 编译器:AspectJ
    • 类加载
    • 运行期:jdk动态代理(实现接口),CGlib(子类,不能用final)

    目标对象(Target):原始对象
    代理对象(Proxy):加入了增强的对象,是生成的;
    切面(Aspect):切点+增强

    接下来我要说的就是在运行期间织入的两种实现方式

    JDK动态代理(实现接口)

    什么是代理模式呢?

    代理模式有三个角色,分别是

    • 抽象角色:接口
    • 目标角色:实现类
    • 代理角色:实现接口(InvocationHandler),并引用目标角色

    代理模式与装饰者模式的区别
    类图(结构)基本一样,但目的不同,装饰模式的前提是已经所有的类,并进行组装;
    而使用代理模式时,我们不能直接访问目标角色或没有权限访问时,可以使用代理模式

    代理模式分为两种

    • 静态代理:需要为每个目标角色,创建一个对应的代理角色;类的数量会急剧膨胀
    • 动态代理:自动为每个目标角色生成对应的代理角色
    1. 接下来就是jdk动态代理的代码:

    实现jdk动态代理的前提是所有的目标类都必须要基于一个统一的接口

    创建统一的接口
    [java] view plain copy
    package com.dao;
    /**
    * 为目标类定义统一的接口SleepDao
    * @author XuXQ
    *
    */
    public interface SleepDao {
      public void sleep();
    }

    定义目标对象
    [java] view plain copy
    package com.daoImpl;
    import com.dao.SleepDao;
    /**
    * 目标类
    * @author XuXQ
    *
    */
    public class SleepDaoImpl implements SleepDao {

    @Override
    public void sleep() {
    System.out.println("本大人要睡觉了");
    }

    }

    创建代理角色

    [java] view plain copy
    package com.handler;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    /**
    * 创建的类得需要实现InvocationHandler,并重写invoke()方法
    * InvocationHandler 是代理实例的调用处理程序 实现的接口
    * 即:MyInvocationHandler 是代理实例的调用处理程序
    * @author XuXQ
    *
    */
    public class MyInvoctionHandler implements InvocationHandler {
    Object object=null;//目标对象

    public MyInvoctionHandler(Object object) {
    super();
    this.object = object;
    }

    /**
    * proxy=代理对象
    * method=被调用方法的方法名
    * args=被调用方法的参数
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws
    Throwable {
    System.out.println("睡觉前要脱衣服啊");
    Object obj=method.invoke(object, args);//obj为目标对象调用方法的返回

    System.out.println("睡着了当然得做个美梦啊");
    return obj;
    }

    }


    [java] view plain copy
    编写测试用例
    [java] view plain copy
    package com.handler;
    [java] view plain copy
    import static org.junit.Assert.*;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;

    import com.dao.SleepDao;
    import com.daoImpl.SleepDaoImpl;
    public class Test1 {
    @Before
    public void setUp() throws Exception {
    }
    @After
    public void tearDown() throws Exception {
    }
    @Test
    public void test() {
    //目标类必须基于统一的接口
    SleepDao s=new SleepDaoImpl();
    ClassLoader classLoader=s.getClass().getClassLoader();
    MyInvoctionHandler myInvoctionHandler=new MyInvoctionHandler(s);

    //Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例

    SleepDao sd=(SleepDao) Proxy.newProxyInstance(classLoader,
    s.getClass().getInterfaces(), myInvoctionHandler);

    //相当于调用代理角色的Invoke()
    sd.sleep();
    }

    }

    结果:

     由结果可以看到成功的将增强织入到了目标对象中了

    此处使用到了jdk的动态代理,invocationHandler,proxy,以及classloader在另外两篇文章有详解

    InvocationHandler和Proxy(Class)的动态代理机制详解

    http://www.cnblogs.com/shoshana-kong/p/9041485.html

    ClassLoader工作机制

    http://www.cnblogs.com/shoshana-kong/p/9042013.html

    2.CGlib代理

    [java] view plain copy
    package com.cglibproxy;
    /**
    * 和JDK动态代理不同,不需要创建统一的接口
    * @author XuXQ
    *
    */
    public class Base {
    public void sleep(){
    System.out.println("本大少要睡觉啦");
    }
    }<strong>
    </strong>

    创建cglib的代理对象
    [java] view plain copy
    package com.cglibproxy;

    import java.lang.reflect.Method;

    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;


    //用来生成代理对象
    public class CglibProxy implements MethodInterceptor {

    public Object getProxy(Object object){
    Enhancer e=new Enhancer();//创建代理对象类
    e.setSuperclass(object.getClass());//声明代理对象的父类是谁(是目标
    对象)
    e.setCallback(this);//设置回调函数,即调用intercept()
    return e.create();//返回创建的代理对象
    }
    /**
    <span style="white-space:pre;"> </span> * proxy=代理对象,也是目标对象的子类
    <span style="white-space:pre;"> </span> * args=方法参数
    <span style="white-space:pre;"> </span> */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args,
    MethodProxy arg3) throws Throwable {
    System.out.println("你个大懒猪,竟然睡觉前不脱衣服,嫌弃o");
    Object object=arg3.invokeSuper(proxy, args);
    System.out.println("起床啦,要不然得迟到了哦");
    return null;
    }

    }

    编写测试用例
    [java] view plain copy
    package com.cglibproxy;

    import static org.junit.Assert.*;

    import org.junit.After;
    import org.junit.Before;

    public class Test {

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @org.junit.Test
    public void test() {
    CglibProxy proxy=new CglibProxy();
    Base base=(Base) proxy.getProxy(new Base());
    base.sleep();
    }

    }

    结果:

     


    由结果同样可以看到成功的将增强织入到了目标对象中了

    此处涉及cglib相关知识

    Cglib及其基本使用

    http://www.cnblogs.com/shoshana-kong/p/9041834.html

    总结
    当然我们以后写代码都是基于Aspect,直接写注解的,当然这并不代表这我们不需要知 道AOP
    底层的实现原理,至于注解用的是哪种实现方式,使用配置来解决的,这里就不详解了 。

  • 相关阅读:
    poj1068
    make&&gcc/g++ 生成 map file
    游戏系统开发笔记(九)——构建战斗系统
    归并排序
    士兵杀敌(四)
    ftp报错 200 port command successful. consider using pasv 425 failed to establish connection
    HDU 4649 Professor Tian
    如何将ASM中的数据文件复制到操作系统中
    Struts2 学习笔记 10 Result部分 part1
    交通管理系统
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/9042102.html
Copyright © 2011-2022 走看看