• [转]JAVA的动态代理机制及Spring的实现方式


    JAVA 代理实现

    代理的实现分动态代理和静态代理,静态代理的实现是对已经生成了的JAVA类进行封装。

    动态代理则是在运行时生成了相关代理累,在JAVA中生成动态代理一般有两种方式。

    JDK自带实现方法

    JDK实现代理生成,是用类 java.lang.reflect.Proxy, 实现方式如下

    EX:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Calendar;
    
    public class JDKProxy {
    
        public static Object getPoxyObject(final Object c) {
    
            return Proxy.newProxyInstance(c.getClass().getClassLoader(), c
                    .getClass().getInterfaces(), new InvocationHandler() {
    
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    // TODO Auto-generated method stub
                    Object reObj = null;
                    System.out.print("you say: ");
                    reObj = method.invoke(c, args);
                    System.out.println(" ["
                            + Calendar.getInstance().get(Calendar.HOUR) + ":"
                            + Calendar.getInstance().get(Calendar.MINUTE) + " "
                            + Calendar.getInstance().get(Calendar.SECOND) + "]");
    
                    return reObj;
                }
    
            });
    
        }
    
    }
    public class ServiceTestImpl implements ServiceTest{
        @Override
        public void saySomething(String a) {
            System.out.println(a);    
        }
    }
    public interface ServiceTest {
        public void saySomething(String a);
    }

    测试代理类方法

    public class TestForPoxy {
    
          public static void main(String[] args) {
    
                ServiceTest service = new ServiceTestImpl();
    
                System.out.println(service.getClass().getSimpleName());
    
                ServiceTest poxyService = (ServiceTest) JDKProxy.getPoxyObject(service);
    
                System.out.println(poxyService.getClass().getSuperclass());
    
                poxyService.saySomething("hello,My QQ code is 107966750.");
    
                poxyService.saySomething("what 's your name?");
    
                poxyService.saySomething("only for test,hehe.");
    
          }
    
    }

    1, Proxy实现代理的目标类必须有实现接口

    2, 生成出来的代理类为接口实现类,和目标类不能进行转换,只能转为接口实现类进行调用

    明显特点:通过此方法生成出来的类名叫做 $Proxy0

    用CGLIB包实现

    CGLIB是一个开源项目,官方网址是:http://cglib.sourceforge.net/,可以去上面下载最新JAR包,

    本项目用的是cglib-3.0.jar

    本项目还加入了依赖JAR包asm-4.0.jar,asm-util-4.0.jar

    实现方式如下

    EX:

    public class CGLIBProxy {
    
          public static Object getPoxyObject(Object c) {
    
                Enhancer enhancer = new Enhancer();
    
                enhancer.setSuperclass(c.getClass());
    
                enhancer.setCallback(new MethodInterceptor() {
    
                      public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
    
                            System.out.print("you say: ");
    
                            proxy.invokeSuper(arg0, arg2);
    
                            System.out.println(" [" + Calendar.getInstance().get(Calendar.HOUR) + ":"
    
                                        + Calendar.getInstance().get(Calendar.MINUTE) + " " + Calendar.getInstance().get(Calendar.SECOND)
    
                                        + "]");
    
                            return null;
    
                      }
    
                });
    
                return enhancer.create();
    
          }
    
    }

    测试代理类方法

    public class TestForPoxy {
    
          public static void main(String[] args) {
    
                ServiceTest service = new ServiceTestImpl();
    
                System.out.println(service.getClass().getSimpleName());
    
    //          ServiceTest poxyService = (ServiceTest) JDKProxy.getPoxyObject(service);
    
                ServiceTest poxyService = (ServiceTest) CGLIBProxy.getPoxyObject(service);
    
                System.out.println(poxyService.getClass().getSuperclass());
    
                poxyService.saySomething("hello,My QQ code is 107966750.");
    
                poxyService.saySomething("what 's your name?");
    
                poxyService.saySomething("only for test,hehe.");
    
          }
    
    }

    1, CGLIB实现方式是对代理的目标类进行继承

    2, 生成出了的代理类可以没方法,生成出来的类可以直接转换成目标类或目标类实现接口的实现类,因JAVA向上转换

    明显特点:通过输出看出,看出生成出的代理类的parent类为代理的目标类

    Spring  AOP的代理类机制分析

    在spring中,bean都是由动态代理生成出来的,那么到底是用JDK的Proxy类实现呢,还是用CGLIB方式实现呢。

    AOP  Spring需要的依赖JAR包有:

    spring-asm-3.2.0.M1.jar

    spring-beans-3.2.0.M1.jar

    spring-context-3.2.0.M1.jar

    spring-core-3.2.0.M1.jar

    spring-expression-3.2.0.M1.jar

    spring-aop-3.2.0.M1.jar

    spring-aspects-3.2.0.M1.jar

    commonscommons-logging-1.1.1commons-logging-1.1.1.jar

    aopallianceaopalliance.jar

    libaspectjweaver.jar

    实现AOP

    先简单的实现AOP

    配置如下

    <?xml version="1.0" encoding="utf-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
    
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    
          xsi:schemaLocation="
    
          http://www.springframework.org/schema/beans
    
          http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    
          http://www.springframework.org/schema/aop 
    
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    
          <bean id="test" class="org.ben.spring.service.Test" />
    
          <bean id="aspectBean" class="org.ben.spring.TestAspect" />
    
          <!-- 对Test类进行AOP拦截 -->
    
          <aop:config>
    
                <aop:aspect id="TestAspect" ref="aspectBean">
    
                      <!--配置切面-->
    
                      <aop:pointcut id="businessService"
    
                            expression="execution(* org.ben.spring.service.Test.say(..))" />
    
                      <aop:before pointcut-ref="businessService" method="doBefore" />
    
                      <aop:after pointcut-ref="businessService" method="doAfter" />
    
                </aop:aspect>
    
          </aop:config>
    
    </beans>

    然后进行运行结果如下,表示AOP拦截成功

    AOP测试类
    
    public class TestBeans {
    
          public static void main(String[] args) {
    
                ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");
    
                Test test=(Test) ctx.getBean("test");
    
                System.out.println(test.getClass().getSimpleName());
    
                test.say();
    
          }
    
    }

    输出:

    do something in befor

    welcome for test

    do something in after

    打印代理类的生成方式

    第一种情况, Test不实现任何接口,代码如下
    
    public class Test {
    
          public void say() {
    
                System.out.println("welcome for test,My QQ is 107966750");
    
          }
    
    }

    在TestBeans中加入打印当前对象的名称

    如下:

    ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");
    
    Test test=(Test) ctx.getBean("test");
    
    System.out.println(test.getClass().getSimpleName());
    
    test.say();
    
    输出:
    
    Test$$EnhancerByCGLIB$$4791b36c
    
    super class is class org.ben.spring.service.Test
    
    do something in befor
    
    welcome for test
    
    do something in after

    明显看到用了AOP之后,输出的是代理类对象Test$$EnhancerByCGLIB$$bb9b6a7c.而且它的父类是我们的代理目标类。说明是有CGLIB生成的

    第二种情况

     

    XML的配置不变,改变代理目标类Test的实现方法,如下

    public class Test implements TestInter{
    
          public void say() {
    
                System.out.println("welcome for test,My QQ is 107966750");
    
          }
    
    }

    和原来不同的是多继承了一个接口,接口中定义了say()方法

    在TestBeans中加入打印当前对象的名称

    如下:

    ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");
    
    TestInter test=(TestInter) ctx.getBean("test");
    
    System.out.println(test.getClass().getSimpleName());
    
    System.out.println("super class is "+test.getClass().getSuperclass());
    
    test.say();

    输出:

    $Proxy0

    super class is class java.lang.reflect.Proxy

    do something in befor

    welcome for test,My QQ is 107966750

    do something in after

    结论

    Spring AOP中,当拦截对象实现了接口时,生成方式是用JDK的Proxy类。当没有实现任何接口时用的是GCLIB开源项目生成的拦截类的子类.

    转:http://www.cnblogs.com/springsource/archive/2012/08/30/2664050.html

  • 相关阅读:
    10个最常见的JS错误
    有哪些新手程序员不知道的小技巧?
    有哪些新手程序员不知道的小技巧?
    有哪些新手程序员不知道的小技巧?
    有哪些新手程序员不知道的小技巧?
    有哪些新手程序员不知道的小技巧?
    有哪些新手程序员不知道的小技巧?
    EF 传递的主键值的数量必须与实体上定义的主键值的数量匹配 原因
    ACM2054_A=B
    五一游记
  • 原文地址:https://www.cnblogs.com/crazylqy/p/5598271.html
Copyright © 2020-2023  润新知