1. 程式人生 > >用cglib包來為類產生動態代理類對象

用cglib包來為類產生動態代理類對象

方法 source uil owa pac 類對象 進行 desc clas

一:在JDK裏也有動態代理的類和接口,是Proxy和InvocationHandler,但是Proxy只能為接口產生代理類,借助InvocationHandler的實現類來完成對類對象的代理;

但是在Spring裏可以為沒有實現接口的類進行aop編程,這時候要模擬則可以借助cglib的Enhancer類和MethodInterceptor接口來實現;

下面的代碼實現裏有很多即興的註釋,諸君可忽略;

二:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.silentdoer</groupId> <artifactId>demo-test-something</
artifactId> <version>0.0.1-SNAPSHOT</version> <!--<packaging>war</packaging>--> <name>Silentdoer</name> <description>描述</description> <developers> <developer> <id>Silentdoer</id>
<name>Mr.Wang</name> <email>[email protected]</email> <roles> <role>C# Engineer</role> <role>Java Engineer</role> </roles> </developer> </developers> <dependencies> <!-- tool begin --> <dependency> <groupId>${alibaba.group}</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>${dom4j.group}</groupId> <artifactId>dom4j</artifactId> <version>${dom4j.version}</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.9</version> </dependency> <!-- 這個版本的可以不用再添加asm的jar包 --> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.1</version> </dependency> <!-- tool end --> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>

三:代碼實現

1.advice的實現

package me.silentdoer.cglibusage.proxy.method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/22/18 2:35 PM
 */
// 和Spring中的 aopalliance 的 MethodInterceptor 類似,不過aopalliance接口的是invoke方法且參數只有一個MethodInvocation
public class TestAdvice implements MethodInterceptor {
    //private Object target;  // TODO 如果target是用默認構造方法創建對象後不需要額外操作,那麽此屬性是可以忽略的;
    // 通過添加一個map(可以自己擴展一個更符合tx:advice定義的結構)作為註冊表可以實現txAdvice的傳播特性
    // ,原理是intercept裏獲得當前方法名然後和map的String匹配看是什麽傳播特性,然後做相關的操作
    private Map<String, Object> attributes;
    // Mybatis的實現Mapper接口也是用類似的方式實現的,即這裏有個map,裏面存儲了方法名和對應的statement之間的關聯
    //,然後執行Mapper接口的方法實際上是執行代理類的invoke方法,invoke方法裏獲得當前方法的名稱然後從map裏獲得對應的statement
    //,最終通過sqlSession.selectOne(statement)完成操作;

    // o是Enhancer產生的代理類對象,method是被代理類的對應方法對象,objects是方法參數,methodProxy則是特殊的類似method的對象
    // 這裏之所以需要methodProxy是因為和JDK的Proxy-InvocationHandler不同,Proxy只能為接口產生代理類,而o則是InvocationHandler的屬性
    //,它是和代理類沒有半毛錢直接關系的,執行method.invoke(o...)就是執行被代理類的方法;而Enhancer是能夠為類產生代理類同時產生此類的默認構造
    //,方法的對象,那麽如果被代理類被創建後不需要做其它操作則不應該作為MethodInterceptor對象的屬性而浪費對象,那麽這時候由於o是代理類對象
    //,因此需要一個能夠執行o繼承的類對象的方法,因此需要methodProxy;
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // TODO 可以用cglib實現 aop 編程,這裏其實已經是一個aop編程了,關鍵是通過解析配置,然後轉換後的配置存到此對象的註冊表裏
        // TODO 這裏則根據method名稱和其它一些特性從而在註冊表裏獲取advice並執行before和after方法(它們的默認實現是空實現,因此
        // TODO 自己寫Advice時沒有實現before則此advice就是值有after的
        System.out.println("Before");  // bu shi zhi ru, before ye shi dai li shi xian
        // output: class me.silentdoer.cglibusage.test.TestContext$$EnhancerByCGLIB$$379b92c3
        System.out.println(o.getClass());
        // output: class me.silentdoer.cglibusage.test.TestContext
        System.out.println(method.getDeclaringClass());
        // output: [88]  // 參數列表
        System.out.println(String.format("Args: %s", Arrays.toString(objects)));
        // 這個和Aspectj的JoinPoint很像
        // output: test
        System.out.println(methodProxy.getSignature().getName());
        // 類似aspectj的ProceedingJoinPoint
        // 如果用的是invoke方法會產生遞歸現象,因為執行的是代理類的方法又會間接再執行此intercept方法;
        Object result = methodProxy.invokeSuper(o, objects);
        // 由於多態性o是繼承自method的聲明類的,這種方式也一樣是執行的是o的方法
        //Object result = method.invoke(o, objects);
        System.out.println("After");
        return result;
    }

    public void setAttributes(Map<String, Object> attributes) {
        this.attributes = attributes;
    }
}

2.被代理類

package me.silentdoer.cglibusage.test;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/22/18 2:26 PM
 */
public class TestContext {
    public String test(long s){
        System.out.println(String.format("Hello:%s", s));
        return "AAAAAa" + s;
    }
}

3.main方法

package me.silentdoer.cglibusage;

import me.silentdoer.cglibusage.proxy.method.TestAdvice;
import me.silentdoer.cglibusage.test.TestContext;
import net.sf.cglib.proxy.Enhancer;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/22/18 2:25 PM
 */
public class Entrance {
    public static void main(String[] args){
        TestContext context = new TestContext();
        System.out.println(context.test(88L));
        // 如果被代理的TestContext有特殊性則應該改寫TestAdvice,改成和InvocationHandler的實現方式引用一個target;
        TestContext testContext = ((TestContext) Enhancer.create(TestContext.class, new TestAdvice()));
        System.out.println(testContext.test(88L));
    }
}

通過這個類是可以實現aop功能的,spring內部也很多地方用到了這個包的api;

用cglib包來為類產生動態代理類對象