如何才算滥用annotation?

本文探讨了Java 1.5中引入的注解(annotation)在O/R Mapping、校验及索引三个场景的应用,并展示了如何使用注解简化配置。

原创作者: Readonly   阅读:353次   评论:1条   更新时间:2005-09-19   转自:JavaEye

java1.5引入了annotation,初看起来非常cool,但是仔细想想它可以被用在什么地方呢?什么样的需求才是用annotation的最佳方式呢?

偶先举几个例子吧:
1. O/R Mapping
比如EJB3的持久化映射, 具体就不用说了, 规范里面非常清楚了, 看代码:

Java代码
  1. @NamedQueries({  
  2.     @NamedQuery(  
  3.         name="authorPosts",  
  4.         queryString="select p from Post p where p.author = :author"  
  5.     );,  
  6.     @NamedQuery(  
  7.         name="allPosts",  
  8.         queryString="select p from Post p"  
  9.     );  
  10. });  
  11.   
  12. @Entity  
  13. @Table(name="FORUM_POST", schema="javaeye");  
  14. public class Post {  
  15.     private Long _id;  
  16.     private String _title;  
  17.     private String _content;  
  18.     private User _author;  
  19.       
  20.     @Id(generate=SEQUENCE, generator ="POST_SEQ");      
  21.     public Long getId(); {  
  22.         return _id;  
  23.     }  
  24.       
  25.     @Column(name="TITLE", nullable=false, length=255);  
  26.     public String getTitle(); {  
  27.         return _title;     
  28.     }  
  29.       
  30.     @Column(name="CONTENT", columnDefinition="CLOB NOT NULL");  
  31.     public String getContent(); {  
  32.         return _content;  
  33.     }  
  34.       
  35.     @ManyToOne(optional=false);  
  36.     @JoinColumn(name="AUTHOR_ID", nullable=false, updatable=false);  
  37.     public User getAuthor(); {  
  38.         return _author;  
  39.     }  
  40. }  
@NamedQueries({
    @NamedQuery(
        name="authorPosts",
        queryString="select p from Post p where p.author = :author"
    );,
    @NamedQuery(
        name="allPosts",
        queryString="select p from Post p"
    );
});

@Entity
@Table(name="FORUM_POST", schema="javaeye");
public class Post {
    private Long _id;
    private String _title;
    private String _content;
    private User _author;
    
    @Id(generate=SEQUENCE, generator ="POST_SEQ");    
    public Long getId(); {
        return _id;
    }
    
    @Column(name="TITLE", nullable=false, length=255);
    public String getTitle(); {
        return _title;   
    }
    
    @Column(name="CONTENT", columnDefinition="CLOB NOT NULL");
    public String getContent(); {
        return _content;
    }
    
    @ManyToOne(optional=false);
    @JoinColumn(name="AUTHOR_ID", nullable=false, updatable=false);
    public User getAuthor(); {
        return _author;
    }
}



2. 校验
比如WebWork的visitor方式校验,能够将action的校验,转发到Entity上, 以前的写法是写一个Post-validation.xml:

Xml代码
  1. <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">  
  2. <validators>  
  3.     <field name="title">  
  4.         <field-validator type="requiredstring">  
  5.             <message>Please input title.</message>  
  6.         </field-validator>  
  7.     </field>  
  8.     <field name="content">  
  9.         <field-validator type="requiredstring">  
  10.             <message>Please input content.</message>  
  11.         </field-validator>  
  12.     </field>  
  13. </validators>  



改用annotation成:

Java代码
  1. @Validator(type="requiredstring", message="Please input title.");  
  2. public String getTitle(); {  
  3.     return _title;     
  4. }  
  5.   
  6. @Validator(type="requiredstring", message="Please input content.");  
  7. public String getContent(); {  
  8.     return _content;  
  9. }  
    @Validator(type="requiredstring", message="Please input title.");
    public String getTitle(); {
        return _title;   
    }
    
    @Validator(type="requiredstring", message="Please input content.");
    public String getContent(); {
        return _content;
    }



3. 索引
比如用lucene做全文搜索,以前是写一个Post-lucene.xml, 当一个Post被持久化的时候,能够自动根据这个配置文件对相应的属性做不同的索引处理:

Xml代码
  1. <lucene>  
  2.     <field type="Keyword" fieldName="title"/>  
  3.     <field type="Text" fieldName="content"/>  
  4. </lucene>  



改用annotation成:

Java代码
  1. @LuceneIndex(type="KEYWORD");  
  2. public String getTitle(); {  
  3.     return _title;     
  4. }  
  5.   
  6. @LuceneIndex(type="TEXT");  
  7. public String getContent(); {  
  8.     return _content;  
  9. }  
    @LuceneIndex(type="KEYWORD");
    public String getTitle(); {
        return _title;   
    }
    
    @LuceneIndex(type="TEXT");
    public String getContent(); {
        return _content;
    }





如果将3种annotation都混合在一起,整个Post Entity将会变成:

Java代码
  1. @NamedQueries({  
  2.     @NamedQuery(  
  3.         name="authorPosts",  
  4.         queryString="select p from Post p where p.author = :author"  
  5.     );,  
  6.     @NamedQuery(  
  7.         name="allPosts",  
  8.         queryString="select p from Post p"  
  9.     );  
  10. });  
  11.   
  12. @Entity  
  13. @Table(name="FORUM_POST", schema="javaeye");  
  14. public class Post {  
  15.     private Long _id;  
  16.     private String _title;  
  17.     private String _content;  
  18.     private User _author;  
  19.       
  20.     @Id(generate=SEQUENCE, generator ="POST_SEQ");      
  21.     public Long getId(); {  
  22.         return _id;  
  23.     }  
  24.       
  25.     @Column(name="TITLE", nullable=false, length=255);  
  26.     @Validator(type="requiredstring", message="Please input title.");  
  27.     @LuceneIndex(type="KEYWORD");  
  28.     public String getTitle(); {  
  29.         return _title;     
  30.     }  
  31.       
  32.     @Column(name="CONTENT", columnDefinition="CLOB NOT NULL");  
  33.     @Validator(type="requiredstring", message="Please input content.");  
  34.     @LuceneIndex(type="TEXT");  
  35.     public String getContent(); {  
  36.         return _content;  
  37.     }  
  38.       
  39.     @ManyToOne(optional=false);  
  40.     @JoinColumn(name="AUTHOR_ID", nullable=false, updatable=false);  
  41.     public User getAuthor(); {  
  42.         return _author;  
  43.     }  
  44. }  
@NamedQueries({
    @NamedQuery(
        name="authorPosts",
        queryString="select p from Post p where p.author = :author"
    );,
    @NamedQuery(
        name="allPosts",
        queryString="select p from Post p"
    );
});

@Entity
@Table(name="FORUM_POST", schema="javaeye");
public class Post {
    private Long _id;
    private String _title;
    private String _content;
    private User _author;
    
    @Id(generate=SEQUENCE, generator ="POST_SEQ");    
    public Long getId(); {
        return _id;
    }
    
    @Column(name="TITLE", nullable=false, length=255);
    @Validator(type="requiredstring", message="Please input title.");
    @LuceneIndex(type="KEYWORD");
    public String getTitle(); {
        return _title;   
    }
    
    @Column(name="CONTENT", columnDefinition="CLOB NOT NULL");
    @Validator(type="requiredstring", message="Please input content.");
    @LuceneIndex(type="TEXT");
    public String getContent(); {
        return _content;
    }
    
    @ManyToOne(optional=false);
    @JoinColumn(name="AUTHOR_ID", nullable=false, updatable=false);
    public User getAuthor(); {
        return _author;
    }
}



本来干干净净的一个Post POJO,变成了充满annotation的怪家伙,这可以称之为annotation的滥用么?难道又一个AOP出现了?难道以后偶们打开一个Java 文件必须用Eclipse之类的IDE,好让它自动将annotation fold,免得花了眼睛么?

<div className="path-card-container" key={path.index}> <Card className={ highlightPathIndex === path.index ? "path-card highlighted" : "path-card" } size="small" onClick={() => handleHighlightPath(path.index)} hoverable > <div className="path-info"> <div className="path-title"> {highlightPathIndex === path.index ? ( <Tag color="blue">路径 {path.index + 1}</Tag> ) : ( `路径${path.index + 1}` )} </div> <div className="path-actions" onClick={(e) => e.stopPropagation()} > <Radio.Group value={path.annotation?.annotation} onChange={(e) => { e.stopPropagation(); handleAnnotate(path.index, e.target.value); }} buttonStyle="solid" size="small" > <Radio.Button value="good" className={ path.annotation?.annotation === "good" ? "good-selected" : "" } onClick={(e) => { e.stopPropagation(); if (path.annotation?.annotation === "good") { handleAnnotate(path.index, "good"); } }} > <CheckCircleOutlined /> 好 </Radio.Button> <Radio.Button value="bad" className={ path.annotation?.annotation === "bad" ? "bad-selected" : "" } onClick={(e) => { e.stopPropagation(); if (path.annotation?.annotation === "bad") { handleAnnotate(path.index, "bad"); } }} > <CloseCircleOutlined /> 差 </Radio.Button> <Radio.Button value="unknown" className={ path.annotation?.annotation === "unknown" ? "unknown-selected" : "" } onClick={(e) => { e.stopPropagation(); if (path.annotation?.annotation === "unknown") { handleAnnotate(path.index, "unknown"); } }} > <QuestionCircleOutlined /> 未知 </Radio.Button> </Radio.Group> </div> </div> </Card> </div> .path-card-container { display: flex; align-items: center; margin-bottom: 8px; } .path-card-container .path-card { flex-grow: 1; /* margin-right: 10px; */ } .path-card-container .path-card .ant-card-body{ padding: 6px; } .path-card-container .path-actions { flex-shrink: 0; } 这个用tailwind
最新发布
10-18
你当前的 React + Ant Design 代码使用了自定义 CSS 来控制 `.path-card-container` 和其子元素的布局与样式。我们可以**完全用 Tailwind CSS 替代这些样式**,无需额外写 CSS 类。 --- ### ✅ 目标功能分析 | 原功能 | 对应需求 | |-------|----------| | `display: flex; align-items: center` | 水平排列卡片内容,垂直居中 | | `margin-bottom: 8px` | 每个容器下方留空 8px | | `.path-card` 使用 `flex-grow: 1` | 卡片主体占满剩余空间 | | `.ant-card-body` 设置 `padding: 6px` | 减少卡片内边距 | | `.path-actions` 设置 `flex-shrink: 0` | 防止操作按钮区域被压缩 | --- ### ✅ 完整的 Tailwind 实现(替换所有 CSS) ```jsx <div className="flex items-center mb-2" key={path.index} > <Card size="small" hoverable onClick={() => handleHighlightPath(path.index)} // 使用 style 控制 body padding,因为 Ant Design 不支持直接用 class 修改 body styles={{ body: { padding: '6px' } }} className={`flex-1 ${ highlightPathIndex === path.index ? 'ring-2 ring-blue-500 rounded-lg' : '' }`} > <div className="flex items-center justify-between gap-2"> {/* 路径标题 */} <div className="flex-1"> {highlightPathIndex === path.index ? ( <Tag color="blue">路径 {path.index + 1}</Tag> ) : ( <span>路径{path.index + 1}</span> )} </div> {/* 操作按钮组 - 固定大小,不压缩 */} <div className="shrink-0" onClick={(e) => e.stopPropagation()}> <Radio.Group value={path.annotation?.annotation} onChange={(e) => { e.stopPropagation(); handleAnnotate(path.index, e.target.value); }} buttonStyle="solid" size="small" > <Radio.Button value="good" className={ path.annotation?.annotation === "good" ? "bg-green-100 border-green-300" : "" } > <CheckCircleOutlined /> 好 </Radio.Button> <Radio.Button value="bad" className={ path.annotation?.annotation === "bad" ? "bg-red-100 border-red-300" : "" } > <CloseCircleOutlined /> 差 </Radio.Button> <Radio.Button value="unknown" className={ path.annotation?.annotation === "unknown" ? "bg-gray-100 border-gray-300" : "" } > <QuestionCircleOutlined /> 未知 </Radio.Button> </Radio.Group> </div> </div> </Card> </div> ``` --- ### 🔍 详细解释:Tailwind 如何替代原 CSS | 原 CSS | Tailwind 替代方案 | 说明 | |--------|-------------------|------| | `.path-card-container { display: flex; align-items: center; }` | `flex items-center` | Flex 布局 + 垂直居中 ✅ | | `margin-bottom: 8px` | `mb-2` | Tailwind `spacing[2] = 0.5rem = 8px` ✅ | | `.path-card { flex-grow: 1 }` | `flex-1` | 占据剩余空间 ✅ | | `.ant-card-body { padding: 6px }` | `styles={{ body: { padding: '6px' } }}` | Ant Design 提供的 API,无法用 class 修改内部 body ✅ | | `.path-actions { flex-shrink: 0 }` | `shrink-0` 或 `flex-shrink-0` | 防止元素被压缩 ✅ | --- ### 📌 特别说明 #### 1. `styles` vs `className` in Ant Design `Card` Ant Design 的 `<Card>` 组件支持 `styles` 属性来定制内部结构: ```tsx styles={{ body: { padding: '6px' } }} ``` 这是官方推荐方式,比全局覆盖 `.ant-card-body` 更安全。 #### 2. 高亮效果替代 `.highlighted` 原类名 `.highlighted` 可以用 Tailwind 实现视觉反馈: ```tsx highlightPathIndex === path.index ? 'ring-2 ring-blue-500 rounded-lg' : '' ``` 👉 效果:蓝色外圈高亮,模拟选中状态。 #### 3. Radio.Button 选中样式美化 Ant Design 默认选中样式有限,可以用 `className` 添加背景色增强可读性: ```tsx className={path.annotation?.annotation === "good" ? "bg-green-100 border-green-300" : ""} ``` Tailwind 中这些是动态类,会叠加在原有样式上。 --- ### 🧩 可选优化:响应式适配 如果你希望在小屏幕上堆叠布局,可以改为: ```html <div className="flex flex-col sm:flex-row items-center gap-2 mb-2"> ``` 这样移动端竖向排列,PC 端横向。 --- ### ✅ 最终优势总结 | 优点 | 说明 | |------|------| | ✅ 零 CSS 文件 | 所有样式由 Tailwind 和内联 `style` 完成 | | ✅ 易维护 | 样式集中在 JSX 中,无需查找外部 CSS | | ✅ 响应式友好 | 可轻松添加 `sm:`、`md:` 等断点 | | ✅ 主题一致 | 使用 Tailwind 颜色系统统一 UI 风格 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值