Groovy08_MOP与元编程(方法拦截) ```

Posted 李樟清

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Groovy08_MOP与元编程(方法拦截) ```相关的知识,希望对你有一定的参考价值。

// java当中我们可以使用反射在运行的时候查看类的结构方法成员属性,
// 但是不能修改一个类的实现,不能动态的往类中去注入一个方法
// 而Groovy利用MOP元编程,可以做到这些,可以基于应用当前的状态,
// 动态的添加或者改变一些类的对象的方法和行为
// 比如一个类的一个方法我们没有写实现,可以通过服务器下发,吧这个方法的实现替换掉或者把里面的成员属性替换掉
// 由终端或者控制台服务器来操作这些行为

1. 第一种拦截方式

// 1. Person 是一个实现了GroovyObject的一个类
class Person
    def name

    def dream()
        println 'i have a dream'
    


def p = new Person(name:'Zeking')

// 调用方法
p.dream()       // i have a dream
p.invokeMethod('dream',null)        // i have a dream

MetaMethod m = p.metaClass.getMetaMethod('dream',null)
m.invoke(p,null)         // i have a dream

println  '========================================================'
// 2. 进行拦截
// 实现GroovyInterceptable 接口 ,虽然这个接口没有任何的新增方法
// 但是我们实现了这个接口,我们在这个对象上调用任何方法
// 他会去调用到 invokeMethod  方法
class Person2 implements GroovyInterceptable
    def name

    def dream()
        println 'p2 i have a dream'
    

    Object invokeMethod(String name, Object args)  // 这边成功进行了拦截
//        println 'inovoke'
        System.out.println 'p2 invoke'
    


def p2 = new Person2(name:'Zeking')
p2.dream()      // Caught: java.lang.StackOverflowError
// 因为 Person2 实现了 GroovyInterceptable方法,所以当我们p2.dream()的时候
// 他不会去调用dream方法会去调用invokeMethod方法,而invokeMethod 被我们改变了行为,
// 行为是println 'inovoke' ,所以他不会去调用dream方法,而是调用println方法
// 又因为println 是 Grovvy 为我们注入到 Object 当中的 ,即为Person注入的方法,
// 而println 方法又会调用到invokeMethod 方法,所以层级太深了。我们可以用System.out.println 'invoke'

p2.dream1()     // 调用不存在的方法也会 调用到invokeMethod 方法,
println  '========================================================'

// 3. metaClass.invokeMethod 说明
class Person3 implements GroovyInterceptable
    def name

    def dream()
        System.out.println  'p3  i have a dream'
    

    Object invokeMethod(String name, Object args)  // 这边成功进行了拦截
        System.out.println 'p3 invoke'
        //respondsTo(name) // 不能调用 respondsTo ,不然又会StackOverflowError
        // 那怎么判断这个类有么有实现 方法
        if (metaClass.invokeMethod(this,'respondsTo',name,args))
            // 这是metaClass上面调用 invokeMethod方法 和在 Person3上面调用 invokeMethod方法有什么不一样的?
            // metaClass 是定义class的行为,而在Person3 是定义Person3类型的对象行为
            // metaClass 没有实现GroovyInterceptable 接口,是可以成功调用的

        
    



def p3 = new Person3(name:'Zeking')
p3.invokeMethod('dream',null)
// 没有实现GroovyInterceptable 接口 : p3 invoke ;
// 实现了GroovyInterceptable 接口:    p3 invoke
p3.metaClass.invokeMethod(p3,'dream',null)
// 没有实现GroovyInterceptable 接口 :p3  i have a dream
// 实现了GroovyInterceptable 接口:   p3  i have a dream
p3.dream()
// 没有实现GroovyInterceptable 接口 : p3  i have a dream
// 实现了GroovyInterceptable 接口:    p3 invoke

println  '========================================================'
// 4.
class Person4 implements GroovyInterceptable
    def name

    def dream()
        System.out.println  'p4  i have a dream'
    

    Object invokeMethod(String name, Object args)  // 这边成功进行了拦截
        System.out.println 'p4 invoke'
        //respondsTo(name) // 不能调用 respondsTo ,不然又会StackOverflowError
        // 那怎么判断这个类有么有实现 方法
        if (metaClass.invokeMethod(this,'respondsTo',name,args))
            metaClass.invokeMethod(this,name,null)
        else 
            System.out.println 'p4 missing method'
        
    

def p4 = new Person4(name:'Zeking')
p4.dream()
// p4 invoke
// p4  i have a dream

println  '========================================================'

// 应用
class Manager 
    static Manager instance
    def isInit

    static Manager getInstance()
        if (null == instance)
            synchronized (Manager.class)
                if (null == instance)
                    instance = new Manager()
                
            
        
        return instance
    

    def init()
        isInit = true
    

    def dream()
        if (isInit)
            System.out.println  'm5  i have a dream'
        
    


class Manager2 implements  GroovyInterceptable
    static Manager2 instance
    def isInit

    static Manager2 getInstance()
        if (null == instance)
            synchronized (Manager2.class)
                if (null == instance)
                    instance = new Manager2()
                
            
        
        return instance
    

    def init()
        isInit = true
    

    def dream()
        if (isInit)
            System.out.println  'm5  i have a dream'
        
    

    Object invokeMethod(String name, Object args)  // 这边成功进行了拦截
        System.out.println 'm2 invoke'
        if (name != 'init')
            if(!isInit)
                System.out.println 'please invoke init first'
                return
            
        

        if (metaClass.invokeMethod(this,'respondsTo',name,args))
            metaClass.invokeMethod(this,name,null)
        else 
            System.out.println 'p4 missing method'
        
    


Manager2.instance.dream()
// p4 invoke
// please invoke init first


Manager2.instance.init()
Manager2.instance.dream()
// m2 invoke
// m2 invoke
// m5  i have a dream

2. 第二种拦截方式

// 1.第一种方式:在单个对象上进行方法拦截
// def zeking = new Zeking()  针对对象而不会对类产生影响
// 2.第二种方式: 在类上进行方法拦截
// Zeking.metaClass

println '==============1.1================================='

class Zeking

    def dream()
        println "Zekign's dream"
    


// 1.1
def zeking = new Zeking()
zeking.dream()              // Zekign's dream
zeking.metaClass.dream =   // 用一个闭包替换掉
    println 'replace dream'

zeking.dream()              // replace dream
new Zeking().dream()        // Zekign's dream

println '==============1.2================================='
// 1.2
class Zeking2

    def dream()
        println "2 : Zekign's dream"
    

def zeking2 = new Zeking2()

zeking2.metaClass.dream =   // 用一个闭包替换掉
    println '2 : replace dream'


// 如果我们覆盖了一个对象或者一个类的invokeMethod方法
// 那么它的实现就和 实现了GroovyInterceptable接口的类 一样了
// 不管调用了他的什么方法,都会调用到invokeMethod 里面去
zeking2.metaClass.invokeMethod = 
    String name, Object args->
        System.out.println('2 : invoke')


zeking2.dream()              // 2 : invoke

println '=============1.3=================================='
// 1.3
class Zeking3

    def dream()
        println "3 : Zekign's dream"
    

def zeking3 = new Zeking3()

zeking3.metaClass.dream =   // 用一个闭包替换掉
    println '3 : replace dream'


zeking3.metaClass.invokeMethod = 
    String name, Object args->
        System.out.println('3 : invoke')
        // metaClass  是谁的? 闭包默认的策略是Closure.OWNER_FIRST  (回去看lsn4)
        // 所以 这个闭包的OWNER 是 lsn8_1 的metaClass 不是 zeking3的 metaClass
        // 所以 不能自己使用metaClass 要使用代理
        // 默认的闭包他的 delegate 和 owner 是一样的
        // 但是 覆盖方法使用metaClass,他的 delegate会变成调用metaClass的对象或者类,即zeking3的
        def method = delegate.metaClass.getMetaMethod(name,args) // 用另一种方法,也可以继续使用 respondsTo
        if (method)
            method.invoke(delegate,args)
        


zeking3.dream()              // 3 : invoke
                             // 3 : replace dream

println '=================1.4=============================='
// 1.4
//  不仅可以改变自己写的类 或者GDK的类,还可以改变 JDK的类

String.metaClass.plus = 
    CharSequence i ->
        i


println  "123"+'abc'        // abc

println '==============2.1================================='
// 2.1
class Zeking10

    def dream()
        println "10 : Zekign's dream"
    

// 2.
def zeking10 = new Zeking10()
zeking10.dream()              // 10 : Zekign's dream
Zeking10.metaClass.dream =   // 用一个闭包替换掉
    println '10 : replace dream'

zeking10.dream()              // 10 : Zekign's dream
new Zeking10().dream()        // 10 : replace dream

//println '==============================================='

3. propertyMissing methodMissing

// 1.propertyMissing  methodMissing
class Man


    def propertyMissing(String name) 
        return null
    

    def propertyMissing(String name, def arg) 

    

    def methodMissing(String name, def args) 
        println 'methodMissing'
        return 'dream'  // 这个返回值是干嘛的,
    



def man = new Man()
man.dream()             // methodMissing
println man.dream()     // methodMissing
                        // dream
// 如果在一个类上实现了 methodMissing 方法的话,
// 我们  在一个类上调用不存在的方法,他会为
// 我们 调用到 methodMissing 方法
// 成员属性就是propertyMissing

4. 模拟 命令行输入,动态改变方法

class Man2

    def dream()
        println 'Man2 dream()'
    

    def propertyMissing(String name) 
        return null
    

    def propertyMissing(String name, def arg) 

    

    def methodMissing(String name, def args) 
        println 'methodMissing'
        return 'dream'  // 这个返回值是干嘛的,
    



def man2 = new Man2()

def scanner = new Scanner(System.in)
Thread.start 
    while (true)
        def msg = scanner.nextLine()
        if (msg == 'exit')
            break
        

        if (man2.respondsTo(msg))
            man2."$msg"()
        else 
            def (name,body) = msg.split(":")
            man2.metaClass."$name" = 
                evaluate(body)
            
        
    

以上是关于Groovy08_MOP与元编程(方法拦截) ```的主要内容,如果未能解决你的问题,请参考以下文章

Groovy09_MOP与元编程(方法注入)

Groovy09_MOP与元编程(方法注入)

GroovyMOP 元对象协议与元编程 ( 方法合成引入 | 类内部获取 HandleMetaClass )

GroovyMOP 元对象协议与元编程 ( 方法委托 | 使用 @Delegate 注解进行方法委托 )

GroovyMOP 元对象协议与元编程 ( 使用 Groovy 元编程进行函数拦截 | 使用 MetaClass 进行方法拦截 | 对象上拦截方法 | 类上拦截方法 )

GroovyMOP 元对象协议与元编程 ( 方法注入 | 使用 Category 分类进行方法注入的优缺点 )