spring源码分析

Easter79
• 阅读 656

     在spring源码中,在很多地方都会出现PropertyEditor,那么属性编辑器用来干什么的呢?属性编辑器主要应用在以下两个方面:

  • 使用PropertyEditors设置Bean属性。当你在XML文件中声明的bean的属性类型为java.lang.String时,Spring将在容器初始化该bean时使用ClassEditor将String解析成Class对象(如果setter方法需要一个Class参数的话)。这么说有点难以理解,请看下面这个代码片段:

    public class ClassHolder { private Class clazz; public Class getClazz() { return clazz; } public void setClazz(Class<?> clazz) { this.clazz = clazz; } }

  • 在Spring MVC架构中使用各种PropertyEditors来解析HTTP请求中的参数。

  Spring提供了许多内建的PropertyEditors可以简化我们的工作,它们都位于org.springframework.beans.PropertyEditors包内。它们中的大多数已经默认在BeanWrapperImpl的实现类中注册好了。作为可配置的选项,我们也可以注册自己的属性编辑器实现去覆盖那些默认编辑器。

    由于PropertyEditor是spring源码分析的基础,于是决定研究一下PropertyEditor。在探索的过程中,发现了下面这篇分析PropertyEditor比较好的文章,就不想再造轮子了。后面还会写一篇关于怎么 在spring中使用自定义PropertyEditor的文章(https://my.oschina.net/thinwonton/blog/1492107),用于补充下面的内容。

=============================================================

    在spring配置文件或配置类里,通过字符串为Bean各种属性提供设置值:不管是double类型还是int类型,在配置文件中都对应字符串类型。BeanWrapper填充Bean属性时如何将这个字面值转换为对应的double或int等内部类型呢?我们可以隐约地感觉到一定有一个转换器在其中起作用,这个转换器就是属性编辑器。

1、PropertyEditor

    java.beans.PropertyEditor是属性编辑器的接口,它规定了将外部设置值转换为内部JavaBean属性值的转换接口方法。PropertyEditor主要的接口方法说明如下: 

  • Object getValue():返回属性的当前值。基本类型被封装成对应的封装类实例; 
  •  void setValue(Object newValue):设置属性的值,基本类型以封装类传入; 
  • String getAsText():将属性对象用一个字符串表示,以便外部的属性编辑器能以可视化的方式显示。缺省返回null,表示该属性不能以字符串表示; 
  •  void setAsText(String text):用一个字符串去更新属性的内部值,这个字符串一般从外部属性编辑器传入; 
  •  String[] getTags():返回表示有效属性值的字符串数组(如boolean属性对应的有效Tag为true和false),以便属性编辑器能以下拉框的方式显示出来。缺省返回null,表示属性没有匹配的字符值有限集合; 
  •  String getJavaInitializationString():为属性提供一个表示初始值的字符串,属性编辑器以此值作为属性的默认值。

    可以看出PropertyEditor接口方法是内部属性值和外部设置值的沟通桥梁。此外,我们可以很容易地发现该接口的很多方法是专为IDE中的可视化属性编辑器提供的:如getTags()、getJavaInitializationString()以及另外一些我们未此介绍的接口方法。

PropertyEditorSupport

    Java为PropertyEditor提供了一个方便类:PropertyEditorSupport,该类实现了PropertyEditor接口并提供默认实现,一般情况下,用户可以通过扩展这个方便类设计自己的属性编辑器。

spring中的属性编辑器

    Spring大部分默认属性编辑器都直接扩展于java.beans.PropertyEditorSupport类,用户也可以通过扩展PropertyEditorSupport实现自己的属性编辑器。比起用于IDE环境的属性编辑器来说,Spring环境下使用的属性编辑器的功能非常单一:仅需要将配置文件中字面值转换为属性类型的对象即可,并不需要提供UI界面,因此仅需要简单覆盖PropertyEditorSupport的setAsText()方法就可以了。 

举个栗子:

实现 UUIDEditor   

public class UUIDEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (StringUtils.hasText(text)) {
            setValue(UUID.fromString(text));
        }
        else {
            setValue(null);
        }
    }

    @Override
    public String getAsText() {
        UUID value = (UUID) getValue();
        return (value != null ? value.toString() : "");
    }

}

public static void main(String[] args) throws ClassNotFoundException, IOException {

        //UUID对象转化为字符串
        UUIDEditor editor=new UUIDEditor();
        editor.setValue(UUID.randomUUID());
        System.out.println(editor.getAsText());//c2878055-fb49-4559-a7db-c60fc3ebee79

        //字符串转化为UUID
        UUIDEditor editor_2=new UUIDEditor();
        editor_2.setAsText("2-1-1-2-3");
        System.out.println(editor_2.getAsText());//00000002-0001-0001-0002-000000000003
        System.out.println(editor_2.getValue().getClass());//class java.util.UUID

}

2、PropertyEditorRegistry

这个接口可以注册和保存属性编辑器

public interface PropertyEditorRegistry {

    void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

    void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);

    PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);

}

PropertyEditorRegistrySupport

PropertyEditorRegistrySupport是PropertyEditorRegistry 默认实现类 
PropertyEditorRegistrySupport中有两个用于保存属性编辑器的Map类型变量:

private Map<Class<?>, PropertyEditor> defaultEditors;

private Map<Class<?>, PropertyEditor> customEditors;

PropertyEditorRegistrySupport通过以下的代码定义默认属性编辑器:

    private void createDefaultEditors() {
        this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);

        // Simple editors, without parameterization capabilities.
        // The JDK does not contain a default editor for any of these target types.
        this.defaultEditors.put(Charset.class, new CharsetEditor());
        this.defaultEditors.put(Class.class, new ClassEditor());
        this.defaultEditors.put(Class[].class, new ClassArrayEditor());
        this.defaultEditors.put(Currency.class, new CurrencyEditor());
        this.defaultEditors.put(File.class, new FileEditor());
        this.defaultEditors.put(InputStream.class, new InputStreamEditor());
        this.defaultEditors.put(InputSource.class, new InputSourceEditor());
        this.defaultEditors.put(Locale.class, new LocaleEditor());
        this.defaultEditors.put(Pattern.class, new PatternEditor());
        this.defaultEditors.put(Properties.class, new PropertiesEditor());
        this.defaultEditors.put(Reader.class, new ReaderEditor());
        this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
        this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
        this.defaultEditors.put(URI.class, new URIEditor());
        this.defaultEditors.put(URL.class, new URLEditor());
        this.defaultEditors.put(UUID.class, new UUIDEditor());
        if (zoneIdClass != null) {
            this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
        }

        // Default instances of collection editors.
        // Can be overridden by registering custom instances of those as custom editors.
        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

        // Default editors for primitive arrays.
        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

        // The JDK does not contain a default editor for char!
        this.defaultEditors.put(char.class, new CharacterEditor(false));
        this.defaultEditors.put(Character.class, new CharacterEditor(true));

        // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
        this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

        // The JDK does not contain default editors for number wrapper types!
        // Override JDK primitive number editors with our own CustomNumberEditor.
        this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
        this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
        this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
        this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
        this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
        this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
        this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
        this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

        // Only register config value editors if explicitly requested.
        if (this.configValueEditorsActive) {
            StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
            this.defaultEditors.put(String[].class, sae);
            this.defaultEditors.put(short[].class, sae);
            this.defaultEditors.put(int[].class, sae);
            this.defaultEditors.put(long[].class, sae);
        }
    }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Spring 学习笔记(三):Spring Bean
1Bean配置Spring可以看做是一个管理Bean的工厂,开发者需要将Bean配置在XML或者Properties配置文件中。实际开发中常使用XML的格式,其中<bean中的属性或子元素如下:id:Bean在BeanFactory中的唯一标识,在代码中通过BeanFac
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
Spring高级应用之注入嵌套Bean
在Spring中,如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。和普通的Bean一样,使用<bean元素来定义嵌套的Bean,嵌套Bean只对它的外部的Bean有效,Spring容器无法直接访问嵌套的Bean,因此定义嵌套Bean也无需指定id属性。如下配置片段是一个嵌套Bean的示例:<bean id
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k