• [技术干货] Python 面向切面编程 AOP 及装饰器(转载)
    什么是 AOP装饰器函数装饰器类装饰器1、函数装饰函数2、类装饰函数3、函数装饰类4、类装饰类什么是 AOPAOP,就是面向切面编程,简单的说,就是动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。这样我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这种思想,可以使原有代码逻辑更清晰,对原有代码毫无入侵性,常用于像权限管理,日志记录,事物管理等等。而 Python 中的装饰器就是很著名的设计,常用于有切面需求的场景。类如,Django 中就大量使用装饰器去完成一下切面需求,如权限控制,内容过滤,请求管理等等。装饰器Python 装饰器(fuctional decorators)就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名或类名的情况下,给函数增加新的功能。下面就跟我一起详细的了解下装饰器是如何工作的。首先,要明确一个概念:Python 中万物皆对象,函数也是是对象!所以,一个函数作为对象,可以在另一个函数中定义。看下面示例:12345678910def a():    def b():        print("I'm b")    b()    c = b    return cd = a()d()b()c()输出结果为:I'm bI'm b抛出 NameError: name 'b' is not defined 错误抛出 NameError: name 'c' is not defined 错误从上可以看出,由于函数是对象,所以:可以赋值给变量可以在另一个函数中定义然后,return 可以返回一个函数对象。这个函数对象是在另一个函数中定义的。由于作用域不同,所以只有 return 返回的函数可以调用,在函数 a 中定义和赋值的函数 b 和 c 在外部作用域是无法调用的。这意味着一个功能可以 return 另一个功能。除了可以作为对象返回外,函数对象还可以作为参数传递给另一个函数:123456def a():    print("I'm a")def b(func):    print("I'm b")    func()b(a) 输出结果:I'm bI'm aOK,现在,基于函数的这些特性,我们就可以创建一个装饰器,用来在不改变原函数的情况下,实现功能。函数装饰器比如,我们要在函数执行前和执行后分别执行一些别的操作,那么根据上面函数可以作为参数传递,我们可以这样实现,看下面示例:1234567def a():    print("I'm a")def b(func):    print('在函数执行前,做一些操作')    func()    print("在函数执行后,做一些操作")b(a)输出结果:在函数执行前,做一些操作I'm a在函数执行后,做一些操作但是这样的话,原函数就变成了另一个函数,每加一个功能,就要在外面包一层新的函数,这样原来调用的地方,就会需要全部修改,这明显不方便,就会想,有没有办法可以让函数的操作改变,但是名称不改变,还是作为原函数呢。看下面示例:123456789101112def a():    print("I'm a") def c(func):    def b():        print('在函数执行前,做一些操作')        func()        print("在函数执行后,做一些操作")    return b a = c(a)a()输出结果:在函数执行前,做一些操作I'm a在函数执行后,做一些操作如上,我们可以将函数再包一层,将新的函数 b,作为对象返回。这样通过函数 c,将 a 改变并重新赋值给 a,这样就实现了改变函数 a,并同样使用函数 a 来调用。但是这样写起来非常不方便,因为需要重新赋值,所以在 Python 中,可以通过 @ 来实现,将函数作为参数传递。看下示例:12345678910def c(func):    def b():        print('在函数执行前,做一些操作')        func()        print("在函数执行后,做一些操作")    return b@cdef a():    print("I'm a")a() 输出结果:在函数执行前,做一些操作I'm a在函数执行后,做一些操作如上,通过 @c,就实现了将函数 a 作为参数,传入 c,并将返回的函数重新作为函数 a。这 c 也就是一个简单的函数装饰器。且如果函数是有返回值的,那么改变后的函数也需要有返回值,如下所以:123456789101112def c(func):    def b():        print('在函数执行前,做一些操作')        result = func()        print("在函数执行后,做一些操作")        return result    return b@cdef a():    print("函数执行中。。。")    return "I'm a"print(a())输出结果:在函数执行前,做一些操作函数执行中。。。在函数执行后,做一些操作I'm a如上所示:通过将返回值进行传递,就可以实现函数执行前后的操作。但是你会发现一个问题,就是为什么输出 I'm a 会在最后才打印出来?因为 I'm a 是返回的结果,而实际上函数是 print("在函数执行后,做一些操作") 这一操作前运行的,只是先将返回的结果给到了 result,然后 result 传递出来,最后由最下方的 print(a()) 打印了出来。那如何函数 a 带参数怎么办呢?很简单,函数 a 带参数,那么我们返回的函数也同样要带参数就好啦。看下面示例:123456789101112def c(func):    def b(name, age):        print('在函数执行前,做一些操作')        result = func(name, age)        print("在函数执行后,做一些操作")        return result    return b@cdef a(name, age):    print("函数执行中。。。")    return "我是 {}, 今年{}岁 ".format(name, age)print(a('Amos', 24))输出结果:在函数执行前,做一些操作函数执行中。。。在函数执行后,做一些操作我是 Amos, 今年24岁 但是又有问题了,我写一个装饰器 c,需要装饰多个不同的函数,这些函数的参数各不相同,那么怎么办呢?简单,用 *args 和 **kwargs 来表示所有参数即可。如下示例:1234567891011121314151617def c(func):    def b(*args, **kwargs):        print('在函数执行前,做一些操作')        result = func(*args, **kwargs)        print("在函数执行后,做一些操作")        return result    return b@cdef a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age)@cdef d(sex, height):    print('函数执行中。。。')    return '性别:{},身高:{}'.format(sex, height)print(a('Amos', 24))print(d('男', 175))输出结果:在函数执行前,做一些操作函数执行中。。。在函数执行后,做一些操作我是 Amos, 今年24岁 在函数执行前,做一些操作函数执行中。。。在函数执行后,做一些操作性别:男,身高:175如上就解决了参数的问题,哇,这么好用。那是不是这样就没有问题了?并不是!经过装饰器装饰后的函数,实际上已经变成了装饰器函数 c 中定义的函数 b,所以函数的元数据则全部改变了!如下示例:123456789101112def c(func):    def b(*args, **kwargs):        print('在函数执行前,做一些操作')        result = func(*args, **kwargs)        print("在函数执行后,做一些操作")        return result    return b@cdef a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age)print(a.__name__)输出结果:b会发现函数实际上是函数 b 了,这就有问题了,那么该怎么解决呢,有人就会想到,可以在装饰器函数中先把原函数的元数据保存下来,在最后再讲 b 函数的元数据改为原函数的,再返回 b。这样的确是可以的!但我们不这样用,为什么?因为 Python 早就想到这个问题啦,所以给我们提供了一个内置的方法,来自动实现原数据的保存和替换工作。哈哈,这样就不同我们自己动手啦!看下面示例:1234567891011121314from functools import wrapsdef c(func):    @wraps(func)    def b(*args, **kwargs):        print('在函数执行前,做一些操作')        result = func(*args, **kwargs)        print("在函数执行后,做一些操作")        return result    return b@cdef a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age)print(a.__name__)输出结果:a使用内置的 wraps 装饰器,将原函数作为装饰器参数,实现函数原数据的保留替换功能。耶!装饰器还可以带参数啊,你看上面 wraps 装饰器就传入了参数。哈哈,是的,装饰器还可以带参数,那怎么实现呢?看下面示例:123456789101112131415161718from functools import wrapsdef d(name):    def c(func):        @wraps(func)        def b(*args, **kwargs):            print('装饰器传入参数为:{}'.format(name))            print('在函数执行前,做一些操作')            result = func(*args, **kwargs)            print("在函数执行后,做一些操作")            return result        return b    return c@d(name='我是装饰器参数')def a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age) print(a('Amos', 24))输出结果:装饰器传入参数为:我是装饰器参数在函数执行前,做一些操作函数执行中。。。在函数执行后,做一些操作我是 Amos, 今年24岁 如上所示,很简单,只需要在原本的装饰器之上,再包一层,相当于先接收装饰器参数,然后返回一个不带参数的装饰器,然后再将函数传入,最后返回变化后的函数。这样就可以实现很多功能了,这样可以根据传给装饰器的参数不同,来分别实现不同的功能。另外,可能会有人问, 可以在同一个函数上,使用多个装饰器吗?答案是:可以!看下面示例:1234567891011121314151617181920212223242526272829303132from functools import wrapsdef d(name):    def c(func):        @wraps(func)        def b(*args, **kwargs):            print('装饰器传入参数为:{}'.format(name))            print('我是装饰器d: 在函数执行前,做一些操作')            result = func(*args, **kwargs)            print("我是装饰器d: 在函数执行后,做一些操作")            return result        return b    return cdef e(name):    def c(func):        @wraps(func)        def b(*args, **kwargs):            print('装饰器传入参数为:{}'.format(name))            print('我是装饰器e: 在函数执行前,做一些操作')            result = func(*args, **kwargs)            print("我是装饰器e: 在函数执行后,做一些操作")            return result        return b    return c@e(name='我是装饰器e')@d(name='我是装饰器d')def func_a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age) print(func_a('Amos', 24))行后,做一些操作我是 Amos, 今年24岁  输出结果:装饰器传入参数为:我是装饰器e我是装饰器e: 在函数执行前,做一些操作装饰器传入参数为:我是装饰器d我是装饰器d: 在函数执行前,做一些操作函数执行中。。。我是装饰器d: 在函数执行后,做一些操作我是装饰器e: 在函数执如上所示,当两个装饰器同时使用时,可以想象成洋葱,最下层的装饰器先包装一层,然后一直到最上层装饰器,完成多层的包装。然后执行时,就像切洋葱,从最外层开始,只执行到被装饰函数运行时,就到了下一层,下一层又执行到函数运行时到下一层,一直到执行了被装饰函数后,就像切到了洋葱的中间,然后再往下,依次从最内层开始,依次执行到最外层。示例:当一个函数 a 被 b,c,d 三个装饰器装饰时,执行顺序如下图所示,多个同理。12345@d@c@bdef a():    pass类装饰器在函数装饰器方面,很多人搞不清楚,是因为装饰器可以用函数实现(像上面),也可以用类实现。因为函数和类都是对象,同样可以作为被装饰的对象,所以根据被装饰的对象不同,一同有下面四种情况:函数装饰函数类装饰函数函数装饰类类装饰类下面我们依次来说明一下这四种情况的使用。1、函数装饰函数1234567891011121314151617181920from functools import wrapsdef d(name):    def c(func):        @wraps(func)        def b(*args, **kwargs):            print('装饰器传入参数为:{}'.format(name))            print('在函数执行前,做一些操作')            result = func(*args, **kwargs)            print("在函数执行后,做一些操作")            return result        return b    return c@d(name='我是装饰器参数')def a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age) print(a.__class__)# 输出结果:<class 'function'>此为最常见的装饰器,用于装饰函数,返回的是一个函数。2、类装饰函数也就是通过类来实现装饰器的功能而已。通过类的 __call__ 方法实现:1234567891011121314151617181920from functools import wrapsclass D(object):    def __init__(self, name):        self._name = name    def __call__(self, func):        @wraps(func)        def wrapper(*args, **kwargs):            print('装饰器传入参数为:{}'.format(self._name))            print('在函数执行前,做一些操作')            result = func(*args, **kwargs)            print("在函数执行后,做一些操作")            return result        return wrapper@D(name='我是装饰器参数')def a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age)print(a.__class__)# 输出结果:<class 'function'>以上所示,只是将用函数定义的装饰器改为使用类来实现而已。还是用于装饰函数,因为在类的 __call__ 中,最后返回的还是一个函数。此为带装饰器参数的装饰器实现方法,是通过 __call__ 方法。若装饰器不带参数,则可以将 __init__ 方法去掉,但是在使用装饰器时,需要 @D() 这样使用,如下:123456789101112131415161718from functools import wrapsclass D(object):    def __call__(self, func):        @wraps(func)        def wrapper(*args, **kwargs):            print('在函数执行前,做一些操作')            result = func(*args, **kwargs)            print("在函数执行后,做一些操作")            return result        return wrapper@D()def a(name, age):    print('函数执行中。。。')    return "我是 {}, 今年{}岁 ".format(name, age) print(a.__class__)# 输出结果:<class 'function'>如上是比较方便简答的,使用类定义函数装饰器,且返回对象为函数,元数据保留。3、函数装饰类下面重点来啦,我们常见的装饰器都是用于装饰函数的,返回的对象也是一个函数,而要装饰类,那么返回的对象就要是类,且类的元数据等也要保留。不怕丢脸的说,目前我还不知道怎么实现完美的类装饰器,在装饰类的时候,一般有两种方法:返回一个函数,实现类在创建实例的前后执行操作,并正常返回此类的实例。但是这样经过装饰器的类就属于函数了,其无法继承,但可以正常调用创建实例。如下:123456789101112131415161718192021222324252627282930from functools import wrapsdef d(name):    def c(cls):        @wraps(cls)        def b(*args, **kwargs):            print('装饰器传入参数为:{}'.format(name))            print('在类初始化前,做一些操作')            instance = cls(*args, **kwargs)            print("在类初始化后,做一些操作")            return instance        return b    return c@d(name='我是装饰器参数')class A(object):    def __init__(self, name, age):        self.name = name        self.age = age        print('类初始化实例,{} {}'.format(self.name, self.age)) a = A('Amos', 24)print(a.__class__)print(A.__class__) # 输出结果:装饰器传入参数为:我是装饰器参数在类初始化前,做一些操作类初始化实例,Amos 24在类初始化后,做一些操作<class '__main__.A'><class 'function'>如上所示,就是第一种方法。4、类装饰类接上文,返回一个类,实现类在创建实例的前后执行操作,但类已经改变了,创建的实例也已经不是原本类的实例了。看下面示例:1234567891011121314151617181920212223242526272829303132def desc(name):    def decorator(aClass):        class Wrapper(object):            def __init__(self, *args, **kwargs):                print('装饰器传入参数为:{}'.format(name))                print('在类初始化前,做一些操作')                self.wrapped = aClass(*args, **kwargs)                print("在类初始化后,做一些操作")             def __getattr__(self, name):                print('Getting the {} of {}'.format(name, self.wrapped))                return getattr(self.wrapped, name)             def __setattr__(self, key, value):                if key == 'wrapped':  # 这里捕捉对wrapped的赋值                    self.__dict__[key] = value                else:                    setattr(self.wrapped, key, value)         return Wrapper    return decorator@desc(name='我是装饰器参数')class A(object):    def __init__(self, name, age):        self.name = name        self.age = age        print('类初始化实例,{} {}'.format(self.name, self.age)) a = A('Amos', 24)print(a.__class__)print(A.__class__)print(A.__name__)输出结果:装饰器传入参数为:我是装饰器参数在类初始化前,做一些操作类初始化实例,Amos 24在类初始化后,做一些操作<class '__main__.desc.<locals>.decorator.<locals>.Wrapper'><class 'type'>Wrapper如上,看到了吗,通过在函数中新定义类,并返回类,这样函数还是类,但是经过装饰器后,类 A 已经变成了类 Wrapper,且生成的实例 a 也是类 Wrapper 的实例,即使通过 __getattr__ 和 __setattr__ 两个方法,使得实例a的属性都是在由类 A 创建的实例 wrapped 的属性,但是类的元数据无法改变。很多内置的方法也就会有问题。我个人是不推荐这种做法的!所以,我推荐在代码中,尽量避免类装饰器的使用,如果要在类中做一些操作,完全可以通过修改类的魔法方法,继承,元类等等方式来实现。如果避免不了,那也请谨慎处理。
  • [其他] Spring三大核心思想之AOP(面向切面编程)
    一、什么是AOP(面向切面编程)?AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。AOP (面向切面编程)是 OOP(面向对象) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程 的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。二、AOP 的作用及其优势作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强优势:减少重复代码,提高开发效率,并且便于维护三、AOP 的底层实现实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现 的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。动态代理的作用:    1)在目标类源代码不改变的情况下,增加功能。    2)减少代码的重复    3)专注业务逻辑代码    4)解耦合,让你的业务功能和日志,事务非业务功能分离。代理设计模式详细笔记 :代理模式详细笔记,想学好Spring的AOP思想先理解代理模式java设计模式之代理设计模式笔记详细内容:代理模式是结构型模式其中的一种现在开发中存在的问题什么是代理模式,为什么需要使用代理模式静态代理及实现什么是动态代理JDK 动态代理和cglib动态代理的使用及区别三种动态代理的对比及优缺点代理模式的使用场景四、AOP 相关概念Spring 的 AOP 实现底层就是对动态代理的代码进行了封装 ,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:1、切面(Aspect)一个切面就是一个代理对象= 要为那些类生成代理+要添加的额外功能是那些2、切入点(pointcut):将来要为那些类生成代理对象3、通知/ 增强(advice):就是要添加的额外功能生活案例:给面包之间涂果酱注意:切面(Aspect)=切入点(pointcut)+通知点(advice,额外功能)4、AOP中的(面向切面编程)通知前置通知:目标方法之前的额外功能 MethodBeforeAdvice环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式 MethodInterceptor(Interceptor [ɪntəˈsɛptə]拦截器)后置通知:目标方法之后的额外功能 AfterReturningAdvice(returning[rɪˈtɜːrnɪŋ])异常通知:执行异常的额外功能 ThrowsAdvice最终通知:一定执行的额外功能5、Target(目标对象):代理的目标对象6、代理(Proxy[ˈprɑːksi]):一个类被AOP注入增强后,就产生一个结果代理类7、连接点(joinPoint):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法(额外功能),因为spring只支持方法类型的连接点五、切面编程步骤1、五种通知类AOP 切面=切入点+通知前置通知:MethodBeforeAdvice后置通知:AfterReturnAdvice环绕通知:MethodInterceptor异常通知:ThrowsAdvice(throw [θroʊz])最终通知通知的配置语法:<aop : 通知类型 method=“切面类中方法名” pointcut=“切点表达式"> </aop:通知类型>1切点表达式的抽取当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。<aop:config>    <!--引用myAspect的Bean为切面对象-->    <aop:aspect ref="myAspect">        <aop:pointcut id="myPointcut" expression="execution(* com.tjcu.aop.*.*(..))"/>        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>    </aop:aspect></aop:config>aop织入的配置<aop:config>    <aop:aspect ref=“切面类”>        <aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>    </aop:aspect></aop:config>切点表达式的写法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))12、AOP的开发步骤1、开发目标类2、开发通知类,确定额外功能3、管理通知类4、配置切入点 确定要为那些类添加额外功能5、将目标类和切面类的对象创建权交给 spring6、组装切面 切入点+通知(额外功能)3、AOP编程所需要的依赖引入依赖   spring-aop   spring-expression   spring-aspects六、AOP实现前置通知案例1、目标接口类public interface CityService {    public void login();    public  void add(String name);}2、目标实现类(核心功能)/** * @author 王恒杰 * @Description:目标对象 */public class CityServiceImpl implements CityService{    @Override    public void login() {        //前置通知:System.out.println("嘻嘻哈哈");        //执行核心的业务逻辑 调用Dao        System.out.println("登录调用Dao");    }    @Override    public void add(String name) {        //前置通知:System.out.println("嘻嘻哈哈");        //执行核心的业务逻辑 调用Dao        System.out.println("添加调用Dao");    }}3、前置通知(额外功能)动态代理代码/** * @author 王恒杰 * @Description: 通知类。额外功能 */public class MyBeforeAdvice implements MethodBeforeAdvice {    /**     * 额外功能书写的方法     * 参数1:代理对象当前调用的方法     * 参数2:当前代理对象调用的方法的参数     * 参数3:目标对象(被代理的对象)     * @param method     * @param objects     * @param o     * @throws Throwable     */    @Override    public void before(Method method, Object[] objects, Object o) throws Throwable {     System.out.println("嘻嘻哈哈");    }}4、将目标类和切面类的对象创建权交给 spring<!--管理目标对象-->    <bean class="before.CityServiceImpl" id="cityService"></bean>    <!--管理通知类 动态代理实现AOP-->    <bean id="myBeforeAdvice" class="before.MyBeforeAdvice"></bean>5、在 spring.xml 中配置织入关系(前置功能)<!--组装切面-->    <aop:config>    <!--配置切入点    id:切入点的唯一标识    expression:切入点表达式,为那些类添加额外功能     execution() 切入点表达式的一种 精确到要添加到额外功能的方法     *:通配     空格:     before.CityServiceImpl.*:所有方法     (..):所有参数    -->        <aop:pointcut id="pc1" expression="execution(* before.CityServiceImpl.*(..))"/>    <!--组装切面=切入点+通知      advice-ref:通知的id      pointcut-ref:切入点的id    -->        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc1"></aop:advisor>    </aop:config>6、测试代码   @Test    public void testMethodBeforeAdvice() {        //启动工厂        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("before/spring.xml");        //获取组件 目标对象就是代理对象        CityService cityService = (CityService) ctx.getBean("cityService");                //目标对象就是代理对象  class com.sun.proxy.$Proxy4        System.out.println(cityService.getClass());                    //调用方法,通过代理类调用目标类        cityService.add("123");    }注意: 获取组件时,目标对象就是代理对象七、Spring中的环绕通知案例1、dao层接口public interface StudentDao {    /**     * 登录     * @param name     */    public void login(String name);    /**     * 分页     * @param name     * @return     */    public String pageShow(String name);}2、dao层实现类public class StudentDaoImpl implements StudentDao{    @Override    public void login(String name) {        //循环10000次        for (int i = 0; i < 10000; i++) {        }        System.out.println("数据库实现登录");    }    @Override    public String pageShow(String name) {        //循环10000次        for (int i = 0; i < 10000; i++) {        }        System.out.println("数据库实现分页");        return name;    }}3、目标接口类public interface StudentService {    /**     * 登录     * @param name     */    public void login(String name);    /**     * 分页     * @param name     * @return     */    public String pageShow(String name);}4、目标实现类public class StudentServiceImpl implements StudentService{    private StudentDao studentDao;    public void setStudentDao(StudentDao studentDao) {        this.studentDao = studentDao;    }    @Override    public void login(String name) {        System.out.println("登录日志");      studentDao.login(name);    }    @Override    public String pageShow(String name) {        System.out.println("分页日志");        String s = studentDao.pageShow(name);        return s;    }}5、环绕通知核心方法: Object proceed = methodInvocation.proceed(); 放行public class StudentAroundAdvice implements MethodInterceptor {    /**     * 参数 内部封装者当前的代理对象 方法的参数,执行方法等     *     * @param methodInvocation     * @return     * @throws Throwable     */    @Override    public Object invoke(MethodInvocation methodInvocation) throws Throwable {        //1.控制事务        System.out.println("控制事务");        //method Invocation 方法调用        System.out.println("当前调用方法的名字" + methodInvocation.getMethod().getName());        //arguments参数 methodInvocation:方法调用        System.out.println("当前的参数为:" + methodInvocation.getArguments()[0]);        System.out.println("--------------");        //记录当前的时间 单位毫秒        long begin = System.currentTimeMillis();        System.out.println("调用查询的数据库");        //放行,执行目标方法 proceed:继续做proceed        Object proceed = methodInvocation.proceed();        //记录结束的时间 单位毫秒        long end = System.currentTimeMillis();        System.out.println("dao执行所用时间" + (end - begin));        return proceed;    }}6、将目标类和切面类的对象创建权交给 spring  <!--管理dao组件-->    <bean id="studentDao" class="com.tjcu.dao.StudentDaoImpl"></bean>    <!--管理Service组件/目标对象-->    <bean id="studentService" class="com.tjcu.service.StudentServiceImpl">        <!--注入值-->        <property name="studentDao" ref="studentDao"></property>    </bean>    <!--管理通知组件-->    <bean id="studentAroundAdvice" class="com.tjcu.advice.StudentAroundAdvice"></bean>7、在 applicationContext.xml 中配置织入关系,aop相关配置 <!--aop相关配置  切面=切点+环绕通知-->    <aop:config>        <!--切入点 execution:[eksɪˈkjuːʃn]执行-->        <aop:pointcut id="pointcut" expression="execution(* com.tjcu.service.StudentServiceImpl.*(..))"/>        <!--组装切面  advisor[ [ædˈvaɪzər]]:顾问  advice-ref:通知  pointcut-ref:切入点-->        <aop:advisor advice-ref="studentAroundAdvice" pointcut-ref="pointcut"></aop:advisor>    </aop:config>8、测试代码  @Test    public void AroundAdviceTest() {        //启动工厂        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/tjcu/Spring/ApplicationContext.xml");        //获取组件 目标对象就是代理对象        StudentService studentService = (StudentService) context.getBean("studentService");        //调用方法,通过代理类调用目标类        studentService.pageShow("通过代理类调用调用目标类");    }
  • [Java] AOP介绍
    AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”来自:https://zhuanlan.zhihu.com/p/90026505
  • [问题求助] 怎么通过AOP获取controller层面的请求入参,使用HttpServletRequest获取request为null
    @Before("executeController()")    public void before(JoinPoint joinPoint) throws Exception{        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;        HttpServletRequest request = servletRequestAttributes.getRequest();        .....