1、注解是什么?
- 注解是往源代码中添加描述信息的机制,这些信息可由编译器测试和验证。
- 该信息后续可通过反射机制提取。然后根据描述,可定义不同的业务逻辑,实现丰富多彩的功能。
注意:注解是静态的,是描述性的,没有任何功能。是注解处理器给了注解强大的力量。
2、注解怎么用?
- 关键词 @interface,表现也更新一个接口,属性按方法用。
- 注解2要素:要明确@Target(作用在类、还是方法)、@Retention(在哪一级,如SOURCE、CLASS、RUNTIME即程序执行时也可提出)。
- 注解中包含一些元素,处理注解时会用到(注解处理器)。
- 用注解处理器,根据标签填写业务逻辑,很强大。
- 注解常用对象:
Object.getAnnotation():从目标对象获取注解对象。
Field.getDeclaredAnnotations():从对象属性获取注解对象。
anns[0] instanceof SQLInteger:注解类型判断。
如,我们定义一个注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
// 注解包含2个信息,且标记了类型,可由编译器检查,是比外部文档好用的地方
public int id();
public String description() default "no descriptino";
}
请看:
- @Target:指这是修饰什么的注解。这里是指描述方法的。
- @Retention:指注解保存在哪里。这里是保存到运行时,也就是会保存到class文件中。
- @interface:是关键字
- name():注解的属性表现的更新方法。
- 但看注解有什么用呢?没用!
注解处理器
有个业务场景,我写了个模块,需求里有n个场景需要覆盖。有没有办法检测我到底覆盖全场景了吗?可以使用上述的注解了。
业务代码:
public class PasswordUtils {
@UseCase(id = 47, description = "Please must contain at least on numric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuffer(password).reverse().toString();
}
@UseCase(id = 49, description = "New password can't equal previously used ones")
public boolean checkForNewPassword(List<String> prevPasswords, String password) {
return !prevPasswords.contains(password);
}
}
注解是静态的,还是没用啊。该注解处理器出厂了。
public class UseCaseTracker {
public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
// cl是目标类,找目标类的方法
for (Method m : cl.getDeclaredMethods()) {
// 找目标类方法上指定类型的注解
UseCase uc = m.getAnnotation(UseCase.class);
if (uc != null) {
System.out.println("Found Use Case:"+uc.id()+" "+uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for (int i : useCases) {
System.out.println("Warning:Missing use case=" + i);
}
/*通过这个例子,得到:
1、注解与源代码关联起来了
2、注解有结构化的数据
3、注解可通过反射机制获取
4、注解处理器可以实现业务逻辑,如这里的代码覆盖案例的情况 */
}
public static void main(String[] args) {
List<Integer> useCases=new ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49,50);
trackUseCases(useCases,PasswordUtils.class);
}
}
输出:
Found Use Case:49 New password can't equal previously used ones
Found Use Case:47 Please must contain at least on numric
Found Use Case:48 no descriptino
Warning:Missing use case=50
3、注解的好处,相对xml配置文件?
1.简化配置,不需要额外的解析工具
2.编译器及可验证正确,防止xml手误,排查半天
缺点:
1.因为是编译进class文件,故修改了文件要重新编译;xml只重启应用即可;
一般:
1.可能修改的如数据源等,用xml配置;
2.ioc用注解。
4、注解实例:
4.1.用注解标记测试用例覆盖度
见上述的例子。
4.2.用注解提起SQL的DDL
注解:
@Target(ElementType.TYPE) //只能注解class
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
public String name() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constrains constraints() default @Constrains;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
int value() default 0;
String name() default "";
Constrains constraints() default @Constrains;
}
业务代码或配置:
@DBTable(name = "MEMBER")
public class Member {
@SQLString(30) String firstName;
@SQLString(50) String lastName;
@SQLInteger Integer age;
@SQLString(value = 30,constraints = @Constrains(primaryKey = true))
String handle;
static int memberCount;
public String getHandle() {
return handle;
}
public String getFirstName() {
return firstName;
}
public String getLastName(){
return lastName;
}
public String toString(){
return handle;
}
public Integer getAge() {
return age;
}
}
注解处理器:
public class TableCreator {
public static void main(String[] args) throws Exception{
if (args.length < 1) {
System.out.println("Arguments:annotaed classes");
System.exit(0);
}
for (String className : args) {
Class<?> cl = Class.forName(className);
/*从目标对象cl获取注解对象*/
DBTable annoDbTable = cl.getAnnotation(DBTable.class);
if (annoDbTable == null) {
System.out.println("No DBTable annotations in class "+className);
continue;
}
String tableName=annoDbTable.name();
if(tableName.length()<1){
tableName = cl.getName().toUpperCase();
}
System.out.println("tableName:"+tableName);
List<String> columnDefs=new ArrayList<String>();
for(Field field:cl.getDeclaredFields()){
String columnName=null;
Annotation[] anns=field.getDeclaredAnnotations();
if(anns.length<1)
continue;
if (anns[0] instanceof SQLInteger) {
SQLInteger sInt=(SQLInteger)anns[0];
if (sInt.name().length() < 1) {
columnName = field.getName().toUpperCase();
} else {
columnName=sInt.name();
}
columnDefs.add(columnName + " INT" + getConstraints(sInt.constraints()));
}
if (anns[0] instanceof SQLString) {
SQLString sString=(SQLString)anns[0];
if (sString.name().length() < 1) {
columnName = field.getName().toUpperCase();
} else {
columnName=sString.name();
}
columnDefs.add(columnName + " varchar(" + sString.value() + ")" + getConstraints(sString.constraints()));
}
StringBuilder createCommand=new StringBuilder("create table "+tableName+"(");
for(String columnDef:columnDefs)
createCommand.append("\n " + columnDef + ",");
String tableCreate = createCommand.substring(0, createCommand.length() - 1) + ");";
System.out.println("Table creation sql for "+className+" is :\n"+tableCreate);
}
}
}
private static String getConstraints(Constrains con) {
String constraints="";
if(!con.allowNull()) constraints += " not null";
if(!con.primaryKey()) constraints += " primary key";
if(con.unique()) constraints += " unique";
return constraints;
}
}
输出:
Table creation sql for com.master.Member is :
create table MEMBER(
FIRSTNAME varchar(30) primary key,
LASTNAME varchar(50) primary key,
AGE INT primary key,
HANDLE varchar(30));
是不配合处理起可以衍生出精彩的功能!
5、注解spring框架中的应用
1.注解实现IOC
注册Bean
@Component("myServiceB")
public class ServiceB implements IService {
@Value("serviceB")
private String name;
@Value("12")
private int age;
public ServiceB() {
System.out.println("========New a ServiceB without parameters");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return this.name+";"+this.age;
}
}
spring获取管理的Bean
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
/*IService serviceA=(ServiceA)ac.getBean("myServiceA");
System.out.println(serviceA);*/
ServiceB serviceB = (ServiceB) ac.getBean("myServiceB");
System.out.println(serviceB);
}
}
就这么简单。
2.注解实现IOC的原理
实现过程分解的很细,但大概原理与上边的例子类似。
简介下别人的总结:
第一步,初始化时设置了Component类型过滤器;
第二步,根据指定扫描包扫描.class文件,生成Resource对象;
第三步、解析.class文件并注解归类,生成MetadataReader对象;
第四步、使用第一步的注解过滤器过滤出有@Component类;
第五步、生成BeanDefinition对象;
第六步、把BeanDefinition注册到Spring容器。以上是@Component注解原理,@Service、@Controller和@Repository上都有@Component修饰,所以原理是一样的。
参考:
https://www.cnblogs.com/wolf-bin/p/11667208.html
《think in java 》第四版