Java开发小技巧大杂烩

导读:本篇文章讲解 Java开发小技巧大杂烩,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

静态导入

toString工具类

Spring提供的Controller层分页类

mybatis提供的Service层分页插件

SpringBoot的Test测试类

@JsonView 注解

 微信抢红包金额随机分配规则

SpringBoot全局异常拦截工具类

SpringBoot使用切面记录日志

SpringBoot接口访问权限,AOP实现

对象转Json字符串出现大写变小写问题


 


静态导入

对于静态方法,使用的时候可以不写上类名,通过import static的方式引用

import static org.springframework.scheduling.support.CronSequenceGenerator.isValidExpression;
import static org.slf4j.LoggerFactory.getLogger;    // 静态导入,注意静态导入不要使用"*"的方式,不方便阅读维护

/**
 * @author lixx
 * @title 动态获取cron表达式
 * @date 2019/4/2
 */
@Component
@Service(interfaceClass = CronService.class)
public class CronServiceImpl implements CronService {

	// 这里使用静态方法getLogger的时候就可以不写上类名
	private static final Logger LOGGER = getLogger(CronServiceImpl.class);    

toString工具类

打印实体类的参数一般情况下我们是重写toString方法,会让实体类多一些代码,如果不想重写toString方法又可以和toString效果一样,可以使用ReflectionToStringBuilder工具类

System.out.println(ReflectionToStringBuilder.toString(对象, ToStringStyle.MULTI_LINE_STYLE));

Spring提供的Controller层分页类

Pageable类和@PageableDefault注解,如果项目使用Spring data jpa对分页的处理会非常方便,如果不使用jpa单纯的使用分页类也是可以的

@GetMapping
@JsonView(User.UserSimpleView.class)
@ApiOperation(value = "用户查询服务")
public List<User> query(UserQueryCondition condition,
				@PageableDefault(page = 1, size = 10, sort = "username, desc") Pageable pageable) {

mybatis提供的Service层分页插件

1.在pom.xml文件中添加依赖

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.2.12</version>
</dependency>

2.在application.properties配置文件中添加如下配置

# 配置pageHelper分页插件
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql

 3.使用,sql语句不需要写limit这样的分页条件,mybatis插件自动带上了

	/**
	 * 优选会员消费明细
	 *
	 * @author lixx
	 * @date 2019-06-17 11:27:28
	 * @param spendDTO 入参dto
	 * @return json集合
	 */
	@Override
	public RequestResult excellentMemberSpendDetails(CrmCustomExcellentSpendDTO spendDTO) {
		CrmCustomExcellentSpendVO vo = new CrmCustomExcellentSpendVO();
		/**
		 * 分页插件PageHelper
		 * 	参数一:查询的指定参数页
		 * 	参数二:每页查询多少条数据
		 */
		PageHelper.startPage(spendDTO.getPageNum(),spendDTO.getPageSize());
		List<CrmCustomExcellentDetailsVO> list = crmCustomMapper.selectExcellentMemberSpendDetails(spendDTO);
		PageInfo<CrmCustomExcellentDetailsVO> pageInfo = new PageInfo<>(list);
		vo.setList(list);
		// 使用PageInfo.total获取分页总条数
		vo.setTotal(pageInfo.getTotal());
		return RequestResult.success(vo);
	}

SpringBoot的Test测试类

  • 测试方法
import com.zj.br.statistics.BrServiceApplication;
import com.zj.br.statistics.entity.SysTask;
import com.zj.br.statistics.mapper.SysTaskMapper;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 测试方法
 *
 * @author lixx
 * @version 1.0
 * @date 2019-07-22 16:14
 */
@RunWith(SpringRunner.class)
// 测试MVC的方式
//@ContextConfiguration("classpath:application.properties")
// 测试SringBoot的方式
@SpringBootTest(classes = BrServiceApplication.class)
public class CronMapperTest {

	@Autowired
	private SysTaskMapper sysTaskMapper;

	@Before
	public void before(){
		// 可以在这里加一些前置,每次执行@Test之前都会执行该方法
	}

	/**
	 * 测试dao层的mapper
	 *
	 * @author lixx
	 * @date 2019-07-22 16:28:53
	 */
	@Test
	public void testSelectByTaskCode(){
		SysTask tasks = sysTaskMapper.selectByTaskCode("010");
		System.out.println(ReflectionToStringBuilder.toString(tasks, ToStringStyle.MULTI_LINE_STYLE));
	}
}
  • 测试接口

接口的controller

	@PostMapping("/sysTask")
	public List<SysTask> test(String username){
		System.err.println(username);
		List<SysTask> list = new ArrayList<>(3);
		list.add(new SysTask());
		list.add(new SysTask());
		list.add(new SysTask());
		return list;
	}

接口测试类

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
 * 测试接口
 *
 * @author lixx
 * @version 1.0
 * @date 2019-07-22 16:52
 */
@RunWith(SpringRunner.class)
// 测试MVC的方式
//@ContextConfiguration("classpath:application.properties")
// 测试SringBoot的方式
@SpringBootTest(classes = BrWebApplication.class)
public class CronControllerTest {

	@Autowired
	private WebApplicationContext wac;

	private MockMvc mockMvc;

	@Before
	public void setup() {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}

	/**
	 * 测试controller层接口
	 *
	 * @author lixx
	 * @date 2019-07-22 16:53:50
	 */
	@Test
	public void testSysTask() throws Exception {
		String result = mockMvc
				.perform(MockMvcRequestBuilders
						// get的请求方式,请求的url接口路径
						//.get("/sysTask")
						// post的请求方式
						.post("/sysTask")
						// 接口的入参
						.param("username", "lixx")
						// 请求头json和utf-8编码
						.contentType(MediaType.APPLICATION_JSON_UTF8)
				)
				// 断言,预期的状态码成功200,非200测试失败
				// 如果返回结果是乱码, 可以在这里返回ResultActions对象,使用resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");处理乱码
				.andExpect(MockMvcResultMatchers.status().isOk())
				// 断言,预期的状态码是4??的
				// .andExpect(MockMvcResultMatchers.status().is4xxClientError());
				// 断言,预期的结果是个list,长度是3,非指定长度测试失败
				// 这里出现了jsonPath表达式,jsonPath表达式的学习请访问官网https://github.com/json-path/JsonPath
				.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3))
				.andReturn().getResponse().getContentAsString();
		System.err.println(result);
	}
}

@JsonView 注解

在返回相同对象的时候,控制指定的资源路径返回指定的字段,这样说可能不太明白,举例说明就是,有一个User对象中有2个字段username,password, 然后有两个请求地址,请求地址1,/getUserList返回User对象的时候不返回password字段,请求地址2,/getUserInfo返回User对象的时候所有字段都返回

使用步骤:

1、在实体类中使用接口来声明多个视图(一个视图显示所有的字段,一个视图不显示某个字段,所以有多个视图)

2、在实体类中字段的get方法上指定视图

public class User {

	/**
	 * 第一步,在实体类中使用接口来声明多个视图
	 */
	private interface UserSimpleView {};
	private interface UserDetailView extends UserSimpleView {};
	
	private String username;
	
	private String password;

	/**
	 * 第二步,在实体类中字段的get方法上指定视图
	 */
	@JsonView(UserSimpleView.class)
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@JsonView(UserDetailView.class)
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

3、在Controller方法上指定视图

	@GetMapping
	// 第三步、在Controller方法上指定视图
	@JsonView(User.UserSimpleView.class)
	@ApiOperation(value = "用户查询服务")
	public List<User> getUserList() {
		return null;
	}

	@GetMapping("/{id:\\d+}")
	// 第三步、在Controller方法上指定视图
	@JsonView(User.UserDetailView.class)
	public User getInfo(@ApiParam("用户id") @PathVariable String id) {
		return null;
	}

 微信抢红包金额随机分配规则

  • 微信的金额什么时候算?

在拆红包的时候实时计算出来的,不是预先分配好的,因为预先分配好的要在数据库中存入数据占用内存,如果红包个数为1000岂不是要预先存好1000条数据?而实时算出金额就没有这样的问题了。如果是活动红包,每次红包个数固定且数量不是太多可以采用预先分配好的。

  • 金额使用什么规则计算的?

随机计算,计算规则是 在0.01和剩余金额 / 剩余个数 * 2之间的随机数

例如:发100块钱,总共10个红包,那么平均值是10块钱一个,那么发出来的红包的额度在0.01元~20元之间波动。
当前面3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:1~(6000/7*2)=1714之间(单位分),使用Random.nextInt(1714)得到的就是随机金额。

SpringBoot全局异常拦截工具类

application.properties配置文件中添加如下配置

#出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
#不要为我们工程中的资源文件建立映射
spring.resources.add-mappings=false

copy到项目中直接使用

import com.o2oleader.ylpay.usercenter.utils.JsonResult;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.HashMap;
import java.util.Map;

/**
 * Controller层全局异常处理
 *
 * @author lixx
 * @version 1.0
 * @date 2019-10-18 11:18
 */
@RestControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

	/**
	 * 全局所有异常
	 *
	 * @param e 异常信息
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	JsonResult handleException(Exception e) {
		e.printStackTrace();
		String message = null != e.getCause() ? e.getCause().getMessage() : e.getMessage();
		Map<String, Object> map = new HashMap<>();
		map.put("code", 500);
		map.put("status", "failed");
		map.put("message", message);
		return JsonResult.newBuilder().setCode("FAIL").setData(map).builder();
	}

	/**
	 * 拦截指定异常, 给出明确的异常信息
	 * redis连接异常
	 *
	 * @param e 异常信息
	 * @return
	 */
	@ExceptionHandler(RedisConnectionFailureException.class)
	JsonResult redisConnectionFailureException(RedisConnectionFailureException e) {
		e.printStackTrace();
		return JsonResult.newBuilder().setCode("FAIL").setMessage("redis连接异常").builder();
	}

	/**
	 * 拦截指定异常, 给出明确的异常信息
	 * RequestParam注解参数为空异常
	 *
	 * @param e 异常信息
	 * @return
	 */
	@ExceptionHandler(MissingServletRequestParameterException.class)
	JsonResult missingServletRequestParameterException(MissingServletRequestParameterException e) {
		e.printStackTrace();
		return JsonResult.newBuilder().setCode("FAIL").setMessage(e.getParameterName() + " 参数不能为空").builder();
	}


	/**
	 * 拦截指定异常, 给出明确的异常信息
	 * NotBlank、NotNull、NotEmpty注解参数为空异常
	 *
	 * @param e 异常信息
	 * @return
	 */
	@ExceptionHandler(BindException.class)
	JsonResult bindException(BindException e) {
		e.printStackTrace();
		return JsonResult.newBuilder().setCode("FAIL").setMessage(e.getBindingResult().getFieldError().getDefaultMessage()).builder();
	}

	/**
	 * 拦截指定异常, 给出明确的异常信息
	 * CustomRuntimeException自定义异常
	 *
	 * @param e 异常信息
	 * @return
	 */
	@ExceptionHandler(CustomRuntimeException.class)
	JsonResult customRuntimeException(CustomRuntimeException e) {
		e.printStackTrace();
		return JsonResult.newBuilder().setCode("SUCCESS").setMessage(e.getMessage()).builder();
	}

	/**
	 * 拦截指定异常, 给出明确的异常信息
	 * DuplicateKeyException自定义异常
	 *
	 * @param e 异常信息
	 * @return
	 */
	@ExceptionHandler(DuplicateKeyException.class)
	JsonResult duplicateKeyException(DuplicateKeyException e) {
		e.printStackTrace();
		String result = null;
		try {
			String message = e.getCause().getMessage();
			int index = message.indexOf("'");
			index = message.indexOf("'", index + 1);
			result = message.substring(message.indexOf("'") + 1, index);
		} catch (Exception ex) {
			return JsonResult.newBuilder().setCode("FAIL").setMessage("违反唯一性, 数据重复").builder();
		}
		return JsonResult.newBuilder().setCode("FAIL").setMessage("违反唯一性, 数据'" + result + "'重复").builder();
	}

	/**
	 * 404错误
	 *
	 * @return
	 */
	@ExceptionHandler({NoHandlerFoundException.class})
	JsonResult noHandlerFoundException(NoHandlerFoundException e) {
		Map<String, Object> map = new HashMap<>();
		map.put("code", 404);
		map.put("status", "failed");
		map.put("message", "请求路径不存在");
		map.put("path", e.getRequestURL());
		map.put("method", e.getHttpMethod());
		return JsonResult.newBuilder().setCode("FAIL").setData(map).builder();
	}
}
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.io.Serializable;

/**
 * @author keven
 * @date 2018-04-30 下午11:33
 * @Description
 */
public class JsonResult implements Serializable {

	@JsonInclude(JsonInclude.Include.NON_NULL)
	private Object data;
	@JsonInclude(JsonInclude.Include.NON_NULL)
	private Object message;
	private String code;
	@JsonInclude(JsonInclude.Include.NON_DEFAULT)
	private long total;

	public JsonResult() {
	}

	public JsonResult(Object data) {
		this.code = "SUCCESS";
		this.data = data;
	}

	public JsonResult(Object result, String message) {
		this.data = result;
		this.message = message;
		this.code = "FAIL";
	}

	public JsonResult(Object data, Object message, String code, long total) {
		this.data = data;
		this.message = message;
		this.code = code;
		this.total = total;
	}

	public static JsonResult success() {
		return new JsonResult(true);
	}

	public static JsonResult success(Object object) {
		return new JsonResult(object);
	}

	public static JsonResult error() {
		return new JsonResult(false);
	}

	public static JsonResult error(String message) {
		return new JsonResult(false, message);
	}

	/**
	 * @author lixx
	 * @date 2019-07-25 16:18:28
	 */
	public static class Builder {
		private Object data;
		private Object message;
		private String code;
		private long total;

		public JsonResult.Builder setData(Object data) {
			this.data = data;
			return this;
		}

		public JsonResult.Builder setMessage(Object message) {
			this.message = message;
			return this;
		}

		public JsonResult.Builder setCode(String code) {
			this.code = code;
			return this;
		}

		public JsonResult.Builder setTotal(long total) {
			this.total = total;
			return this;
		}

		public JsonResult builder() {
			return new JsonResult(this.data, this.message, this.code == null ? "SUCCESS" : this.code, this.total);
		}
	}

	public static JsonResult.Builder newBuilder() {
		return new JsonResult.Builder();
	}
}

SpringBoot使用切面记录日志

1.引入springboot-aop集成jar

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.application.properties 文件中启用声明

spring.aop.proxy-target-class=true
spring.aop.auto=true

3.自定义一个拦截controller的注解

import java.lang.annotation.*;
 
@Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface SystemControllerLog {
    String description() default "";
}

4.自定义一个拦截service的注解

import java.lang.annotation.*;
 
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemServiceLog {
    String description() default "";
}

5.定义日志切面类

import com.alibaba.fastjson.JSONObject;
import com.o2oleader.ylpay.usercenter.domain.UcUserLog;
import com.o2oleader.ylpay.usercenter.dto.UcUserInfoDto;
import com.o2oleader.ylpay.usercenter.service.UcUserLogService;
import com.o2oleader.ylpay.usercenter.utils.DeviceUtils;
import com.o2oleader.ylpay.usercenter.utils.IPUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 日志切面
 *
 * @author lixx
 * @version 1.0
 * @date 2019-10-14 11:30:15
 */
@Aspect
@Component
public class SystemLogAspect {

	@Autowired
	private UcUserLogService ucUserLogService;

	//本地异常日志记录对象
	private static final Logger LOGGER = LoggerFactory.getLogger(SystemLogAspect.class);

	//Service层切点
	@Pointcut("@annotation(com.o2oleader.ylpay.usercenter.annotation.SystemServiceLog)")
	public void serviceAspect() {
	}

	//Controller层切点
	@Pointcut("@annotation(com.o2oleader.ylpay.usercenter.annotation.SystemControllerLog)")
	public void controllerAspect() {
	}


	/**
	 * 前置通知  用于拦截Controller层记录用户的操作
	 *
	 * @param joinPoint
	 * @param controllerAspect() 表示使用此注解的方法才会进行切面
	 * @return
	 * @author lixx
	 * @date 2019-10-14 14:45:11
	 */
	// @After("controllerAspect()")
	@Deprecated
	public void doBefore(JoinPoint joinPoint) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		HttpSession session = request.getSession();
		// ip地址
		String ip = IPUtils.getRealIP(request);
	}

	/**
	 * 返回结果切面
	 *
	 * @param joinPoint
	 * @param obj
	 * @param controllerAspect() 表示使用此注解的方法才会进行切面
	 * @return
	 * @author lixx
	 * @date 2019-10-14 15:02:20
	 */
	@AfterReturning(pointcut = "controllerAspect()", returning = "obj")
	public void doAfterReturning(JoinPoint joinPoint, Object obj) {
		JSONObject jsonObject = JSONObject.parseObject(obj.toString());
		UcUserInfoDto ucUserInfo = jsonObject.getObject("data", UcUserInfoDto.class);
		// 登录成功的记录信息
		if (null != ucUserInfo) {
			LOGGER.info(" ==========================  记录登录日志...");
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			try {
				UcUserLog ucUserLog = new UcUserLog();
				ucUserLog.setUsername(ucUserInfo.getUsername());
				ucUserLog.setStatus(1);
				ucUserLog.setType(1);
				ucUserLog.setIpAddress(IPUtils.getRealIP(request));
				ucUserLog.setDevice(DeviceUtils.isDevice(request));
				ucUserLog.setRequestUrl(request.getHeader("Referer"));
				ucUserLog.setRequestMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName()));
				ucUserLog.setRequestParams(handlerParameter(joinPoint));
				ucUserLog.setRequestDescription(getControllerMethodDescription(joinPoint));
				ucUserLog.setResponse(JSONObject.toJSONString(ucUserInfo));
				ucUserLog.setGmtCreated(new Date());
				ucUserLogService.insert(ucUserLog);
			} catch (Exception e) {
				LOGGER.error("==========登录日志切面异常: {}===========\n", e.getMessage());
			}
		} else {
			LOGGER.info(" ==========================  登录用户为空...");
		}
	}


	/**
	 * 异常通知 用于拦截service层记录异常日志
	 *
	 * @param joinPoint
	 * @param e
	 * @return
	 * @author lixx
	 * @date 2019-10-14 14:45:24
	 */
	@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        try{
            /*========控制台输出=========*/
            System.out.println("=====异常通知开始=====");
            System.out.println("异常代码:" + e.getClass().getName());
            System.out.println("异常信息:" + e.getMessage());
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
            System.out.println("方法描述:" + getServiceMethodDescription(joinPoint));
        }catch (Exception ex){
            //记录本地异常日志
            LOGGER.error("==异常通知异常==");
            LOGGER.error("异常信息:{}", ex.getMessage());
        }
	}

	/**
	 * 获取注解中对方法的描述信息 用于service层注解
	 *
	 * @param joinPoint
	 * @return
	 * @author lixx
	 * @date 2019-10-14 14:46:00
	 */
	public static String getServiceMethodDescription(JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		String description = "";
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					description = method.getAnnotation(SystemServiceLog.class).description();
					break;
				}
			}
		}
		return description;
	}


	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 *
	 * @param joinPoint
	 * @return
	 * @author lixx
	 * @date 2019-10-14 14:46:11
	 */
	public static String getControllerMethodDescription(JoinPoint joinPoint) throws ClassNotFoundException {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();//目标方法名
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		String description = "";
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					description = method.getAnnotation(SystemControllerLog.class).description();
					break;
				}
			}
		}
		return description;
	}

	private String handlerParameter(JoinPoint point) {
		StringBuilder stringBuilder = new StringBuilder();
		MethodSignature methodSignature = (MethodSignature) point.getSignature();
		String[] parameterNames = methodSignature.getParameterNames();
		// Class[] parameterTypes = methodSignature.getParameterTypes();
		Object[] args = point.getArgs();
		int i = 0;
		for (Object pojo : args) {
			if (parameterNames[i].equals("password")) {
				stringBuilder.append(parameterNames[i]).append("=").append(DigestUtils.md5DigestAsHex(pojo.toString().getBytes())).append("&");
			} else {
				stringBuilder.append(parameterNames[i]).append("=").append(pojo).append("&");
			}
			i++;
		}
		String s = stringBuilder.toString();
		return s.substring(0, s.length() - 1);
	}
}

6.开始使用切面

@RestController
@RequestMapping("/user")
public class LoginController {


	@Autowired
	private LoginService loginService;

	/**
	 * 用户登录
	 *
	 * @param username 用户名
	 * @param password 密码
	 * @return
	 */
	@PostMapping("/login")
	@SystemControllerLog(description = "用户登录")    //这里使用切面注解
	public JsonResult loginAction(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password) {
		Map<String, String> map = new HashMap<>();
		map.put("username", username);
		map.put("password", DigestUtils.md5DigestAsHex(password.getBytes()));
		UcUserInfoDto ucUserInfoDto = loginService.loginService(map);
		return JsonResult.success(ucUserInfoDto);
	}
}
@Service
public class LoginService {

	@SystemServiceLog(description = "用户登录service")    // 这里使用切面注解service
	public UcUserInfoDto loginService(Map<String, String> map) {}
}

SpringBoot接口访问权限,AOP实现

1.引入springboot-aop集成jar

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.application.properties 文件中启用声明

spring.aop.proxy-target-class=true
spring.aop.auto=true

3.自定义一个注解,在接口上使用

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 接口权限注解
 *
 * @author lixx
 * @version 1.0
 * @date 2019-11-01 9:29
 */
@Target(ElementType.METHOD)    // 只能在方法上使用
@Retention(RetentionPolicy.RUNTIME)    // 运行时注解
public @interface Authentication {

	long[] role() default {1};

}

4.定义切面类,处理验证权限的逻辑

import com.o2oleader.ylpay.usercenter.utils.JsonResult;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * 接口访问权限控制
 *
 * @author lixx
 * @version 1.0
 * @date 2019-11-01 9:41
 */
@Aspect
@Component
public class AuthenticationAspect {

	private static Logger logger = LoggerFactory.getLogger(AuthenticationAspect.class);

	// 注解切点
	@Pointcut("@annotation(authentication)")
	public void authenticationService(Authentication authentication) {
	}

	/**
	 * 处理验证权限的逻辑
	 * @param value          = "authenticationService(authentication) // 使用此切点的注解, 参数需要带上切点参数
	 * @param joinPoint      // aop 反射对象信息
	 * @param authentication // 自定义注解
	 * @return 权限通过:放行, 权限未通过,return掉
	 * @throws Throwable	// 环绕通知异常, 表示使用此通知的地方如果发生异常, 这里就会产生异常,使用异常catch块可以处理异常通知
	 */
	@Around(value = "authenticationService(authentication)", argNames = "joinPoint,authentication")
	public Object doAround(ProceedingJoinPoint joinPoint, Authentication authentication) throws Throwable {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String rolePoint = request.getHeader("role");//获取请求头中的权限,多个权限用逗号分隔
		if (StringUtils.isEmpty(rolePoint)) {
			return JsonResult.newBuilder().setCode("FAIL").setMessage("权限验证未通过").builder();
		}
		String[] roleArray = rolePoint.contains(",") ? rolePoint.split(",") : new String[]{rolePoint};
		long[] aims = authentication.role();//获取接口允许的权限
		boolean isPass = false;
		for (long aim : aims) {
			if (Arrays.asList(roleArray).contains(String.valueOf(aim))) {
				isPass = true;
				break;
			}
		}
		if (isPass) {
			return joinPoint.proceed();
		} else {
			logger.error("[authentication] permission verification failed");
			return JsonResult.newBuilder().setCode("FAIL").setMessage("权限验证未通过").builder();
		}
	}
}

5.使用,接口上使用权限注解

	@PostMapping("add")
	@Authentication(role = {1,2})// 权限注解的使用,表示请求接口的请求头拥有指定的一个权限就可以访问
	public JsonResult insertPermissionCode() {	}

对象转Json字符串出现大写变小写问题

解决方法:

	// 如果需要json字符串中的name首字母大写, 注解中的大写, 变量中的小写
	@JSONField(name = "Name")
	private String name;
	
	// 不要写成这样, 这样json字符串中还是小写
	@JSONField(name = "Name")
	private String Name;

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72570.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!