一,泛型和反射的初识
Java泛型是JDK 5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许程序员在编译时监测非法的类型。简单的理解就是参数化类型,我们一开始就知道这个泛型T是String,Student,还是其他,而不是Object。如此对我们编码比较友好,以及类型安全问题也得到解决。
Java反射是Java的一个重要的高级特性,很多重复性的工作都可以利用这个特性进行系统底层的编写。简单的理解,任何一个类都可以利用这个机制获取到类,方法,属性等。即使是泛型也能进行获取到类型。
基于上述说明,对这两个点进行例子+说明的方式进行编写文章。
二,泛型
自JDK5以来,我们其实或多或少已经使用到泛型的东西,如列表
List list = new ArrayList();
Map map = HashMap();
这些都是泛型的应用。
首个示例:(数据库增删改查示例)
先定义一个基础的数据库对象都会包括字段的类:
import lombok.Data;
import java.util.Date;
@Data
public class BaseModel {
private Long id;
private Date createdAt;
private String createUserId;
private String createUserName;
private Date updatedAt;
private String updateUserId;
private String updateUserName;
}
再写数据库操作基础接口类:
import java.io.Serializable;
import java.util.List;
/*** 基础 DAO接口** @author Lawliet*/
public interface BaseMapper {
/*** 保存,存在id就更新,不存在就添加** @param entity 实体对象* @return boolean*/
int save(T entity);
/***
* 插入一条记录(选择字段,策略插入)*
** @param entity 实体对象* @return boolean*/int insert(T entity);
/***
* 插入一条记录(全部字段)*
** @param entity 实体对象* @return boolean*/int insertAllColumn(T entity);
/*** 插入(批量)** @param entityList 实体对象列表* @return boolean*/
int insertBatch(List entityList);
/*** 根据 ID 删除** @param id 主键ID* @return boolean*/
int deleteById(Serializable id);
/*** 删除(根据ID 批量删除)** @param idList 主键ID列表* @return boolean*/
int deleteBatchIds(List idList);
/*** 根据 ID 选择修改** @param entity 实体对象* @return boolean*/
int updateById(T entity);
/*** 根据ID 批量更新** @param entityList 实体对象列表* @return boolean*/
int updateBatchById(List entityList);
/*** 根据 ID 查询** @param id 主键ID* @return V*/
T selectById(Serializable id);
/*** 查询(根据ID 批量查询)** @param idList 主键ID列表* @return List*/
List selectBatchIds(List idList);
}
再写上述接口的实现,由于只是例子,所以只是简单打印消息:
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.util.List;
/*** 基础 DAO** @author Lawliet*/
@Slf4j
public class BaseManager implements BaseMapper {
@Override
public int save(T entity) {
System.out.println("底层获取到数据:" + entity.toString());
System.out.println("获取到model[ID]属性: " + entity.getId());
return 0;
}
@Override
public int insert(T entity) {
System.out.println("底层获取到数据:" + entity.toString());
System.out.println("获取到model属性" + entity.getId());
return 0;
}
@Override
public int insertAllColumn(T entity) {
System.out.println("底层获取到数据:" + entity.toString());
System.out.println("获取到model属性" + entity.getId());
return 0;
}
@Override
public int insertBatch(List entityList) {
System.out.println("底层获取到数据:" + entityList.toString());
System.out.println("获取到model属性" + entityList.get(0).getId());
return 0;
}
@Override
public int deleteById(Serializable id) {
System.out.println("获取到model属性" + id);
return 0;
}
@Override
public int deleteBatchIds(List idList) {
return 0;
}
@Override
public int updateById(T entity) {
return 0;
}
@Override
public int updateBatchById(List entityList) {
return 0;
}
@Override
public T selectById(Serializable id) {
return null;
}
@Override
public List selectBatchIds(List idList) {
return null;
}
}
上述写完之后,则底层增删改查的底层算是完成,则需要写业务的类:(以用户User为例)
import lombok.Data;
import lombok.ToString;
@Data
@ToString(callSuper = true)
public class User extends BaseModel{
private String userName;
private String userTitle;
private Integer userAge;
private Integer userSex;
}
public class UserManager extends BaseManager{
}
由于该例子是没使用springboot,所以实际运用会加上@Service的注解的。
上最后的测试代码:
public class Test {
public static void main(String[] args) {
//创建用户,实际应用场景应该由前端传数据过来 User user = new User();
user.setUserAge(11);
user.setUserName("小明");
user.setUserSex(1);
user.setUserTitle("学生");
user.setId(12138L);
//调用方法 UserManager userManager = new UserManager();
userManager.save(user);
}
}
效果如下:
通过上述简单的示例,我们可以体验到泛型的魅力,就是我只需要写好最底层的基础类:BaseMapper 和 BaseManager,后面我们只需要写很简单的继承,即可实现数据库的增删改查的事情,效率提升N倍。
上述示例不想敲代码的,可以下载体验学习:
第二个泛型实例:(应用于实际场景,easypoi实现文件导入导出)
由于篇幅有点过长,因此展示导出的实例,会贴上云盘下载地址。
定义导出接口:
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/*** 导出监听器* 创建时间 2019-12-30 11:28** @author lincf2*/
public abstract class BaseExportDataPoiListener implements Serializable {
/*** 导出附件名称*/
protected abstract String getFileName();
/*** 导出数据*/
protected abstract List getExportData(Map map);
}
实现上述接口的静态controller代码,基本上全部导出逻辑在这个位置
import com.alibaba.excel.EasyExcel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.net.URLEncoder;
import java.util.*;
/*** 基础导出模板* 创建时间 2019-12-30 11:13** @author lincf2*/
public abstract class BaseExportDataPoiController implements Serializable {
private static Logger logger = LoggerFactory.getLogger(BaseExportDataPoiController.class);
@RequestMapping(value = "/excel/export", method = {RequestMethod.GET})
public void exportExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
L l = this.getExportDataPoiListener();
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = l.getFileName() + ".xlsx";
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
List dataList = l.getExportData(this.getQuery(request));
EasyExcel.write(response.getOutputStream(), this.getTClass()).sheet("sheet").doWrite(dataList);
}
private Class getTClass() {
Class tClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return tClass;
}
private L getExportDataPoiListener() {
ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
Class clazz = (Class) ptype.getActualTypeArguments()[1];
try {
L l = (L) clazz.newInstance();
return l;
} catch (Exception exp) {
logger.error("无法初始化导出对象L : {}", exp.getMessage());
}
return null;
}
private Map getQuery(HttpServletRequest request) {
Map map = new HashMap();
Enumeration paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = (String) paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length > 0) {
String paramValue = paramValues[0];
if (paramValue.length() != 0) {
map.put(paramName, paramValue);
}
}
}
Set> set = map.entrySet();
for (Map.Entry entry : set) {
logger.debug(entry.getKey() + ":" + entry.getValue());
}
return map;
}
}
根据上述底层编写业务实现:(以账户为例)
导出对象:
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/*** 类描述* 创建时间 2019-12-30 14:55** @author lincf2*/
@Data
@ToString(callSuper = true)
@ContentRowHeight(20)//高度@HeadRowHeight(20)
@ColumnWidth(25)//宽度public class DemoExportVo implements Serializable {
/*** 总金额*/
@ExcelProperty(value = "总金额", index = 0)
private BigDecimal amount;
/*** 基本余额*/
@ExcelProperty(value = "基本余额", index = 1)
private BigDecimal balance;
/*** 赠送金额*/
@ExcelProperty(value = "赠送金额", index = 2)
private BigDecimal largessAmount;
/*** 冻结金额*/
@ExcelProperty(value = "冻结金额", index = 3)
private BigDecimal freezeAmount;
/*** md5加密保存*/
@ExcelProperty(value = "密码", index = 4)
private String payPasswd;
/*** 商家名称*/
@ExcelProperty(value = "商家名称", index = 5)
private String objectName;
/*** 有效期*/
@ExcelProperty(value = "有效期", index = 6)
private Date expireDate;
}
实现导出业务逻辑,返回数据的监听器层
import com.lawliet.demo.base.BaseExportDataPoiListener;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/*** 类描述* 创建时间 2019-12-30 14:59** @author lincf2*/
@Slf4j
@Data
@ToString(callSuper = true)
public class DemoExportDataPoiListener extends BaseExportDataPoiListener {
@Override
protected String getFileName() {
return "导出demo文件";
}
@Override
protected List getExportData(Map map) {
log.info("信息参数:" + map.toString());
//TODO 根据上面参数处理业务,做成如下数据即可
List demoExportVoList = new ArrayList();
DemoExportVo demoExportVo = new DemoExportVo();
demoExportVo.setAmount(new BigDecimal("100"));
demoExportVo.setBalance(new BigDecimal("100"));
demoExportVo.setExpireDate(new Date());
demoExportVo.setFreezeAmount(new BigDecimal("100"));
demoExportVo.setObjectName("周小系");
demoExportVo.setPayPasswd("sisdjahhgdjdhdfhvvhjsjsj");
demoExportVoList.add(demoExportVo);
return demoExportVoList;
}
}
最后的controller写法
import com.lawliet.demo.base.BaseExportDataPoiController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*** 类描述* 创建时间 2019-12-30 14:51** @author lincf2** http://127.0.0.1:8081/demo/alibaba/account/excel/export?id=12&name=Lawliet**/
@Controller
@RequestMapping("/demo/alibaba/account")
public class DemoExportDataPoiController extends BaseExportDataPoiController {
}
最后复制上面链接去浏览器打开就可以导出数据,效果图如下:
总结:编写该底层也是为了让我们更加注重业务的开发,编写我们导出的VO,导出名称,导出的业务数据来源。统一controller写法。简易的编写几个业务类,即可实现高性能的导出功能。也是泛型给我们带来的魅力。学好泛型等于让自己效率提高一个档次,代码质量也会上去。
下面链接是整个demo项目,包括导出导入功能实现,在生产环境验证过的。
三,反射
下次更新....