(京东面试题)java动态代理主要怎么实现的,spring aop 原理 如下类

京东面试题

1、java动态代理主要怎么实现的,spring aop 原理

如下类

public class Test {
  public void example(){
    System.out.println("example");
  }
}

如何实现在方法example 之前打印一句话,之后打印一句话

Spring aop 主要是在不改变原有代码的基础上,通过sppring动态添加代码

是对oop面向对象的有益补充

AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面

通过前置通知,后置通知和环绕通知可以实现


轻松理解AOP(面向切面编程)

本文主要介绍AOP思想,而不是Spring,Spring在本文只做为理解AOP的工具和例子,所以也不打算介绍Spring的Aspect、Join point、Advice、AOP proxy等概念,那样初学者会很难理解,如果你懂了AOP的思想,那么Spring的AOP,还是AspectJ都容易理解了。

Spring如此流行,当我第一次接触Spring的时候,到网上看了一些文章,都讲得神乎其乎,最后我篇也没看懂,我当时就是认为这个东西一定很高深,于是我就遇到做WEB开发的人就会打听一下。得到最多的一个回答就是“Spring是一个框架”,然后我就会问框架是什么,但都没有一种说法不让我感觉玄乎乎的,同时也没有听懂,于是,我更感觉Spring很神了,这可能叫做朦胧美。还有一种说法就是“Spring两个首要的特性就是AOP和IoC”,这种说法让我感觉Spring简直神的飞上天了,我都不敢接着问了,再问可能想上天去找Spring了,后来我就怀疑这玩意儿真有那么高深吗,强列的好奇心让我实在hold,决定一定要试一下这个神器,于是就看了一些Step By Step的文章,自己写了一个HelloWorld,发现这AOP确实是一个比较新颖的思想,也算是打破了常规,是从不同方面思考问题。不过没有那些童鞋说得那么神。

本文旨在帮助还没有理解AOP的童鞋看透弄懂AOP,也欢迎高手批评指正

先说一个Spring是什么吧,大家都是它是一个框架,但框架这个词对新手有点抽象,以致于越解释越模糊,不过它确实是个框架的,但那是从功能的角度来定义的,从本质意义上来讲,Spring是一个库,一个Java库,所以我个人觉得应该这样回答Spring是什么:Spring是一个库,它的功能是提供了一个软件框架,这个框架目的是使软件之间的逻辑更加清晰,配置更灵活,实现这个目的的手段使用AOP和IoC,而AOP和IoC是一种思想,是一种什么样的思想呢,等下细说,先说AOP在Java里是利用反射机制实现(你也可以认为是动态代理,不过动态代理也是反射机制实现的,所以还是先不要管动态代理,我们这里化繁为简,不让它干扰咱们对AOP的理解),如何使用AOP呢,很简单滴,等下介绍。

下面先说AOP是什么样的思想,我们一步一步慢慢来,先看一下传统程序的流程,比如银行系统会有一个取款流程

我们可以把方框里的流程合为一个,另外系统还会有一个查询余额流程,我们先把这两个流程放到一起:

有没有发现,这个两者有一个相同的验证流程,我们先把它们圈起来再说下一步:

有没有想过可以把这个验证用户的代码是提取出来,不放到主流程里去呢,这就是AOP的作用了,有了AOP,你写代码时不要把这个验证用户步骤写进去,即完全不考虑验证用户,你写完之后,在另我一个地方,写好验证用户的代码,然后告诉Spring你要把这段代码加到哪几个地方,Spring就会帮你加过去,而不要你自己Copy过去,这里还是两个地方,如果你有多个控制流呢,这个写代码的方法可以大大减少你的时间,不过AOP的目的不是这样,这只是一个“副作用”,真正目的是,你写代码的时候,事先只需考虑主流程,而不用考虑那些不重要的流程,懂C的都知道,良好的风格要求在函数起始处验证参数,如果在C上可以用AOP,就可以先不管校验参数的问题,事后使用AOP就可以隔山打牛的给所有函数一次性加入校验代码,而你只需要写一次校验代码。不知道C的没关系,举一个通用的例子,经常在debug的时候要打log吧,你也可以写好主要代码之后,把打log的代码写到另一个单独的地方,然后命令AOP把你的代码加过去,注意AOP不会把代码加到源文件里,但是它会正确的影响最终的机器代码。

现在大概明白了AOP了吗,我们来理一下头绪,上面那个方框像不像个平面,你可以把它当块板子,这块板子插入一些控制流程,这块板子就可以当成是AOP中的一个切面。所以AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面,这句话应该好理解吧,我们把纵向流程画成一条直线

这个验证用户这个子流程就成了一个条线,也可以理解成一个切面,aspect的意思我认为是方面,你用什么实物去类比,只要你能理解都可以。这里的切面只插了两三个流程,如果其它流程也需要这个子流程,也可以插到其它地方去。

讲了这么多,那到AOP该如何使用呢?我们要写一个HelloWorld吗,我看还是算了,关于这种类型的文章,网上已经泛滥成灾,我再写也不一定比人家写得好,所以,我会在下面贴几个我认为写得不错的文章链接,但我在这里先介绍一下Spring如何实现AOP的吧。其实也不难理解,Spring的实现是基于函数(或叫方法)的,就是说,你写好了一个函数后,你还可以在不更改原来的代码情况,通过Spring在函数前或函数后动态的加入新的代码。比如你原来的代码是这样的:

void foo() {    
    System.out,println("in foo()");
}

然后你想在函数执行前(当然后也可以加到执行后,或前后都加,原理是一样)加一句:

System.out.println("before execute foo()");  

你也可以多加几句,通过Spring,你可以把这些代码动态的加到函数前面,而不用改变原来的代码。从而会得到与以下等效的执行码:

void foo() {
    System.out.println("before execute foo()");
    System.out.println("in foo()");
}

我这样一说你可能更想亲手试试了,可以看看以下这篇文章,写得很好,我们在此也感谢其作者的辛勤付出。

一个简单的Spring的AOP例子

参考资料:

Spring AOP Guide: http://docs.spring.io/spring/docs/2.5.4/reference/aop.html


一个简单的Spring的AOP例子

经过这段日子的学习和使用Spring,慢慢地体会到Spring的优妙之处,正在深入地吸收Spring的精华,呵呵。现在写的这个只是个简单AOP例子,包括前置通知,后置通知,环绕通知,和目标对象。写这个例子的主要目标只是想让想学AOP的能更快地入门,了解一下如何去配置AOP里面的东东。

目标对象的接口:IStudent.java

package com.dragon.study;

public interface IStudent {
    
    public void addStudent(String name);
    
}

目标类:StudentImpl.java

package com.dragon.study.Impl;

import com.dragon.study.IStudent;

public class StudentImpl implements IStudent{

    public void addStudent(String name) {
        System.out.println("欢迎"+name+" 你加入Spring家庭!");
    }
    
}

前置通知:BeforeAdvice.java

package com.dragon.Advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice{

    public void before(Method method, Object[] args, Object target) throws Throwable {
    
        System.out.println("这是Before类的before方法");
    
    }

}

后置通知:AfterAdvice.java

package com.dragon.Advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice{

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("这是AfterAdvince类的afterReturning方法");
    }
    
    
}

环绕通知:CompareInterceptor.java

package com.dragon.Advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class CompareInterceptor implements MethodInterceptor{

    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object result = null;
        String stu_name = invocation.getArguments()[0].toString();
        if(stu_name.equals("dragon")) {
            //如果学生是dragon时,执行目标方法,
            result = invocation.proceed();
        } else {
            System.out.println("此学生是"+stu_name+"而不是dragon,不批准其加入.");
        }
        
        return result;
    }
    
}

配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="beforeAdvice" class="com.dragon.Advice.BeforeAdvice"></bean>
    <bean id="afterAdvice" class="com.dragon.Advice.AfterAdvice"></bean>
    <bean id="compareInterceptor" class="com.dragon.Advice.CompareInterceptor"></bean>
    <bean id="studenttarget" class="com.dragon.study.Impl.StudentImpl"></bean>

    <bean id="student" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces">
            <value>com.dragon.study.IStudent</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>
                <value>afterAdvice</value>
                <value>compareInterceptor</value>
            </list>
        </property>
        <property name="target">
            <ref bean="studenttarget" />
        </property>

    </bean>


</beans>

现在开始写测试类,Test.java

package com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.dragon.study.IStudent;

public class Test {
    public static void main(String[] args) {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        IStudent person = (IStudent) ctx.getBean("student");
        person.addStudent("dragon");
    }
}

代码结构


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