怎样理解 java 注解和运用注解编程?

完整源码下载地址 GitHub - MatrixSeven/JavaAOP: 一个基于原生JDK动态代理实现的AOP小例子 使用反射结合JDK动态代理实现了类似于Spring框架的简单“ @xxx(xx="xxxx") ”的数据注入。

作者:Accelerator 链接:https://www.zhihu.com/question/47449512/answer/106034220 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 注解基础知识点 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。 作用分类: ①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】 ② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】 ③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】 and so on….如果你对注解(Annotation)还没有了解请左移百度百科:AnnotationJava 注解

啊,,是不是很眼熟,这个不就是经常看到的那个@Override神马的么…原来这个东西就叫做注解啊. —–引至百度:“我个人使用的理解,annotation是一种在类、类型、属性、参数、局部变量、方法、构造方法、包、annotation本身等上面的一个附属品(ElementType这个枚举中有阐述),他依赖于这些元素而存在,他本身并没有任何作用,annotation的作用是根据其附属在这些对象上,根据外部程序解析引起了他的作用。”例如编译时的,其实编译阶段就在运行:java Compiler,他就会检查这些元素,例如:@SuppressWarnings、@Override、@Deprecated等等;

的确是,但是想想Spring的IOC依赖注入,控制反转@xxx(xxx=xxx)然后Bean里就被赋值了,是不是觉得好帅,哈,是不是也觉得好神奇? 是不是我创建一个注解,然后把他作用到类上就能赋值了?赶紧试一试…

首先,创建一个注解,用关键字@interface来声明,这是一个注解类.@Target来声明注解目标, @Retention用来说明该注解类的生命周期.

package proxy.annon;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface Seven {
 
    public String value() default "小黑";
 
    public String Property() default "无属性";
 
}

然后还用咱们昨天的写的JavaBean,加上注解后的样子就是:

package proxy;
 
import proxy.annon.Seven;
import proxy.imp.AnimalInterface;
 
public class DogImp implements AnimalInterface {
 
    @Seven(value = "Lumia")
    private String name;
 
    private String Property;
 
    public DogImp() {
    }
 
    @Override
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public String getName() {
        return this.name;
    }
 
    @Override
    public void say() {
        System.out.println("小狗:汪汪汪汪.....");
    }
 
    @Override
    @Seven(Property = "水陆两栖战士")
    public void setProperty(String Property) {
        this.Property = Property;
    }
 
    @Override
    public void getProperty() {
        System.out.println(this.name + this.Property);
    }
}

迫不及待运行下:

public class Test {
 
    public static void main(String[] args) {
        DogImp dogImp = new DogImp();
        System.out.println(dogImp.getName());
        dogImp.getProperty();
    }
}
 
输出:
null
nullnull

额.好伤心,表示并没有什么效果,那@的注入到底是怎么实现的呢…. 转了一圈,还得依赖咱们的反射大军哈哈….. 赶紧改造下昨天的Demo,让其能够注解..咱们一起来… 注解已经创建好了,怎么让咱们的注解产生效果呢,赶紧动手写个AnnoInjection类. 用来实现注解的内容的注入.. 但是要注意,让注如属性的时候,一定要有对用的get/set方法,如果访问级别为private则可以直接使用 属性的set(obj, value),如果为public,则需要自己获取方法,然后调用方法的invoke

package proxy.annon;
 
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class AnnoInjection {
 
    public static Object getBean(Object obj) {
        try {
            // 获得类属性
            Field f[] = obj.getClass().getDeclaredFields();
            // 遍历属性
            for (Field ff : f) {
                // 获得属性上的注解
                Seven s = ff.getAnnotation(Seven.class);
                if (s != null) {
                    System.err.println("注入" + ff.getName() + "属性" + "tt" + s.value());
                    // 反射调用public set方法,如果为访问级别private,那么可以直接使用属性的set(obj,
                    // value);
                    obj.getClass()
                            .getMethod("set" + ff.getName().substring(0, 1).toUpperCase() + ff.getName().substring(1),
                                    new Class[] { String.class })
                            .invoke(obj, s.value());
                }
            }
            // 获得所有方法
            Method m[] = obj.getClass().getDeclaredMethods();
            for (Method mm : m) {
                // 获得方法注解
                Seven s = mm.getAnnotation(Seven.class);
                if (s != null) {
                    System.err.println("注入" + mm.getName() + "方法" + "t" + s.Property());
                    mm.invoke(obj, s.Property());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
 
}

这样呢,就实现了属性的方法的注入..在哪里调用呢……. 哦,,查看之前的AnimalFactory代码,会发现在getAnimalBase里有

Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new AOPHandle(obj,method))

很明显,咱们的动态代理new AOPHandle(obj,method)时传入了反射生成obj.然后实现代理拦截直接, 咱们来先处理这个obj.所以咱们简单修改下这个就好了 修改后的样子:

/***
     * 获取对象方法
     * @param obj
     * @return
     */
    private static Object getAnimalBase(Object obj,AOPMethod method){
        //获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new AOPHandle(AnnoInjection.getBean(obj),method));
    }

soga….看起来完美了,,赶紧跑起来试一试..

不对..之前的方法拦截太罗嗦,咱们只拦截getProperty方法..好吧 要不然输出太恶心了…. 修改后的AOPTest….

package proxy;
 
import java.lang.reflect.Method;
 
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;
 
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
 
    public static void main(String[] args) {
 
        AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() {
            // 这里写方法执行前的AOP切入方法
            public void before(Object proxy, Method method, Object[] args) {
                if (method.getName().equals("getProperty")) {
                    System.err.println("成功拦截" + method.getName() + "方法,启动");
                }
            }
 
            // 这里系方法执行后的AOP切入方法
            public void after(Object proxy, Method method, Object[] args) {
                if (method.getName().equals("getProperty"))
                    System.err.println("成功拦截" + method.getName() + "方法,结束");
 
            }
        });
        dog.say();
        String name1 = "我的名字是" + dog.getName();
        System.out.println(name1);
        dog.setName("二狗子");
        String name2 = "我的名字是" + dog.getName();
        System.out.println(name2);
        dog.getProperty();
    }
}

然后给我run起来…. beauty,成功出来了呢。。。

输出顺序可能不一致,这是因为输出流缓存

在每句的输出语句后面flush就好了。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏纯洁的微笑

Java 8的新特性—终极版

1. 简介 毫无疑问,Java 8是Java自Java 5(发布于2004年)之后的最重要的版本。这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特...

42860
来自专栏搜云库

BTA 常问的 Java基础40道常见面试题及详细答案

最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案。

62260
来自专栏小樱的经验随笔

【Java学习笔记之二十八】深入了解Java8新特性

前言: Java 8 已经发布很久了,很多报道表明java 8 是一次重大的版本升级。在Java Code Geeks上已经有很多介绍Java 8新特性的文章,...

37770
来自专栏光变

3.1 ASM-方法-结构

ASM-方法-结构 本章将会介绍如果使用ASM core API生成或者转换Java编译后的method。 本将开始会展示编译后的method,然后使用很多说...

16720
来自专栏ImportSource

Java8真不用再搞循环了?

Java8以后真的不用循环了?真的不用了? 好吧,本文分享的内容是java8之前和java8之后一些代码的不同写法,我们会先介绍java8之前和java8之后不...

2.7K110
来自专栏java思维导图

总结:JDK1.5-JDK1.8各个新特性

作者:iwen-J | 链接:https://my.oschina.net/zhuqingbo0501/blog/1784693 JDK各个版本的新特性 以...

69170
来自专栏Jimoer

在Java的反射中,Class.forName和ClassLoader的区别

最近在面试过程中有被问到,在Java反射中Class.forName()加载类和使用ClassLoader加载类的区别。当时没有想出来后来自己研究了一下就写下来...

17620
来自专栏java、Spring、技术分享

深入分析Spring Formatter

  在Web项目中,通常需要将数据转换为具有某种格式的字符串进行展示,因此Spring3引入了格式化转换器(Formatter SPI) 和格式化服务API(F...

16030
来自专栏积累沉淀

【译】Java 8的新特性—终极版

声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:...

260100
来自专栏Java开发

mybatis foreach多次遍历问题

使用MyBatis foreach循环的时候出现了个问题,第一次循环使用的变量 第二次再次循环使用的时候 出现了报错

42020

扫码关注云+社区

领取腾讯云代金券