org.springframework.beans简单解读

本文探讨了Spring框架中用于操作JavaBean的核心包,详细解析了BeanWrapper接口及其实现类BeanWrapperImpl的功能,介绍了PropertyEditor及其具体实现ClassEditor的工作原理,并讨论了CachedIntrospectionResults类的作用和实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    这个包的说明是说主要是包括用于操作JavaBean的类和接口,将被大部分spring包使用。在读这个包的代码前,我特意将JavaBean规范读了一遍。JavaBean规范不仅仅是getter、setter,定义了一个完整的轻量级组件模型,事件、方法、属性、持久化等等支持均包含在内。JavaBean规范很明显是学习Delphi的组件模型,sun希望通过它来形成一个java组件的市场,可惜结果不如人意,JavaBean在GUI方面并未形成一个类似delphi控件市场;随着spring等轻量级框架的流行,而EJB重量级的组件模型被越来越多的人放弃,JavaBean反而在服务端模型方面占据了主流 。废话不提,这个包的核心接口和类就是BeanWrapper和BeanWrapperImpl,顾名思义,这个接口就是用于包装JavaBean的行为,诸如设置和获取属性,设置属性编辑器等(PropertyEditor)。看下这个包的核心类图:
spring-bean.jpg
BeanWrapper接口继承了PropertyAccessor(用于属性访问和设置)和PropertyEditorRegistry(属性编辑器的获取和设置),而BeanWrapperImpl除了实现BeanWrapper接口外还继承自PropertyEditorRegistrySupport 类。在PropertyEditorRegistrySupport 类中可以看到spring默认设置的一系列自定义PropertyEditor。比如:
protected   void  registerDefaultEditors() {
        
this .defaultEditors  =   new  HashMap( 32 );

        
//  Simple editors, without parameterization capabilities.
        
//  The JDK does not contain a default editor for any of these target types.
         this .defaultEditors.put(Class. class new  ClassEditor());
        
this .defaultEditors.put(File. class new  FileEditor());
        
this .defaultEditors.put(InputStream. class new  InputStreamEditor());
        
this .defaultEditors.put(Locale. class new  LocaleEditor());
        
this .defaultEditors.put(Properties. class new  PropertiesEditor());
        
this .defaultEditors.put(Resource[]. class new  ResourceArrayPropertyEditor());
        
this .defaultEditors.put(String[]. class new  StringArrayPropertyEditor());
        
this .defaultEditors.put(URL. class new  URLEditor());

。。。。。。。

    PropertyEditor的概念就是属性编辑器,或者说属性转换器,比如我们在spring的配置文件中设置某个bean的class,这是一个字符串,怎么转换为一个Class对象呢?通过上面注册的ClassEditor,看看这个类是怎么实现的:

public   class  ClassEditor  extends  PropertyEditorSupport {

    
private   final  ClassLoader classLoader;

    /**
     * Create a default ClassEditor, using the given ClassLoader.
     * 
@param classLoader the ClassLoader to use
     * (or <code>null</code> for the thread context ClassLoader)
     
*/
    
public ClassEditor(ClassLoader classLoader) {
        
this.classLoader =
                (classLoader 
!= null ? classLoader : Thread.currentThread().getContextClassLoader());
    }


    
public void setAsText(String text) throws IllegalArgumentException {
        
if (StringUtils.hasText(text)) {
            
try {
                //调用辅助类,得到Class对象
                setValue(ClassUtils.forName(text.trim(), 
this.classLoader));
            }
            
catch (ClassNotFoundException ex) {
                
throw new IllegalArgumentException("Class not found: " + ex.getMessage());
            }
        }
        
else {
            setValue(
null);
        }
    }

    
public String getAsText() {
        Class clazz 
= (Class) getValue();
        
if (clazz == null) {
            
return "";
        }
        
if (clazz.isArray()) {
            
return clazz.getComponentType().getName() + ClassUtils.ARRAY_SUFFIX;
        }
        
else {
            
return clazz.getName();
        }
    }

}
    代码已经解释了一切,继承javabean的PropertyEditorSupport,自己实现转换即可。这个包另外就是定义了一个完整的异常体系,值的我们参考。另外一个值的注意的地方是CachedIntrospectionResults类的实现,这个类使用了单例模式,它的作用在于缓存JavaBean反省(Introspect)得到的信息,因为每次使用Introspector对获取JavaBean信息是个不小的性能开支。缓存使用的是WeakHashMap,而不是HashMap,看看spring的解释:
/**
     * Map keyed by class containing CachedIntrospectionResults.
     * Needs to be a WeakHashMap with WeakReferences as values to allow
     * for proper garbage collection in case of multiple class loaders.
     
*/
    
private   static   final  Map classCache  =  Collections.synchronizedMap( new  WeakHashMap());

因为缓存使用的key是bean的Class对象(以保证唯一性),因此在应用存在多个class loaders的时候,为了保证垃圾收集的进行,不出现内存泄露而采用WeakHashMap,为了理解这一点,我用JProfiler测试了自定义ClassLoader情况下,内存堆的使用情况,从快照上看。在使用HashMap的情况下,因为测试的bean的Class对象被载入它的ClassLoader以及java.beans.BeanInfo,java.beans.PropertyDescriptor,java.lang.reflect.Method这四个对象强引用,而导致不可回收。而在使用WeakHashMap时,判断当载入bean的ClassLoader和载入CachedIntrospectionResults的ClassLoader是不同的时候,使用弱引用包装缓存对象,保证能被回收。请看:
private   static   boolean  isCacheSafe(Class clazz) {
        
// CachedIntrospectionResults的ClassLoader
        ClassLoader cur  =  CachedIntrospectionResults. class .getClassLoader();
        
// 载入bean的ClassLoader
        ClassLoader target  =  clazz.getClassLoader();
        
if  (target  ==   null   ||  cur  ==  target) {
            
return   true ;
        }
        
while  (cur  !=   null ) {
            cur 
=  cur.getParent();
            
if  (cur  ==  target) {
                
return   true ;
            }
        }
        
return   false ;
    }

public   static  CachedIntrospectionResults forClass(Class beanClass)  throws  BeansException {

   dot.gifdot.gif
   
boolean  cacheSafe  =  isCacheSafe(beanClass);
   
if  (cacheSafe) {
                classCache.put(beanClass, results);
            }
            
else  {
           // 弱引用   
            classCache.put(beanClass, new WeakReference(results));

            }
   dot.gifdot.gif

    不知道我的理解是否有误,如果有误,请不吝指出,谢谢。

110889.html

dennis 2007-04-16 10:23 发表评论
springboot项目单元测试时,报java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@727320fa testClass = com.example.lpro.mybatisPlus.api.SyUserControllerTest, locations = [], classes = [com.example.lpro.LProApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [[ImportsContextCustomizer@2935fd2c key = [@org.springframework.context.annotation.Import(value={org.mybatis.spring.annotation.MapperScannerRegistrar.class}), @org.mybatis.spring.annotation.MapperScan(annotationClass=java.lang.annotation.Annotation.class, basePackageClasses={}, basePackages={"com.example.lpro.mapper"}, defaultScope="", factoryBean=org.mybatis.spring.mapper.MapperFactoryBean.class, lazyInitialization="", markerInterface=java.lang.Class.class, nameGenerator=org.springframework.beans.factory.support.BeanNameGenerator.class, processPropertyPlaceHolders=true, sqlSessionFactoryRef="", sqlSessionTemplateRef="", value={"com.example.lpro.mapper"}), @org.springframework.boot.test.context.SpringBootTest(args={}, classes={}, properties={}, useMainMethod=NEVER, value={}, webEnvironment=MOCK), @org.apiguardian.api.API(consumers={"*"}, since="5.0", status=STABLE), @org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.context.SpringBootTestContextBootstrapper.class)]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@68c9133c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@5b218417, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@6c284af, org.springframework.boot.test.web.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory$DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer@6be968ce, org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory$OnFailureConditionReportContextCustomizer@1033576a, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@6c4906d3, org.springframework.boot.test.context.SpringBootTestAnnotation@129c1300], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null] 请帮我分析错误原因
06-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值