美文网首页
第2讲.注解/JavaBean/内省机制

第2讲.注解/JavaBean/内省机制

作者: 祥祺 | 来源:发表于2020-02-18 20:15 被阅读0次

注解/JavaBean/内省机制

加载配置文件

什么是硬编码?

在Java开发中,我们很多地方都需要使用到配置文件
以前我们所有的数据都是放在Java代码中的,

如:
String username=”admin”;
String password=”123”;

在今后,我们的密码是可以修改的,所以在登录的时候需要将Java代码中写死的密码同时修改。
首先要找到项目的源码--->再修改对应的Java代码--->编译---->发布

总结:硬编码的概念。

1.将数据写死在Java代码中

2.后期会不定时的去做修改

解决方案:使用配置文件解决硬编码问题。

把数据写到一个配置文件中,通过Java在运行时去读取里面的数据

什么是配置文件

在java的开发中,配置一些以后可能经常修改的数据,参数到一个文件中,这个文件存放在项目中,通过写代码进行加载该文件,并进行读取文件中存的数据。

配置文件特点:无论是开发过程中还是编译之后,还是生产环境中,都是一个状态

编写配置文件的分类

在java中,配置文件分为两类。

一个是以.properties为扩展名的文件

配置文件是以properties为扩展名的,其内容为键值对形式存储,且键名和键值都是字符串格式。JAVA提供java.util.Properties类,可以非常方便的读取配置文件的信息。

java在对*.properties文件进行操作的时候,实际上是通过IO对文档进行逐行的扫描,然后将文中非注释的部分存放在一个properties对象中。Properties 实际上是继承了hashtable,实现了Map接口。可以这样理解,它是进行了进一步封装的HashMap。存放到properties中后,可以对properties进行一系列的操作,此时的数据保存在内存中。最后,当需要保存数据的时候,是将properties中所有的键值重新写入到文件中去。 对properties文件的操作,jdk提供了一系列的API。实现了对properties文件的增删查改的功能。

一个是以.xml为扩展名的文件

是一种可扩展的标记语言,类似HTML。

文件是使用一堆标签来包装数据的。

一般存储的数据具有一定的结构性,层次性。例如省市区。

两种配置文件相比较,第一种编写起来和使用起来更为简单些。

编写properties配置文件

选中项目右击鼠标,选择SourceFolder选项,创建一个resources文件夹,这个文件夹,是专门用来存储配置文件的。

在开发工具中,源文件夹(Source Folder)中的资源都会同步输出到字节码输出目录。

什么是字节码输出目录?

Java源文件编译后,字节码存放的目录,对于STS开发工具来讲默认就在当前项目的bin目录中

我们在Java开发中,如果要加载外部的资源到JVM中通常情况下都是从字节码输出目录中加载的,因为对于Java程序而言无论什么情况下,都一定会有字节码输出目录的

加载properties配置文件

类加载器是把Java字节码加载到JVM中的工具,其主要是从字节码输出目录中加载

类加载器可以从字节码的输出目录中加载资源

ClassLoader类中的方法:

InputStream getResourceAsStream(String name) : 从字节码输出路径中加载资源

读取到的数据在流中,但是取出数据操作时存在很多的不方便

在java中提供了Properties类来方便我们操作流中的属性信息

    properties = new Properties();

//加载配置文件方式一:(不推荐) 通过类的加载器从项目的编译路径classPath路径加载 ,不安全

//InputStream inputStream = 当前类.class.getClassLoader().getResourceAsStream("config.properties");

//加载配置文件方式二:(推荐)通过Thread类来获取当前类的类加载器 

InputStream inputStream = Thread.currentThread().getContextClassLoader()

                .getResourceAsStream("config.properties");

properties.load(inputStream); 

JavaBean/内省机制

什么是JavaBean?

JavaBean 是一种JAVA语言写成的可重用组件。

为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。

JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制发现和操作这些JavaBean 的属性。

JavaBean中的属性和字段

字段:在类中定义的成员变量

private String name;

属性:由类中的getter或setter方法决定

    getName--->属性名是去掉get,将首字母小写得到的名称就是属性名:name

    setName---->属性名是去掉set,将首字母小写得到的名称就是属性名:name

字段名和属性名绝大多数时候是一样的,但是可以不一样

注意:普通的字段对应的get方法就是getXxx();

如果字段类型是boolean,那么对应的get方法是:isXxx();

JavaBean的类中包含的成员

1.字段

2.方法

3.事件:了解,SE中的内容

Introspector:内省机制核心类

如果使用反射,我们可以操作JavaBean中的字段/方法/构造器等,如果我们在开发中需要大量的操作JavaBean的属性(getter/setter方法)

所以,我们可以选择一种更专业的手段来操作JavaBean的属性---内省机制(底层使用反射实现)

内省机制的主要作用:用于操作javabean中的属性

操作步骤:

1.获取JavaBean相关的信息对象:BeanInfo

2.该BeanInfo中就会封装有当前Bean的成员(字段/属性/事件)

3.获取到对应的属性,对其操作

java.beans.Introspector类:

常用API:

static BeanInfo getBeanInfo(Class<?> beanClass) : 获取字节码对象对应的JavaBean信息 

static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)

通过一张图来认识一下这两个API的区别。

1.png

java.beans.BeanInfo接口:

常用API:

PropertyDescriptor[] getPropertyDescriptors() : 获取所有的属性描述器

java.beans.PropertyDescriptor类:

常用API:

String getName() : 获得属性的名称 

Class<?> getPropertyType() : 获得属性的类型 

Method getReadMethod() : 获得用于读取属性值的方法

Method getWriteMethod() : 获得用于设置属性值的方法

lombok插件

Lombok是什么

Lombok是一款小巧的代码生成工具。官方网址:http://projectlombok.org/

LomBok主要特性有:自动生成默认的getter/setter方法、自动化的资源管理(通过@Cleanup注解)及注解驱动的异常处理等。目前在国外广泛应用。

LomBok它和jquery一样,目标是让程序员写更少的代码,以及改进一些原始语法中不尽人意的地方。Lombok能做到这一点。既不是用annotations process,也不是用反射。而是直接黑到了编译过程中。所以对运行效率没有任何影响,我们可以通过反编译class文件进行验证。

为何项目中要引入Lombok

主要因为以下三点:

    1. 提高开发效率
    1. 使代码直观、简洁、明了、减少了大量冗余代码(一般可以节省60%-70%以上的代码)
    1. 极大减少了后期维护成本

如何安装,如何使用

  • 1:java -jar lombok.jar

  • 2:在Java项目中导入lombok.jar包

  • 3:使用注解@Setter/@Getter/@ToString/@Data/@AllArgsConstructor/@NoArgsConstructor等

插件lombok的安装流程图
3.png 4.png

流程图中通过java -jar的命令,来安装lombok插件。其本质是:

5.png
lombok插件的使用流程图
6.png 7.png 8.png 9.png 10.png

JavaBean和MAP的互转

Map是由key-value组成,key是不能重复的.

JavaBean是由属性名和属性值组成,属性名是不同的.

如果把JavaBean的属性名看做是Map的key,把属性值看做是Map的value,那么一个Map对象和一个JavaBean是等级的.
如图:

2.png
// 需求:把一个javaBean 转换成Map
    public Map<String, Object> bean2Map(Object bean) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        // 获取传入对象的字节码对象
        Class<? extends Object> clz = bean.getClass();
        // 通过内省获取该类的BeanInfo对象
        BeanInfo beanInfo = Introspector.getBeanInfo(clz, Object.class);
        // 通过BeanInfo对象获取属性描述器
        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : descriptors) {
            // 获取属性名称
            String name = propertyDescriptor.getName();
            // 获取getter方法对象
            Method readMethod = propertyDescriptor.getReadMethod();
            // 通过反射执行方法获取值
            Object value = readMethod.invoke(bean);
            map.put(name, value);
        }
        return map;
    }

// 需求:把一个Map转换成javaBean
    public <T> T map2Bean(Map<String, Object> map, Class<T> clz) throws Exception {
        // 根据字节码对象获取类的真实对象
        T instance = clz.newInstance();
        // 通过内省获取该类的BeanInfo对象
        BeanInfo beanInfo = Introspector.getBeanInfo(clz, Object.class);
        // 通过BeanInfo对象获取属性描述器
        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : descriptors) {

            // 属性名称作为map中的key,根据key获取value
            Object value = map.get(propertyDescriptor.getName());
            // 获取方法对象
            Method writeMethod = propertyDescriptor.getWriteMethod();
            // 把value 作为参数,通过反射调用setter方法
            writeMethod.invoke(instance, value); // 相当于instance.setXxx(value) 方法
        }
        return instance;
    }
}

注解

什么是注解

java.lang.annotation,接口 Annotation。

对于Annotation,是Java5的新特性,JDK5引入了Metadata(元数据)很容易的就能够调用Annotations。Annotations提供一些本来不属于程序的数据,比如:一段代码的作者或者告诉编译器禁止一些特殊的错误。An annotation 对代码的执行没有什么影响。Annotations使用@annotation的形式应用于代码:类(class),属性(attribute),方法(method)等等。一个Annotation出现在上面提到的开始位置,而且一般只有一行,也可以包含有任意的参数。

通过一张图来深入理解一下注解的含义:

11.png

使用注解需要注意,必须有三方参与才有意义:

1):得有注解标签本身;
2):被贴的程序元素(类,字段,构造器,方法,参数上,包上等);
3):由第三方的程序使用反射的手段来赋予注解特殊的功能(也是Java代码). 

JDK中的内置注解

public class CommonAnnotation {

    // JDK5中提供的三个注解
    /*
    @Override    只能贴在方法上
    @Deprecated   可以贴在变量上,方法上, 类上 
    @SuppressWarnings 可以贴在变量上,方法上,类上
    */

    /*作用:表示该方法是重写父类的方法,
     会帮我们验证方法名称是否和父类中的方法名称是否一致*/
    @Override
    public String toString() {
        return super.toString();
    }

    /*作用:表示该方法已经过时,
    不建议使用,但是可以使用*/
    @Deprecated
    private void MylocalString() {

    }

    /*作用:表示抑制警告,
    只是看不到警告,不代表没有警告*/
    @SuppressWarnings("rawtypes")
    List list = new ArrayList();

    // JDK7中新增了一个注解
    /*
     @SafeVarargs 一个方法中同时出现可变参数和泛型
     */
    /*抑制堆污染发出的警告
    问题依然存在*/
    @SafeVarargs
    public static <T> List<T> asList(T... a) {
        return null;
    }

    // JDK8中增加一个注解
    /*@FunctionalInterface  函数式接口 
     */
    /*接口中只有一个抽象方法*/
    @FunctionalInterface
    public interface MyRunnable {
        void run();
    }
}

JDK中的元注解

元注解有4个:

@Retention、@Target、@Documented、@Inherited

其中@Documented、@Inherited只需要了解

@Documented 表示注解会被javadoc指令编辑到API中

@Inherited 表示注解会遗传给子类


@Target

@Target:决定了该注解可以贴在什么地方

可以贴注解的地方有很多,都封装在枚举:ElementType中

ElementType.ANNOTATION_TYPE 贴在注解上

ElementType.CONSTRUCTOR  贴在构造方法上    

ElementType.FIELD  贴在字段上(包括枚举常量)

ElementType.LOCAL_VARIABLE   贴在局部变量上          

ElementType.METHOD  贴在方法上

ElementType.PACKAGE  贴在包上(极少使用) 

ElementType.PARAMETER  贴在参数上 

ElementType.TYPE 贴在类、接口或枚举上

其中贴在类/字段/方法上要重点知道,以后要用到


@Retention

@Retention:决定注解可以保存到哪个时期

注解的有效期有3个都封装在枚举:RetentionPolicy中

RetentionPolicy.SOURCE: 表示注解只存在于源文件中,不会被编译到字节码中

RetentionPolicy.CLASS:  表示注解会被编译到字节码中,但是JVM不加载注解

RetentionPolicy.RUNTIME: 表示注解会被编译到字节中,会随着字节码的加载而进入JVM,因此可以反射性地读取

通过一张图来区分三者的有效期的不同

12.png

我们开发中的自定义的注解的有效期都要使用RUNTIME,这样才能在程序运行时使用反射赋予注解功能

反编译演示有些注解不会被编译到字节码中

思考:

自定义的注解应该保存到哪个时期呢?

RUNTIME时期,为啥?

需要使用反射来赋予注解特殊的含义,反射是在运行阶段才生效

定义注解的语法

使用@interface 注解名称

    例如:public @interface VIP {
            //抽象方法 属性
          }

注解的抽象方法称为属性,如果要给这个属性赋予默认值可以在抽象方法后使用default 值


使用注解语法:@注解名[(属性名=属性值,属性名=属性值)]

例如:@VIP

注意:给属性赋值时如果只有1个属性要赋值且名称叫value时,可以省略value不写 如:@VIP("xxx")


注意:属性的返回值类型只能是基本类型/枚举/Class/注解/String/枚举以及他们各自的数组

自定义注解的步骤:

1.自定注解: public @interface 注解名称{}

2.说明当前方法能够贴的位置和能够保存的时期

@Target:  值保存在ElementType枚举类中

@Retention: 值保存在RetentionPolicy枚举类中

3.将注解贴在能够贴的位置

4.为注解传递参数

    需要在注解中定义属性

语法:和接口中定义抽象方法是一样的

类型  属性名()  default  默认值 ;

特殊使用: 如果属性名是value,同时在使用的时候我们只需要为value设值的话,此时可以省略value属性名

例如:在后面的Servlet中,会用到一个注解: @WebServlet(“/hello”)

操作注解:

意识:注解是贴在类上,方法上,字段上等位置

所以,对应的Class/Method/Field类上应该会有操作注解的方法

<T extends Annotation>  T   getAnnotation(Class<T> annotationClass) 

自定义注解的案例:

MyAnnotation.java

@Target({ ElementType.METHOD, ElementType.TYPE }) // 确定作用目标
@Retention(RetentionPolicy.RUNTIME) // 存活周期
public @interface MyAnnotation {
    String value() default "少壮不努力老大徒伤悲";
    //
    //  int age();
    //
    //  String name();

    /*
     注意:
      ElementType.METHOD 表示可以作用在方法上,
      ElementType.TYPE 表示可以作用在类上,接口上,
      RetentionPolicy.RUNTIME 表示当前注解可以在运行阶段使用
         如果在使用注解的时候,属性名为value的属性传值并且注解中只有这一个抽象方法,
         此时可以省略属性名
        可以通过default 来设置默认值
    */
}

Employee.java

@MyAnnotation("哈哈")
// 注解括号中的key=value 内容 相当于注解中的抽象方法的实现
public class Employee {

    @MyAnnotation(value = "哈哈")
    public void init() {
        System.out.println("init 方法 ");
    }
}

AnnotationTest.java

public class AnnotationTest {

    // 通过反射获取注解的内容
    @Test
    public void testAnno() throws Exception {
        // 通过反射获取Employee的字节码对象
        Class<?> clz = Class.forName("cn.wolfcode.day02._02_.annotation.Employee");
        // 通过字节码对象获取自定义注解对象
        MyAnnotation annotation = clz.getAnnotation(MyAnnotation.class);
        // 通过注解对象调用自身的抽象方法获取对应的值
        String value = annotation.value();
        System.out.println(value);

        Method method = clz.getMethod("init");
        MyAnnotation annotation2 = method.getAnnotation(MyAnnotation.class);
        System.out.println(annotation2.value());
}
}

相关文章

  • 第2讲.注解/JavaBean/内省机制

    注解/JavaBean/内省机制 加载配置文件 什么是硬编码? 在Java开发中,我们很多地方都需要使用到配置文件...

  • 内省机制

    内省机制(操作javaBean的信息) ----是不是联想到了反射机制了哈,这两者有什么区别呢? 1、内省机制和反...

  • day02-注解&JavaBean&内省机制

    ----------------------写代码过程中的一些新的体会.1.如果没有get/set. 就算你有字段...

  • 2 内省

    1、内省(Introspector) — JavaBean内省基于反射实现,主要用于操作JavaBean,通过内省...

  • Java内省机制(Introspector)

    内省 通过反射的方式操作 JavaBean 的属性内省基于反射实现,主要用于操作JavaBean,Introspe...

  • Java学习之内省、注解与类加载器

    一、由内省IntroSpector引出JavaBean 1、概述: 1、IntroSpector:即内省,是对内部...

  • Java基础

    一、反射&内省 1、反射 反射会有些许性能消耗,因为它把装载期做的事情搬到了运行期。 2、JavaBean内省 内...

  • excel2javaBean注解的使用

    编者按 excel2javaBean注解参数一览 目录 一、 常用注解 二、 使用方法 一、常用注解 1.1 @...

  • javabean的内省

    javabean的一部分可以看做是对数据的封装,其向外暴露出数据的getter/setter方法, 用框架开发时,...

  • Java内省技术Introspector

    首先清楚内省是什么? 内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺...

网友评论

      本文标题:第2讲.注解/JavaBean/内省机制

      本文链接:https://www.haomeiwen.com/subject/hpmpyqtx.html