`
talentkep
  • 浏览: 100516 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Spring 在进行反射时候主要有两种策略

    博客分类:
阅读更多

 

Spring 在进行反射时候主要有两种策略,一种是直接用 JDK 的反射,另外是用 CgLib 

cglib是一个开源项目, 是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO字节码的动态生成。cglib项目主页: http://cglib.sourceforge.net/

CgLib 底层用的 asm

Spring-CGLIB 

Asm 是一个强大的 Java 字节码生成框架,和 BCEL 或者 SERP 很类似,但是小很多,可以动态修改 java 字节码 .

其中最核心的代码为

newProxyInstance (obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), InvocationHandler invocationHandler);

 obj.getClass().getInterfaces() 可以看出,如果直接用 JDK 的反射需要创建接口,接口是用来搞架构的,但是对于非常非常小的项目去写接口有点麻烦。

 CgLib 就可以不用接口,它底层调用 asm 动态生成一个代理类去覆盖父类中非 final 的方法,然后实现 MethodInterceptor 接口的 intercept 方法,这样以后直接调用重写的方法,比 JDK 要快。

但是加载 cglib 消耗时间比直接 jdk 反射时间长,开发的过程中,如果是反复动态生成新的代理类推荐用 jdk 自身的反射,反之用 cglib.

Spring  Bean 工厂中有一个方法

public void setOptimize (boolean optimize)- 是否使用 CGLIB 代理优化策略 . 仅用于 CGLIB 
代理 ; 对于 JDK 动态代理 ( 缺省代理 ) 无效 .


下面是一个 demo( HelloWorld 

Jar  : cglib-2.2.jar, asm-all-3.1

Helloworld 方法

package com.greysh.cglib;

/**

 * @author Genix.Cao

 */

public class HelloWorld {

       public void say() {

              System.out.println("Hello World");

       }

}

代理类

package com.greysh.cglib;

/**

 * @author Genix.Cao

 */

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

       @Override

       public Object intercept(Object object, Method method, Object[] args,

                     MethodProxy methodProxy) throws Throwable {

              System.out.println("Before Helloworld~");

        methodProxy.invokeSuper(object, args);

              System.out.println("After Helloworld~");

              return null;

       }

}

测试类

package com.greysh.cglib;

/**

 * @author Genix.Cao

 */

import net.sf.cglib.proxy.Enhancer;

public class TestCglib {

       public static void main(String[] args) {

              Enhancer enhancer = new Enhancer();

           enhancer.setSuperclass(HelloWorld.class);

           enhancer.setCallback(new CglibProxy());

           HelloWorld helloWorld = (HelloWorld) enhancer.create();

           helloWorld.say();

       }

}

Cglib 使用起来学习路线还是很低的

原理是实现 MethodInterceptor 这个接口

System.out.println("Before Helloworld~");

methodProxy.invokeSuper(object, args);

System.out.println("After Helloworld~");

这里就是 AOP 的思想

然后去装载的时候先要生成一个 Enhancer 

这个类的作用是先设定被代理类

enhancer.setSuperclass(HelloWorld.class);

然后实例化代理类

enhancer.setCallback(new CglibProxy());

HelloWorld helloWorld = (HelloWorld) enhancer.create();

这样被调用的类就是被 asm 动态改变改变后的类

 

Spring  proxy 有趣问题:

Java代码 复制代码
  1. <BR>public class UserDAOImpl{   
  2. <BR><BR>    public void save() {   
  3. <BR>        // TODO Auto-generated method stub   
  4. <BR>        System.out.println("user saved");   
  5. <BR>    }   
  6. <BR>}   
  7. <BR>//相关配置,省略了一些不相关内容   
  8. <BR><bean id="userDAO" class="UserDAOImpl">   
  9. <BR><bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">    
  10. <BR>    <property name="target">    
  11. <BR>        <ref local="userDAO" />    
  12. <BR>    </property>    
  13. <BR></bean>  
public class UserDAOImpl{


	public void save() {

		// TODO Auto-generated method stub

		System.out.println("user saved");

	}

}

//相关配置,省略了一些不相关内容

<bean id="userDAO" class="UserDAOImpl">

<bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 

    <property name="target"> 

        <ref local="userDAO" /> 

    </property> 

</bean>

   测试代码

Java代码 复制代码
  1.         ApplicationContext ctx =   
  2. <BR>            new FileSystemXmlApplicationContext("applicationContext.xml");   
  3. <BR>        UserDAOImpl userDAOImpl =    
  4. <BR>            (UserDAOImpl)ctx.getBean("userDAOProxy");   
  5. <BR>        userDAOImpl.save();  
		ApplicationContext ctx =

			new FileSystemXmlApplicationContext("applicationContext.xml");

		UserDAOImpl userDAOImpl = 

			(UserDAOImpl)ctx.getBean("userDAOProxy");

		userDAOImpl.save();

   上面这种情况下程序可以正常运行,但是如果UserDAOImpl实现了一个接口,其他不变

Java代码 复制代码
  1. public class UserDAOImpl implements UserDAO {   
  2. <BR>   
  3. <BR>    public void save() {   
  4. <BR>        // TODO Auto-generated method stub   
  5. <BR>        System.out.println("user saved");   
  6. <BR>    }   
  7. <BR>   
  8. <BR>}  
public class UserDAOImpl implements UserDAO {



	public void save() {

		// TODO Auto-generated method stub

		System.out.println("user saved");

	}



}

 这种情况下,程序将不能正常运行,会抛出java.lang.ClassCastException异常

理解上面这种情况产生的原因需要了解Spring AOP的实现原理。
Spring 实现AOP是依赖JDK动态代理和CGLIB代理实现的。
以下是JDK动态代理和CGLIB代理简单介绍
    JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
    CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
Spring是依靠什么来判断采用哪种代理策略来生成AOP代理呢?以下代码就是Spring的判断逻辑

Java代码 复制代码
  1.     //org.springframework.aop.framework.DefaultAopProxyFactory   
  2. <BR>    //参数AdvisedSupport 是Spring AOP配置相关类   
  3. <BR>    public AopProxy createAopProxy(AdvisedSupport advisedSupport)   
  4. <BR>            throws AopConfigException {   
  5. <BR>        //在此判断使用JDK动态代理还是CGLIB代理   
  6. <BR>        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()   
  7. <BR>                || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {   
  8. <BR>            if (!cglibAvailable) {   
  9. <BR>                throw new AopConfigException(   
  10. <BR>                        "Cannot proxy target class because CGLIB2 is not available. "  
  11. <BR>                                + "Add CGLIB to the class path or specify proxy interfaces.");   
  12. <BR>            }   
  13. <BR>            return CglibProxyFactory.createCglibProxy(advisedSupport);   
  14. <BR>        } else {   
  15. <BR>            return new JdkDynamicAopProxy(advisedSupport);   
  16. <BR>        }   
  17. <BR>    }  
    //org.springframework.aop.framework.DefaultAopProxyFactory

	//参数AdvisedSupport 是Spring AOP配置相关类

	public AopProxy createAopProxy(AdvisedSupport advisedSupport)

			throws AopConfigException {

		//在此判断使用JDK动态代理还是CGLIB代理

		if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()

				|| hasNoUserSuppliedProxyInterfaces(advisedSupport)) {

			if (!cglibAvailable) {

				throw new AopConfigException(

						"Cannot proxy target class because CGLIB2 is not available. "

								+ "Add CGLIB to the class path or specify proxy interfaces.");

			}

			return CglibProxyFactory.createCglibProxy(advisedSupport);

		} else {

			return new JdkDynamicAopProxy(advisedSupport);

		}

	}

 

 

 

         advisedSupport.isOptimize()advisedSupport.isProxyTargetClass()默认返回都是false,所以在默认情况下目标对象有没有实现接口决定着Spring采取的策略,当然可以设置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回为true,这样无论目标对象有没有实现接口Spring都会选择使用CGLIB代理。所以在默认情况下,如果一个目标对象如果实现了接口Spring则会选择JDK动态代理策略动态的创建一个接口实现类(动态代理类)来代理目标对象,可以通俗的理解这个动态代理类是目标对象的另外一个版本,所以这两者之间在强制转换的时候会抛出j ava.lang.ClassCastException。而所以在默认情况下,如果目标对象没有实现任何接口,Spring会选择CGLIB代理, 其生成的动态代理对象是目标类的子类。

 

 

   

     以上说的是默认情况下,也可以手动配置一些选项使Spring采用CGLIB代理。

org.springframework.transaction.interceptor.TransactionProxyFactoryBeanorg.springframework.aop.framework. ProxyConfig的子类,所以可以参照ProxyConfig里的一些设置如下所示,将optimizeproxyTargetClass任意一个设置为true都可以强制Spring采用CGLIB代理。

Java代码 复制代码
  1. //相关配置,省略了一些不相关内容   
  2. <BR><bean id="userDAO" class="UserDAOImpl">   
  3. <BR><bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">    
  4. <BR>    <property name="target">    
  5. <BR>        <ref local="userDAO" />    
  6. <BR>    </property>    
  7. <BR>    <property name="optimize">    
  8. <BR>        <value>true</value>   
  9. <BR>    </property>   
  10. <BR>    <property name="proxyTargetClass">    
  11. <BR>        <value>true</value>   
  12. <BR>    </property>    
  13. <BR></bean>  
//相关配置,省略了一些不相关内容

<bean id="userDAO" class="UserDAOImpl">

<bean id="userDAOProxy"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 

    <property name="target"> 

        <ref local="userDAO" /> 

    </property> 

    <property name="optimize"> 

        <value>true</value>

    </property>

    <property name="proxyTargetClass"> 

        <value>true</value>

    </property> 

</bean>

 

使用CGLIB代理也就不会出现前面提到的ClassCastException问题了,

也可以在性能上有所提高,但是也有它的弊端,Spring doc原文解释如下optimization will usually mean that advice changes won't  take effect after a proxy has been created. For this reason, optimization  is disabled by default

 

分享到:
评论

相关推荐

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    Spring AOP支持不同的代理策略,包括JDK动态代理和CGLIB代理。如果被代理的类没有实现接口,Spring AOP会采用CGLIB来生成代理对象。CGLIB(Code Generation Library)是一个开源的代码生成库,它允许运行时在内存中...

    Spring常见面试题

    Spring常见面试题 Spring框架是Java平台上的一个开源的软件框架,由Rod Johnson创建,現在由Spring.io维护。Spring框架提供了一个通用的编程模型和配置机制,...有两种使用方式,一种是xml的方式,一种是注解的方式。

    springAOP配置动态代理实现

    1. **JDK动态代理**:Spring在没有CGLIB库的情况下,会使用Java的反射API创建动态代理。动态代理类会实现目标接口,并在调用接口方法时插入通知。因此,使用JDK动态代理的目标类必须实现至少一个接口。 2. **CGLIB...

    Spring + SpringMVC + Mybatis总结,同步博客中的pdf文档

    AOP通过代理模式实现,Spring 提供了基于注解和XML配置两种方式来定义切面。 总之,Spring + SpringMVC + Mybatis 的组合是企业级Java应用中常见的技术栈,它们共同提供了强大的框架支持,帮助开发者构建高效、可...

    分页组件+反射分页组件+反射分页组件+反射

    2. **多数据源支持**:不同数据源可能有不同的分页实现,通过反射,我们可以针对每个数据源编写对应的分页策略,并在运行时根据数据源类型选择合适的策略。 3. **插件化设计**:使用反射,我们可以设计一个插件化的...

    详细的Spring配置和Spring Boot-外文翻译

    本章将介绍一些策略,使你的bean能够在生命周期的各个阶段接收到Spring容器的通知。你可以通过实现Spring定义的特定接口,通过反射指定Spring可以调用的方法,或者使用JSR-250的JavaBeans生命周期注解来实现这一点。...

    Spring框架源码

    在Spring中,AOP是通过动态代理实现的,有两种主要的代理方式:JDK动态代理和CGLIB代理。前者针对实现了接口的类,后者则针对未实现接口的类。源码中,`Advisor`、`Pointcut`、`Aspect`等接口及其实现类定义了切面的...

    Spring系列之依赖注入的三种方式.docx

    【Spring 依赖注入详解】 在 Spring 框架中,依赖注入(Dependency Injection,简称 DI)是一种关键的设计模式,它允许我们解耦...结合使用两种注入策略,可以更好地管理对象的依赖关系,提高代码的可读性和可维护性。

    Spring-MVC处理XSS、SQL注入攻击的方法总结

    本文介绍了两种在 Spring MVC 应用中防御 XSS 和 SQL 注入攻击的方法:数据入库前非法字符转义与显示时非法字符转义,并提供了相应的示例代码。此外,还提到了利用框架内置工具来简化这一过程的可能性。这些方法不仅...

    spring第四天.pdf

    Spring AOP提供了两种动态代理技术: 1. **JDK代理**:如果目标对象实现了接口,Spring会使用Java的动态代理机制,通过反射创建一个代理对象,该代理对象在调用目标对象的方法时会执行通知逻辑。 2. **CGLIB代理**...

    hibernate+spring的配置.docx

    这两种技术的结合使得开发者能够更好地管理和控制数据库操作,同时保持代码的简洁和模块化。下面我们将详细探讨如何配置Hibernate与Spring进行集成,以及它们各自的作用。 首先,Hibernate是一个对象关系映射(ORM...

    16.spring与springmvc常见面试题.docx

    "spring与springmvc常见面试题" ...答:有两种方式:1、编程式事务,在代码中硬编码。(不推荐使用)2、声明式事务,在配置文件中配置(推荐使用)声明式事务又分为两种:基于注解的声明式事务和基于 XML 的声明式事务。

    spring动态代理

    Spring提供了两种动态代理的方式:JDK动态代理和CGLIB代理。 1. JDK动态代理: JDK动态代理基于Java的反射机制,适用于目标对象实现了接口的情况。在Java.lang.reflect包下,有两个关键类:InvocationHandler和...

    实现Spring那样的Ioc注解装配

    总的来说,实现一个类似Spring的IoC注解装配系统是一项复杂但有益的任务,它需要对Java反射、注解处理以及设计模式有深入的理解。这样的系统可以帮助我们构建松耦合、易于测试的代码,提高开发效率。在实际开发中,...

    Spring简单仿写,实现基本IOC,依赖注入和AOP 未解决循环依赖

    在IT行业中,Spring框架是Java开发中的一个核心组件,它主要负责管理对象的生命周期和对象间的依赖关系。本文将深入探讨Spring框架的核心特性——依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-...

    SpringIOC、AOP、注解等相关的详细解释(附有代码贴图)

    例如,Spring的注解解析器会在容器启动时,查找所有的bean,并根据Resource注解来注入依赖,这个过程中涉及到类型匹配和名称匹配两种策略。 通过以上的详细解释,我们理解了Spring框架中IoC容器的运行机制、依赖...

    java之hibernate和spring技术难点及其要点总结

    4. **适配器模式与代理模式**:这两种设计模式在Spring中被广泛应用于实现AOP等功能。 5. **Spring MVC核心概念**: - **核心类和接口**:了解Spring MVC中的关键组件,如`DispatcherServlet`、`HandlerMapping`等...

    JAVA的反射机制与动态代理.pdf

    在Java中,主要有两种动态代理技术: - **JDK动态代理**:基于接口实现的动态代理。使用`java.lang.reflect.Proxy`类和`InvocationHandler`接口实现。 - **CGLIB动态代理**:基于子类继承的动态代理。适用于没有...

Global site tag (gtag.js) - Google Analytics