折腾无极限,技术永无止境
前言
未标准化进行java开发时,没有进行对业务代码以及公用逻辑进行分离,如果需求遇到改动,需要改公用逻辑时,需要去一处处业务代码中去找,大大的浪费了时间,使用接口后,可以合理的解决这一问题。
接口的概念
- 使用interface声明的类
- 只有方法标识符,没有方法体
- 接口像蓝图一样,指明一个类必须要做什么和不能做什么
- 接口中所有的方法都是抽象的和public的,所有的属性都是public,static,final的。
- 一个类如果要实现某个接口时,必须要实现这个接口中的所有方法。
接口的作用
- 实现多继承:因为Java不能像c++一样支持多继承,Java可以通过实现接口来弥补这个问题。
- 接口可以解偶
- 可以将逻辑代码与业务代码进行分
接口的语法
- 使用interface声明一个类,在类内声明一个注册方法
public interface UserService {
// 声明一个方法
public void method();
}
- 创建接口实现类,使用Override重写接口中声明的方法
// 实现接口中声明的方法
@Override
public void method() {
// +++++++ 具体的实现 +++++++
}
至此就就完成了一个接口的声明到实现
实际应用
我们先来看看不使用接口时,将功能的实现和业务代码全写在controller层的代码,这里我们以登录为例,如果遇到新的需求,我们就要去找找功能实现部分进行修改,业务代码过多时是一件很头疼的事情
@RestController
@RequestMapping("/user")
public class GreetingController {
private final Logger logger= LoggerFactory.getLogger(getClass());
@Resource
private UserInfoMapper userMapper;
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@CrossOrigin()
// 登录请求
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@RequestBody String request){
// 获取前端参数
JSONObject jsReq = new JSONObject(request);
JSONObject jsReply = new JSONObject();
String username = jsReq.getString("username");
String password = jsReq.getString("password");
UserInfo row = new UserInfo();
JSONArray queryType = new JSONArray();
queryType.put("name");
queryType.put("password");
queryType.put("career");
queryType.put("avatarSrc");
queryType.put("userID");
row.setQueryType(queryType);
row.setName(username);
List<UserInfo> resultList = userMapper.customQuery(row);
JSONArray result = null;
if(resultList.size()>0){
result = new JSONArray(resultList);
if(password.equals(result.getJSONObject(0).getString("password"))){
jsReply.put("code",0);
// 根据当前用户名和密码生成token
jsReply.put("token", JwtUtil.sign(username,password));
jsReply.put("msg","验证成功,token有效期为30分钟");
jsReply.put("avatarSrc",result.getJSONObject(0).getString("avatarSrc"));
jsReply.put("userID",result.getJSONObject(0).getString("userID"));
}else{
jsReply.put("code",-2);
jsReply.put("msg","密码错误");
}
}else{
jsReply.put("code",-1);
jsReply.put("msg","当前登录用户不存在");
}
return jsReply.toString();
}
}
- 使用接口对上述场景的代码进行分离(这里我们以注册功能为例)
// service层
package com.lk.service;
import com.lk.enums.ResultEnum;
public interface UserService {
// 新用户注册接口
public ResultEnum AddUser(String userName, String password, String avatarSrc) throws Exception;
}
// serviceimpl层(接口实现)
package com.lk.serviceimpl;
import com.lk.dao.UserMapper;
import com.lk.entity.User;
import com.lk.enums.ResultEnum;
import com.lk.service.UserService;
import com.lk.utils.DateUtil;
import com.lk.utils.HMacMD5;
import com.lk.utils.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
// @Slf4j是lombok的注解,使用此注解后可以在当前类直接使用log关键字来打印日志
@Slf4j
@Service("userServiceImpl")
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
// 从配置文件读取加密key
@Value("${HMacKey}")
private String HMacKey;
// 实现注册接口
@Override
public ResultEnum AddUser(String userName, String password, String avatarSrc) throws Exception {
// 判断参数是否合法
if(StringUtils.isBlank(userName) ||userName.length()>10){
log.error("违规操作:用户名为空或用户名长度大于10,"+userName);
return ResultEnum.USER_ERROR;
}
if(StringUtils.isBlank(password)||password.length()>16){
log.error("违规操作:密码为空或密码长度大于10,"+password);
return ResultEnum.PASSWORD_ERR;
}
if(StringUtils.isBlank(avatarSrc)||avatarSrc.length()>300){
log.error("违规操作:头像地址为空或头像地址过长,"+avatarSrc);
return ResultEnum.AVATAR_ERR;
}
// 对密码进行加密
password = HMacMD5.encryptHMAC2String(password,HMacKey);
User row = new User();
row.setUserName(userName);
row.setUserId(UUIDUtil.getUUID());
row.setPassword(password);
row.setAvatarSrc(avatarSrc);
row.setCreateTime(DateUtil.getThisTime());
log.info(userMapper+"");
// 增加用户,向数据库插入一条数据
int result = userMapper.insertUser(row);
return ResultEnum.SUCCESS;
}
}
// controller层(业务代码)
package com.lk.controller;
import com.lk.VO.ResultVO;
import com.lk.dao.UserMapper;
import com.lk.entity.User;
import com.lk.enums.ResultEnum;
import com.lk.serviceimpl.UserServiceImpl;
import com.lk.utils.ResultVOUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@Api(value = "/api/user", tags = "用户信息接口模块")
@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {
// 注入用户接口
@Resource(name = "userServiceImpl")
private UserService userService;
@ApiOperation(value = "注册接口", notes = "传用户名、密码、头像地址来注册用户")
@RequestMapping(value = "/registered", method = RequestMethod.POST)
public ResultVO registered(@RequestBody User user) throws Exception {
// 调用注册接口
ResultEnum result = userService.AddUser(user.getUserName(),user.getPassword(),user.getAvatarSrc());
// 获取返回枚举的code判断是否成功
if(result.getCode()==0){
return ResultVOUtil.success(result.getMessage());
}
return ResultVOUtil.error(result.getCode(),result.getMessage());
}
}
特别感谢
感谢评论区开发者给我提出的建议,让我意识到自己有的Java有多菜,之前写项目一直处于松散开发,好多场景都没考虑到,写的代码凌乱不堪。我没有真实参加过公司的Java项目开发,因为我全职前端,文中讲的不到位的地方,还望大家多多包涵。
针对评论区开发者的建议,我做了如下修改:
- controller不要写任何逻辑只做控制,将所有的逻辑代码放在接口的impl里实现
- 使用枚举类声明一组命名的常数,便于判断接口相关操作是否成功,解决了魔法数字问题
- 修改接口返回类型为枚举
- 修改之前的json接收前端参数方法,采用dto来接收前端参数
- 修改之前返回使用json构造返回数据的方法,采用VO结果视图给前端返回参数
- 集成swagger,用于生成接口文档
- 至于评论区@muruxing 开发者说的我为什么不使用@autowired注解,因为使用这个注解时idea会有黄线警告,所以我使用了@Resource,而且我考虑到同一个接口可能有很多的实现类,所以我采用给每一个实现类注入@Service(“userServiceImpl”),然后在Controller中使用时通过@Resource(name=“userServiceImpl”)方法实例化对应的实现类
- 使用@Slf4j注解,省去了之前在实例化logger的工厂对象
踩坑记录
在controller层使用接口实现类里的方法时,spring注解的内容一直为null,无法拿到@Resource注解的mybatis里的mapper对象和@Value注解的获取yml文件中的属性,接口实现类报空指针异常。
原因是:我在controller层实例化接口实现类时,对象是自己new的,没有走spring,所以一直为null。找了很多解决方案,走了很多弯路,最终使用@Service注解和@Resource注解完美解决了这个问题。
我的问题可以描述为:接口的实现类在controller层如何注入
- 在controller层自己new接口实现类对象
// 错误的解决方案,实现类不走spring容器,报空指针异常
UserServiceImpl userService = new UserServiceImpl();
- 在接口实现类中使用@service注解,在controller层使用@Resource注入
// 这里括号中的名字为当前类名首字母小写
@Service("userServiceImpl")
public class UserServiceImpl implements UserService {
/////++++++++++++++++++++++++/////
}
// controller层
public class UserController {
// 括号里的name值对应接口实现类@Service注解括号里的名字
@Resource(name = "userServiceImpl")
private UserService userService;
}
至此,关于接口实现类在controller层如何注入的问题就完美解决了,带大家看下上述注册接口实现的效果
写在最后
- 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
- 本文首发于掘金,如需转载请评论区留言💌
评论区