spring高质量系列-AOP

spring支持的AOP的方式有:AspecJ,ProxyFactoryBean,ProxyFactory
其中:AspectJ是目前大家最常用的 起到集成AspectJ和Spring,ProxyFactoryBean是将我们的AOP和IOC融合起来,而ProxyFactory 则是只能通过代码硬编码进行编写 一般都是给spring自己使用

AnnotationAwareAspectJAutoProxyCreator 通过继承SmartInstantiationAwareBeanPostProcessor 来将AsepctJ融入spring 内部生成的代理对象是采用ProxyFactory

涉及到的几个关键类

ProxyConfig:为上面三个类提供配置属性
AdvisedSupport:继承ProxyConfig,实现了Advised。封装了对通知(Advise)和通知器(Advisor)的操作
ProxyCreatorSupport:继承AdvisedSupport,其帮助子类(上面三个类)创建JDK或者cglib的代理对象
Advised:可以获取拦截器和其他 advice, Advisors和代理接口

涉及到的几个关键概念

Advice:通知,定义在连接点做什么,比如我们在方法前后进行日志打印
pointcut:切点,决定advice应该作用于那个连接点,比如根据正则等规则匹配哪些方法需要增强
Pointcut 目前有getClassFilter(类匹配),getMethodMatcher(方法匹配),Pointcut TRUE (全匹配)
JoinPoint:连接点,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的钱、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那不是咱们关注的,只要记住,和方法有关的前前后后都是连接点。
advisor:把pointcut和advice连接起来

ProxyFactory

 public static void main(String[] args) {
        MarshallService marshallService = new MarshallService();
        ProxyFactory proxyFactory = new ProxyFactory(marshallService);
        proxyFactory.addAdvice(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("MethodBeforeAdvice1 method=" + method.getName());
            }
        });


        proxyFactory.addAdvice(new MarshallAdvice());
        MarshallService proxy = (MarshallService) proxyFactory.getProxy();
        proxy.say1();
        proxy.say2();
    }

1.ProxyFactory proxyFactory = new ProxyFactory(marshallService); 分别设置AdvisedSupport类的targetSource,interfaces,并且清空methodCache

targetSource:我们需要代理的原始对象,interfaces:该原始对象实现的接口,methodCache:保存某个方法对应的advisor chain List
3.proxyFactory.addAdvice(new MarshallAdvice()); 添加advice,先判断advice的类型,然后将其包装成不同的advisor,一般都是默认的DefaultPointcutAdvisor,然添加进入集合advisors,并且更新相应的数组advisorArray(为了方便操作)
4 . MarshallService proxy = (MarshallService) proxyFactory.getProxy(); 获取代理对象
5 . 首先调用了父类ProxyCreatorSupport的createAopProxy去激活(即激活相关的监听事件),然后获取AopProxyFactory,通过这factory获取AopProxy(ObjenesisCglibAopProxy,JdkDynamicAopProxy),通过AopProxy获取真正的代理对象

cglib

6.AopProxy.getProxy()方法对于cglib主要是设置enhancer的Callback,这些callback会在代理对象执行方法时候调用,callback的类型有
MethodInterceptor:这就是我们aop,因为 MethodInterceptor的效率不高,它需要产生不同类型的字节码,并且需要生成一些运行时对象(InvocationHandler就不需要),所以Cglib提供了其它的接口供我们选择
NoOp:简单地把方法调用委托给了被代理类的原方法(本例中是Person),不做任何其它的操作
LazyLoader:,它也提供了一个方法:Object loadObject() throws Exception;,loadObject()方法会在第一次被代理类的方法调用时触发,它返回一个代理类的对象,这个对象会被存储起来然后负责所有被代理类方法的调用
Dispatcher :也是提供了loadObject()方法,这个方法同样地返回一个代理对象,这个对象同样可以代理原方法的调用。不过它们之间不同的地方在于,Dispatcher的loadObject()方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。也就是说Dispatcher的loadObject()方法返回的对象并不会被存储起来,可以类比成Spring中的Prototype类型,而LazyLoader则是lazy模式的Singleton
InvocationHandler:它的使用方式和MethodInterceptor差不多,所有对invoke()方法的参数proxy对象的方法调用都会被委托给同一个InvocationHandler,所以可能会导致无限循环
FixedValue:同样也提供了一个loadObject()方法,不过这个方法返回的不是代理对象,而是原方法调用想要的结果。也就是说,在这个Callback里面,看不到任何原方法的信息,也就没有调用原方法的逻辑,不管原方法是什么都只会调用loadObject()并返回一个结果。听起来可能有些没有,但是配合CllbackFilter可以强制使某个方法返回固定的值,并且带来的开销很小。需要注意的是,如果loadObject()方法的返回值并不能转换成原方法的返回值类型,那么会抛出类型转换异常。

jdk

  1. 除了生成字节码不一样其他的都类似 都是调用方法时候被拦截然后通过ProxyCreatorSupport类getInterceptorsAndDynamicInterceptionAdvice获取每个方法对应的advice
    8.spring 的AspectJ代理好像就是使用ProxyFactory去生成代理

ProxyFactoryBean

具体代码如下

    MarshallProxyFactoryBean marshallProxyFactoryBean = (MarshallProxyFactoryBean)applicationContext.getBean("&MarshallProxyFactoryBean");
下面三个属性 也可以通过xml配置
        //advic
        marshallProxyFactoryBean.setInterceptorNames(new String[]{"MarshallAdvice"});
        //代理类实现的接口,如果没有就是cglib
        marshallProxyFactoryBean.setProxyInterfaces(new Class[]{MarshallInterface.class});
        //需要生成proxy的对象
        marshallProxyFactoryBean.setTarget(new MarshallService());
具体生成proxy
        marshallProxyFactoryBean.getObject();
  1. getObject 就是通过initializeAdvisorChain初始化advisor
    2.通过advisorAdapterRegistry 包装咱们的advice,逻辑是如果本身就是advisor 直接返回,如果是MethodInterceptor,包装成DefaultPointcutAdvisor(拦截所有方法),看是否是属于AdvisorAdapter(MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter),也包装成,包装成DefaultPointcutAdvisor
    3.最终都是调用CglibAopProxy或者JdkDynamicAopProxy 获取动态代理对象

AspectJProxyFactory

就是通过@AspectJ 获取对应的class 然后从class里面获取Pointcut 即获取匹配哪些类的哪些方法的规则,然后在获取各个advice 组装起来
最终通过getProxy获取代理对象 通过父类统一初始化advisor和method的映射关系

总结 AspectJProxyFactory,ProxyFactoryBean,ProxyFactory 大体逻辑都是填充AdvisedSupport(ProxyCreatorSupport是其子类)的,然后交给父类ProxyCreatorSupport,然后得到JDK或者CGLIB的AopProxy,代理调用时候被invoke或者intercept方法拦截 (分别在JdkDynamicAopProxy和ObjenesisCglibAopProxy的中)并且在这两个方法中调用ProxyCreatorSupport的getInterceptorsAndDynamicInterceptionAdvice方法去初始化advice和各个方法直接映射关系并缓存

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容