动态代理管理bean二种方式

作者: Ian | 2019-02-17 | 阅读

转载 何锦彬:http://www.cnblogs.com/springsource/archive/2012/08/30/2664050.html

JAVA 代理实现

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

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

JDK自带实现方法

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

EX:

public class JDKProxy {

      public static Object getPoxyObject(final Object c) {
    
            return Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(),// JDK实现动态代理,但JDK实现必须需要接口
    
                        new InvocationHandler() {
    
                             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 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. 生成出来的代理类为接口实现类,和目标类不能进行转换,只能转为接口实现类进行调用
  3. 明显特点:通过此方法生成出来的类名叫做 $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向上转换
  3. 明显特点:通过输出看出,看出生成出的代理类的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

commons\commons-logging-1.1.1\commons-logging-1.1.1.jar

aopalliance\aopalliance.jar

lib\aspectjweaver.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框架中,动态代理的策略是如果被拦截的方法,是接口中定义的方法,以jdk动态代理生成代理对象,实现通知;否则,使用cglib进行生成子类代理实例,实现通知;事实上,无论是否在接口中定义的方法,均可使用cglib生成动态代理对象,完成拦截和通知,是更通用的方式,但由于jdk动态代理的性能更佳,因此spring框架中优先选择jdk动态代理技术。

  • spring实现aop,动态代理技术的两种实现是jdk动态代理、cglib代理,根据被通知的方法是否为接口方法,来选择使用哪种代理生成策略

  • jdk动态代理,原理是实现接口的实例,拦截定义于接口中的目标方法,性能更优,是spring生成代理的优先选择

  • cglib代理,原理是使用cglib库中的字节码动态生成技术,生成被代理类的子类实例,可以拦截代理类中的任一public方法的调用,无论目标方法是否定义于接口中,更通用,但性能相对jdk代理差一些;



  相关文章:


留言区:

TOP