Java注解(二):实战 - 直接使用对象列表生成报表

通过对Java注解(一):介绍,思想及优点学习了解,相信大家对Java注解有一定程度的了解,本篇文章将实战项目中的应用来加深对Java注解的了解。

本实例实现根据指定字段的JavaBean,生成对应列的报表。使用Java注解就是方便实现JavaBean与Excel或CSV列已一一映射。直观展现数据,不需要中间转换,Java注解可以很轻松实现。

下面先给出Java注解的定义:

import java.lang.annotation.*;

/**
 * 类功能描述:列属性
 *
 * @author WangXueXing create at 18-10-29 下午4:40
 * @version 1.0.0
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ColumnProperty {
    /**
     * 列序号
     * @return
     */
    int index();

    /**
     * 列表头名
     * @return
     */
    String name();
}

  本注解定义目的是使用在JavaBean的每个字段映射到Excel中的列序号及列表头名。这样对于开发者,我只需要定义好报表展示的列及相对位置,并组装JavaBean list.直观,不需要关心转换过程。

下面代码就是定义JavaBean及使用上面定义的注解:

import com.today.service.financereport.util.ColumnProperty

import scala.annotation.meta.field
import scala.beans.BeanProperty

/**
  * 类功能描述:营收明细
  *
  * @author WangXueXing create at 18-10-23 下午7:52
  * @version 1.0.0
  */
case class IncomeDetail(/** 店号		 */
                        @(ColumnProperty@field)(index = 0, name = "店号")
                        @BeanProperty
                        storeId: String,

                        /** 店名		 */
                        @(ColumnProperty@field)(index = 1, name = "店名")
                        @BeanProperty
                        storeName: String,

                        /** 门店类型		 */
                        @(ColumnProperty@field)(index = 2, name = "门店类型")
                        @BeanProperty
                        storeType: String,

                        /** 营业日期		 */
                        @(ColumnProperty@field)(index = 3, name = "营业日期")
                        @BeanProperty
                        businessDate: String,

                        /** 交易时间		 */
                        @(ColumnProperty@field)(index = 4, name = "交易时间")
                        @BeanProperty
                        payTime: String,

                        /** 订单号		 */
                        @(ColumnProperty@field)(index = 5, name = "订单号")
                        @BeanProperty
                        orderNo: String,

                        /** 交易流水号		 */
                        @(ColumnProperty@field)(index = 6, name = "交易流水号")
                        @BeanProperty
                        tradeNo: String,

                        /** 金额		 */
                        @(ColumnProperty@field)(index = 7, name = "金额")
                        @BeanProperty
                        tradeAmount: BigDecimal,

                        /** 抹零金额		 */
                        @(ColumnProperty@field)(index = 8, name = "抹零金额")
                        @BeanProperty
                        spotAmount: BigDecimal,

                        /** 支付方式		 */
                        @(ColumnProperty@field)(index = 9, name = "支付方式")
                        @BeanProperty
                        tradeType: String,

                        /** POS机		 */
                        @(ColumnProperty@field)(index = 10, name = "POS机")
                        @BeanProperty
                        posId: String,

                        /** 收银员		 */
                        @(ColumnProperty@field)(index = 11, name = "收银员")
                        @BeanProperty
                        cashierName: String,

                        /** 订单类型  */
                        @(ColumnProperty@field)(index = 12, name = "订单类型")
                        @BeanProperty
                        payType: String,

                        /** 第三方商户订单号  */
                        @(ColumnProperty@field)(index = 13, name = "第三方商户订单号")
                        @BeanProperty
                        thirdPartyPaymentNo: String
                       )

  其中@BeanProperty是Scala自带的一个注解,就是免去Java中对每个字段的getter()与setter()的定义。

 以下是伪代码,从数据库中获取对应数据结构为IncomeDetail的数据列表:

def getReportData: List[IncomeDetail] = {
    return jdbc.getIncomeDetailList()
}

  获取到数据后,将数据通过注解转换并填入报表:

val incomeDetailList = getReportData()
val file = File.createTempFile( "营收明细报表_" + MathUtils.getRandom(4), ".csv")
      ScalaFunction.tryWithResources(new PrintWriter(file, "GBK")) { out =>
        val dataObj =incomeDetailList.isInstanceOf[Seq[AnyRef@unchecked]] match {
          case true => incomeDetailList.asInstanceOf[List[AnyRef@unchecked]]
          case _ => List.empty
        }
        //根据注解顺序生成CSV数据列表
        AnnotationUtil.getValueWithHead(dataObj).foreach(out.println(_))
      }

  其中ScalaFunction.tryWithResources()仿照Java try with resources本人定义了一个Scala函数来处理流关闭,详情请参考我以前的博客:Scala实现Try with resources自动关闭IO

/**
  * 类功能描述:Scala高级函数
  *
  * @author WangXueXing create at 18-11-22 下午5:29
  * @version 1.0.0
  */
object ScalaFunction {
  /**
    * Scala实现Java7 try-with-resources
    * @see https://www.cnblogs.com/barrywxx/p/10002422.html
    */
  def tryWithResources[A <: {def close(): Unit }, B](a: A)(f: A => B): B = {
    try {
      f(a)
    } finally {
      if(a != null){
        a.close()
      }
    }
  }
}

  我们重点关注下AnnotationUtil.getValueWithHead(), 这个方法定义了通过注解将数据填入Excel或CSV报表的过程:

import java.lang.reflect.Field

import com.today.service.financereport.dto.IncomeDetail

/**
  * 类功能描述:Scala注解工具类
  *
  * @author WangXueXing create at 18-10-30 下午3:35
  * @version 1.0.0
  */
object AnnotationUtil {
  /**
    * 根据注解获取值列表
    */
  def getValueWithHead(valList: List[_]): List[String] ={
    var fields: Array[Field] = Array.empty
    val dataList: List[String] = valList.zipWithIndex.map{ x=>
      if(x._2 == 0){
        fields = x._1.getClass.getDeclaredFields.sortBy(_.getAnnotations.apply(0).asInstanceOf[ColumnProperty].index())
      }

      fields.map{f=>
         f.setAccessible(true)
         val value = f.get(x._1)
         if(value == null){
            ""
         } else {
           value.toString
         }
      }.mkString(",")
    }
    fields.map(_.getAnnotation(classOf[ColumnProperty]).name()).mkString(",")+:dataList
  }

  def main(args: Array[String]): Unit = {
    val list = List(IncomeDetail("wew1","eerr1","wrw1",null,"1","1","1",BigDecimal(12.1),BigDecimal(12.1),"1","1","1"),
                    IncomeDetail("rer2","wrwrw2","wrw2","2","2","2","2",BigDecimal(12.1),BigDecimal(12.1),"2","2","2"))

    val objList = getValueWithHead(list)
    objList.foreach(println(_))
  }
}

  好了,到这里,这个实例已经完成。是不是以后就不需要关注怎样组装复杂报表数据结构,只需要将JavaBean定义好,按照这个JavaBean的数据结构组装数据就行了!

posted @ 2019-01-17 22:05  BarryW  阅读(315)  评论(0编辑  收藏  举报