11. jvm之注解

一. 注解基本使用

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

使用@interface的方式新建注释,与创建接口类似

@Target指定注释的所修饰的对象范围

取值(ElementType)有:

  • CONSTRUCTOR:用于描述构造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部变量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述参数
  • TYPE:用于描述类、接口(包括注解类型) 或enum声明

@Retention描述注解的生命周期

  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在运行时有效(即运行时保留)

@Inherited

@Inherited阐述了某个被标注的类型是被继承的。
如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Anno {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Anno2 {
}
@Anno
@Anno2
class Parent {
}
public class Children extends Parent {
    public static void main(String[] args) {
        Annotation[] annos = Children.class.getAnnotations();
        for (Annotation anno : annos) {
            System.out.println(anno.toString());
        }
    }
}

输出结果

@Anno()

即未标识@Inherited的注解未被子类继承

@Repeatable

@Repeatable注解以及其相关特性,是在JDK 8中提供的,简单而言,我们可以设计一种风格的annotation,可以被多次修饰在符合要求的@Target上,在此前一个类型的注释是不能重复修饰在相同地方。


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Anno {
    Anno2[] value();
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Anno.class)
@interface Anno2 {
    String value();
}


@Anno2("1")
@Anno2("2")
class Parent {
}

@Anno({@Anno2("1"), @Anno2("2")})
class Parent2 {
}

public class Children {
    public static void main(String[] args) {
        Annotation[] annos = Parent.class.getAnnotations();
        for (Annotation anno : annos) {
            System.out.println(anno.toString());
        }

        Annotation[] annos2 = Parent2.class.getAnnotations();
        for (Annotation anno : annos2) {
            System.out.println(anno.toString());
        }
    }
}
  • Parent @Anno2("1")与@Anno({@Anno2("1")})这两种注解效果是一致的
  • Anno.class中必须存在value()方法,并且返回值Anno2数组,否则会报错
  • 上述输出结果为@Anno(value=[@Anno2(), @Anno2()]);表示虽然我们类上注解标注的是Anno2,但是实际上的注解是Anno的数组
  • 根据规范,@Scheduled元注解,不能与@Schedules容器注解同时修饰在同一个Target,否则会编译错误

使用方式

@Test
private int i = 0;

二. 注解中可包含方法

  1. 创建方式,和定义接口基本一至,不同的是定义注解可以指定默认值
    如果不指定默认值的话,在使用注解时,必须指定参数值
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
    public String id() default "-1";
    public String name() defaule "轶名";
}
  1. 使用方式,在使用注解时,指定注解属性的值,若不指定则取默认值,并且元素值必须为常量,不可使用变量
@Test(id="1",name="我是测试")
private int i = 0;
  1. 如果只创建注解,使用效果有限,最多只能对字段等进行描述,可对注解创建相应的注解处理器

三. 注解处理器

public class TestAnno { 
private String str = "我是测试2"; 

@Test(id="1",name="我是测试")
private int i = 0; 
public static void main(String []args){
        Field[] fs = TestAnno.class.getDeclaredFields();
        for(Field f : fs){
            Test tAnno = f.getAnnotation(Test.class);
            if(tAnno != null){
                System.out.println("id:"+tAnno.id()+";name:"+tAnno.name());
            }else{
                System.out.println("无注解");
            }
        }
    }
}

通过getAnnotation可以获得方法或字段等域上的注解,获得该注解后,可取得注解时的属性,对根据不同的属性进行不同的操作。

四. 使用java注解时不写属性名会给哪个属性赋值

  • 如果注解只有一个属性,那么肯定是赋值给该属性。
  • 如果注解有多个属性,而且前提是这多个属性都有默认值,那么你不写注解名赋值,会赋值给名字为“value”这属性。
  • 如果注解有多个属性,其中有没有设置默认值的属性,那么当你不写属性名进行赋值的时候,是会报错的。

附件

1. 注解的定义

package net.xinshi.jemall.psh.util.logUtil;

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Json {
    /**
     * 是否需要编码
     * @return
     */
    public boolean urlEncode() default false;
    /**
     * 是否需要添加前缀
     * @return
     */
    public String prefix() default "";
    /**
     * 是否为id
     * @return
     */
    public boolean id() default false;
    /**
     * 将该字段存日志的时候以key赋值的形式存储
     * @return
     */
    public String key() default "";
}

2. 注解的注释

/*
 * 该类使用CodeGen代码生成器生成
 * 生成时间: 2016-10-09 11:37:50.938
 * 生成要求:数据库的主键统一命名为id,int类型
 */
package net.xinshi.jemall.psh.pshKeyWord.bean;

import java.sql.Timestamp;

import net.xinshi.jemall.psh.util.logUtil.Json;

/**
 *搜索词命中率表
 */
public class PSHKeyWord implements java.io.Serializable{

	
    private int id;
    //返回结果条数
    private String count;
    //分站id
    private int mid;
    //城市id
    private int cityid;
    //搜索时间
    private Timestamp searchtime;
    //团类型 0-多品团 1-单品团 
    private int type;
    //用户的userid
    private int userid;
    //备注
    private String remark;
    //搜索词
    private String keyword;
    //solr查询语句
    private String searchQ;
    //渠道信息
    private String channel;

    @Json(id=true)
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }


    /**
     * 返回结果条数
     */
    @Json
    public String getCount() {
        return count;
    }

    public void setCount(String count) {
        this.count = count;
    }
    /**
     * 分站id
     */
    @Json
    public int getMid() {
        return mid;
    }

    public void setMid(int mid) {
        this.mid = mid;
    }
    /**
     * 城市id
     */
    @Json
    public int getCityid() {
        return cityid;
    }

    public void setCityid(int cityid) {
        this.cityid = cityid;
    }
    /**
     * 搜索时间
     */
    @Json(key="JsonTime")
    public Timestamp getSearchtime() {
        return searchtime;
    }

    public void setSearchtime(Timestamp searchtime) {
        this.searchtime = searchtime;
    }
    /**
     * 团类型 0-多品团 1-单品团 2-跨境购
     */
    @Json
    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
    /**
     * 用户的userid
     */
    @Json
    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }
    /**
     * 备注
     */
    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }
    /**
     * 搜索词
     */
    @Json(urlEncode=true)
    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }
    @Json
    public String getSearchQ() {
		return searchQ;
	}

	public void setSearchQ(String searchQ) {
		this.searchQ = searchQ;
	}
    @Json(prefix="PSH")
    public String getChannel() {
        return channel;
    }
    public void setChannel(String channel) {
        this.channel = channel;
    }

	/* 主键相同即认为对象相等. */
    public boolean equals(Object obj) {
        if (this == obj)
            return true;

        if (obj == null || !(obj instanceof PSHKeyWord))
            return false;

        PSHKeyWord bean = (PSHKeyWord) obj;
        if (bean.getId() !=this.getId())
            return false;

        return true;
    }
    @Override
    public int hashCode() {
    	String clazzName = this.getClass().toString();
    	clazzName = clazzName + "_" + this.getId();
    	return clazzName.hashCode();
    }
}

3. 注解的使用

package net.xinshi.jemall.psh.util.logUtil;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.sql.Timestamp;
import java.util.Date;

import org.json.JSONObject;

import net.xinshi.jemall.base.Environment;
import net.xinshi.jemall.commons.StringUtil;
import net.xinshi.jemall.commons.Util;
import net.xinshi.jemall.paymode.util.wechatutil.MD5Util;
import net.xinshi.jemall.psh.pshKeyWord.bean.PSHKeyWord;

/**
 * 用于程序运行期间输出一些运行信息到文件中,如:执行时间、耗时、错误等,可方便的查看程序执行的效率、及正确性与否。
 * 对于调试一些程序执行效果要较长期才能看出,或大数据量处理的有用。
 * 注意:输出信息完后,一定要关闭掉
 */
public class LoggerUtil {
    private PrintWriter myFile; //文件输出流
    private String logFileNameKeyword;//文件名关键字
    private String logFileSrc;//完整的日志目录+日志名称
    private String logFileDir;//日志存放的根目录
    private File logFile;//日志文件
    private StringBuffer strf;//日志内容
    private String dirSub;//子文件夹
    private String defaulePath = "d:\\RunLog";


    public LoggerUtil(String fileNameKeyword, String dirSub) {
        try {
	        this.logFileNameKeyword = fileNameKeyword;
	        if (dirSub != null) {
	        	this.dirSub = dirSub;
	        }
	        String webAppPath = Environment.getEnv().getWebAppPath();
	        if(webAppPath != null){
	        	this.logFileDir = webAppPath;
	        }else{
	        	this.logFileDir = defaulePath;
	        }
	        this.initLogger();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

   
    //创建一个PrintWriter对像,用于写数据,第一次创建的时间同时写入操作文件的信息

    private PrintWriter getPrintWriter() throws Exception {
        try {
	        logFile = new File(this.logFileSrc);
	        if (!logFile.exists()) {
	            logFile.createNewFile();
	        }
            FileWriter fileWriter = new FileWriter(logFile, true);
            myFile = new PrintWriter(fileWriter);
            //文件不存在则创建文件,并记录操作文件信息
            if (!logFile.exists()) {
                logFile.createNewFile();
                myFile.println("当前日志文件:" + logFile.getAbsolutePath());
            }
            return myFile;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 关闭之前打印日志,,创建文件和流
     */
    private String loggering() {
     try {
    	 	//判断是否存在日志
	        if(strf != null && strf.length() ==0){
	        	return null;
	        }
	        String path = this.logFileDir;
	        File file = new File(path);
	        if (!file.exists() || !file.isDirectory()) {
	            file.mkdirs();
	        }
	
	        String dirSubTmp = this.dirSub;
	        if (dirSubTmp != null && dirSubTmp.trim().length() != 0) {
	            path = path + File.separator + dirSubTmp;
	            this.logFileDir = path;
	            File file2 = new File(path);
	            if (!file2.exists() || !file2.isDirectory()) {
	                file2.mkdirs();
	            }
	        }
	        
            String shortDataString = Util.getShortDateFormatByTimestamp(new Timestamp(System.currentTimeMillis()));
            String fileName = this.logFileNameKeyword + "_" + shortDataString + ".log";
            this.logFileSrc=this.logFileDir + "/" + fileName;
            this.myFile = this.getPrintWriter();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }
    /**
     * new DebugLogger 时候创建StringBuffer
     */
    private void initLogger() {
        try {
	        strf = new StringBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //记录一条log记录

    public void log(String content) {
        try {
		  	strf.append(content);
	    } catch (Exception e) {
	    }
    }
    
    //关闭文件
    public void closeLogger() {
        try {
        	String str = this.loggering();
        	if(!StringUtil.isBlank(str)){
        		this.myFile.println(strf.toString());
        		this.myFile.println();
        		this.myFile.flush();
        		this.myFile.close();
        	}
	    } catch (Exception e) {
	    }
    }
    
    public <T> String obj2Json(T t){
    	JSONObject jsonObject = new JSONObject();
		try {
			Class clazz = t.getClass();
			Method[] methods = clazz.getDeclaredMethods();
			for(Method method : methods){
				String fieldValue = getFieldValue(method, t);
				String key = method.getName();
				if(key.indexOf("get") == 0){
					key = key.replaceFirst("get", "");
				}
				key = String.valueOf(key.charAt(0)).toLowerCase()+key.substring(1);
				jsonObject.put(key, fieldValue);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return jsonObject.toString();
    }
    
    private <T> String getFieldValue(Method method,T t){
    	String fieldValue = null;
    	try {
    		Json log = method.getAnnotation(Json.class);
        	if(log == null){
        		return null;
        	}
        	if(method.invoke(t) == null){
        		fieldValue = "";
        	}else{
        		fieldValue = String.valueOf(method.invoke(t));
        	}
    		if(log.id()){
    			fieldValue = t.getClass().getPackage()+"."+t.getClass().getName()+"."+fieldValue.toString();
    			fieldValue = MD5Util.MD5Encode(fieldValue, "utf-8");
    		}
    		if(log.prefix()!=null && !"".equals(log.prefix())){
    			fieldValue = log.prefix()+fieldValue;
    		}
    		if(log.urlEncode()){
    			fieldValue = URLEncoder.encode(fieldValue);
    		}
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return fieldValue;
    }
    
    public static void main(String[] args) {
		LoggerUtil util = new LoggerUtil("", "");
		PSHKeyWord pshKeyWord = new PSHKeyWord();
		pshKeyWord.setCityid(31000);
		pshKeyWord.setCount("10");
		pshKeyWord.setKeyword("牛奶");
		pshKeyWord.setMid(0);
		pshKeyWord.setSearchtime(new Timestamp(new Date().getTime()));
		pshKeyWord.setType(0);
		pshKeyWord.setUserid(8542344);
		pshKeyWord.setSearchQ("q");
		pshKeyWord.setChannel("H5");
		String result = util.obj2Json(pshKeyWord);
		System.err.println(result);
	}
}