python注解实现原理_⾃定义注解!绝对是程序员的利
器!!
相信很多⼈对Java中的注解都很熟悉,⽐如我们经常会⽤到的⼀些如@Override、@Autowired、@Service等,这些都是JDK或者诸如Spring这类框架给我们提供的。
在以往的⾯试过程中,我发现,关于注解的知识很多程序员都仅仅停留在使⽤的层⾯上,很少有⼈知道注解是如何实现的,更别提使⽤⾃定义注解来解决实际问题了。
但是其实,我觉得⼀个好的程序员的标准就是懂得如何优化⾃⼰的代码,那在代码优化上⾯,如何精简代码,去掉重复代码就是⼀个⾄关重要的话题,在这个话题领域,⾃定义注解绝对可以算得上是⼀个⼤⼤的功⾂。
所以,在我看来,会使⽤⾃定义注解 ≈ 好的程序员。
那么,本⽂,就来介绍⼏个,作者在开发中实际⽤到的⼏个例⼦,向你介绍下如何使⽤注解来提升你代码的逼格。
基本知识
在Java中,注解分为两种,元注解和⾃定义注解。
很多⼈误以为⾃定义注解就是开发者⾃⼰定义的,⽽其它框架提供的不算,但是其实上⾯我们提到的那⼏个注解其实都是⾃定义注解。
关于"元"这个描述,在编程世界⾥⾯有都很多,⽐如"元注解"、"元数据"、"元类"、"元表"等等,这⾥的"元"其实都是从meta翻译过来的。
⼀般我们把元注解理解为描述注解的注解,元数据理解为描述数据的数据,元类理解为描述类的类...
所以,在Java中,除了有限的⼏个固定的"描述注解的注解"以外,所有的注解都是⾃定义注解。
在JDK中提供了4个标准的⽤来对注解类型进⾏注解的注解类(元注解),他们分别是:
@Target
@Retention
@Documented
@Inherited
除了以上这四个,所有的其他注解全部都是⾃定义注解。
这⾥不准备深⼊介绍以上四个元注解的作⽤,⼤家可以⾃⾏学习。数字组词有哪些组词
本⽂即将提到的⼏个例⼦,都是作者在⽇常⼯作中真实使⽤到的场景,这例⼦有⼀个共同点,那就是都⽤到了Spring的AOP技术。
什么是AOP以及他的⽤法相信很多⼈都知道,这⾥也就不展开介绍了。
使⽤⾃定义注解做⽇志记录
不知道⼤家有没有遇到过类似的诉求,就是希望在⼀个⽅法的⼊⼝处或者出⼝处做统⼀的⽇志处理,⽐如记录⼀下⼊参、出参、记录下⽅法执⾏的时间等。
如果在每⼀个⽅法中⾃⼰写这样的代码的话,⼀⽅⾯会有很多代码重复,另外也容易被遗漏。
这种场景,就可以使⽤⾃定义注解+切⾯实现这个功能。
假设我们想要在⼀些web请求的⽅法上,记录下本次操作具体做了什么事情,⽐如新增了⼀条记录或者删除了⼀条记录等。
立思丁的使用方法
⾸先我们⾃定义⼀个注解:
快速开发app软件/**
* Operate Log 的⾃定义注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OpLog {
/**
* 业务类型,如新增、删除、修改
*
* @return
*/
public OpType opType();
/**
* 业务对象名称,如订单、库存、价格
*
* @return
*/
public String opItem();
/**
* 业务对象编号表达式,描述了如何获取订单号的表达式
*
* @return
常指针和指针常量
*/
public String opItemIdExpression();
}
因为我们不仅要在⽇志中记录本次操作了什么,还需要知道被操作的对象的具体的唯⼀性标识,如订单号信息。
但是每⼀个接⼝⽅法的参数类型肯定是不⼀样的,很难有⼀个统⼀的标准,那么我们就可以借助Spel表达式,即在表达式中指明如何获取对应的对象的唯⼀性标识。
有了上⾯的注解,接下来就可以写切⾯了。主要代码如下:
/**
* OpLog的切⾯处理类,⽤于通过注解获取⽇志信息,进⾏⽇志记录
*
* @author Hollis
*/
@Aspect
@Component
public class OpLogAspect {
private static final Logger LOGGER = Logger(OpLogAspect.class);
@Autowired
HttpServletRequest request;
@Around("@annotation(com.hollis.annotation.OpLog)")
public Object log(ProceedingJoinPoint pjp) throws Exception {
Method method = ((Signature()).getMethod();
OpLog opLog = Annotation(OpLog.class);
Object response = null;
try {
// ⽬标⽅法执⾏
response = pjp.proceed();
} catch (Throwable throwable) {
throw new Exception(throwable);
}
if (StringUtils.isNotEmpty(opLog.opItemIdExpression())) {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(opLog.opItemIdExpression());
EvaluationContext context = new StandardEvaluationContext();
// 获取参数值
Object[] args = Args();
// 获取运⾏时参数的名称
LocalVariableTableParameterNameDiscoverer discoverer
= new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = ParameterNames(method);
// 将参数绑定到context中
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
}
// 将⽅法的resp当做变量放到context中,变量名称为该类名转化为⼩写字母开头的驼峰形式
if (response != null) {
context.setVariable(
CaseFormat.(CaseFormat.LOWER_CAMEL, Class().getSimpleName()),
response);
}
// 解析表达式,获取结果
String itemId = String.Value(context));
// 执⾏⽇志记录
python基础代码注释
handle(opLog.opType(), opLog.opItem(), itemId);
}
return response;
}
private void handle(OpType opType, String opItem, String opItemId) {
// 通过⽇志打印输出
LOGGER.info("opType = " + opType.name() +",opItem = " +opItem + ",opItemId = " +opItemId);
}
}
以上切⾯中,有⼏个点需要⼤家注意的:
1、使⽤@Around注解来指定对标注了OpLog的⽅法设置切⾯。
2、使⽤Spel的相关⽅法,通过指定的表⽰,从对应的参数中获取到⽬标对象的唯⼀性标识。
3、再⽅法执⾏成功后,输出⽇志。
有了以上的切⾯及注解后,我们只需要在对应的⽅法上增加注解标注即可,如:
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
@OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#id")
public @ResponseBody
HashMap view(@RequestParam(name = "id") String id)
throws Exception {
}
上⾯这种是⼊参的参数列表中已经有了被操作的对象的唯⼀性标识,直接使⽤#id指定即可。
如果被操作的对象的唯⼀性标识不在⼊参列表中,那么可能是⼊参的对象中的某⼀个属性,⽤法如下:
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})模块建房和砖混建房哪个结实
@OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#orderVo.id")
public @ResponseBody
HashMap update(OrderVO orderVo)
throws Exception {
}
以上,即可从⼊参的OrderVO对象的id属性的值获取。
如果我们要记录的唯⼀性标识,在⼊参中没有的话,应该怎么办呢?最典型的就是插⼊⽅法,插⼊成功之前,根本不知道主键ID是什么,这种怎么办呢?
我们上⾯的切⾯中,做了⼀件事情,就是我们把⽅法的返回值也会使⽤表达式进⾏⼀次解析,如果可以解析得到具体的值,可以是可以。如以下写法:
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
@OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#insertResult.id")
public @ResponseBody
InsertResult insert(OrderVO orderVo)
throws Exception {
return orderDao.insert(orderVo);
}
以上,就是⼀个简单的使⽤⾃定义注解+切⾯进⾏⽇志记录的场景。下⾯我们再来看⼀个如何使⽤注解做⽅法参数的校验。
使⽤⾃定义注解做前置检查
当我们对外部提供接⼝的时候,会对其中的部分参数有⼀定的要求,⽐如某些参数值不能为空等。⼤多数情况下我们都需要⾃⼰主动进⾏校验,判断对⽅传⼊的值是否合理。
这⾥推荐⼀个使⽤HibernateValidator + ⾃定义注解 + AOP实现参数校验的⽅式。
⾸先我们会有⼀个具体的⼊参类,定义如下:
public class User {
private String idempotentNo;
@NotNull(
message = "userName can't be null"
)
private String userName;
}
以上,对userName参数注明不能为null。
然后再使⽤hibernate validator定义⼀个⼯具类,⽤于做参数校验。
/**
* 参数校验⼯具
*
* @author Hollis
*/
public class BeanValidator {
private static Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(true)
.buildValidatorFactory().getValidator();
/**
* @param object object
* @param groups groups

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。