• 用cglib包来为类产生动态代理类对象


    一:在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>1010993610@qq.com</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;

  • 相关阅读:
    eclipse加载maven工程提示pom.xml无法解析
    tensorflow入门——5tensorflow安装
    tensorflow入门——3解决问题——4让我们开始吧
    神经网络入门——12梯度下降代码
    神经网络入门——11梯度下降算法
    如何避免梯度下降到局部最小值
    神经网络入门——10.梯度下降
    神经网络入门——9.简单神经网络
    神经网络入门——8XOR感知器
    神经网络入门——7or 感知器
  • 原文地址:https://www.cnblogs.com/silentdoer/p/8908557.html
Copyright © 2020-2023  润新知