Java SE 学习笔记(十八)—— 注解、动态代理

news2024/12/22 22:25:05

目录

  • 1 注解
    • 1.1 注解概述
    • 1.2 自定义注解
    • 1.3 元注解
    • 1.4 注解解析
    • 1.5 注解应用于 junit 框架
  • 2 动态代理
    • 2.1 问题引入
    • 2.2 动态代理实现

1 注解

1.1 注解概述


Java 注解(Annotation)又称Java标注,是JDK 5.0引入的一种注释机制,Java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注,至于到底做何种处理由业务需求来决定。

例如: JUnit 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。

在这里插入图片描述

1.2 自定义注解


自定义注解就是自己做一个注解来使用。

自定义注解的格式

在这里插入图片描述

示例代码

public class AnnotationDemo1 {

    @MyBook(name="《精通JavaSE2》",authors = {"小明", "dlei"} , price = 199.5)
    private AnnotationDemo(){

    }

    @MyBook(name="《精通JavaSE1》",authors = {"小明", "dlei"} , price = 199.5)
    public static void main(String[] args) {
        @MyBook(name="《精通JavaSE2》",authors = {"小明", "dlei"} , price = 199.5)
        int age = 21;
    }
}

// 自定义注解
@interface MyBook {
    String name() ;
    String[] authors();
    double price();
}

注意:

  • value 属性,如果只有一个 value 属性的情况下,使用 value 属性的时候可以省略 value 名称不写
  • 但是如果有多个属性 , 且多个属性没有默认值,那么 value 名称是不能省略的。

1.3 元注解


元注解:注解注解的注解。

常见的元注解有两个:

  • @Target:约束自定义注解只能在哪些地方使用,可使用的值定义在 ElementType 枚举类中,常用值如下
    • TYPE ,类,接口
    • FIELD, 成员变量
    • METHOD, 成员方法
    • PARAMETER, 方法参数
    • CONSTRUCTOR, 构造器
    • LOCAL_VARIABLE, 局部变量
  • @Retention :申明注解的生命周期,可使用的值定义在 ElementType 枚举类中,常用值如下
    • SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
    • CLASS : 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值 .
    • RUNTIME :注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

示例代码

自定义注解

@Target({ElementType.METHOD,ElementType.FIELD}) // 元注解,规定其只能注解方法和成员变量
@Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失
public @interface MyTest {
}
//@MyTest // 只能注解方法和成员变量
public class AnnotationDemo2 {

    @MyTest
    private String name;

    @MyTest
    public void test(){

    }

    public static void main(String[] args) {

    }
}

1.4 注解解析


注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。

与注解解析相关的接口

  • Annotation:注解的顶级接口,注解都是 Annotation 类型的对象
  • AnnotatedElement:该接口定义了与注解解析相关的解析方法

在这里插入图片描述

所有的类成分 Class, Method , Field , Constructor ,都实现了 AnnotatedElement 接口他们都拥有解析注解的能力

解析注解的技巧

  • 注解在哪个成分上,我们就先拿哪个成分对象。
  • 比如注解作用成员方法,则要获得该成员方法对应的 Method 对象,再来拿上面的注解
  • 比如注解作用在类上,则要该类的 Class 对象,再来拿上面的注解
  • 比如注解作用在成员变量上,则要获得该成员变量对应的 Field 对象,再来拿上面的注解

需求:注解解析的案例

分析

  • 定义注解 Book ,要求如下:
    • 包含属性: String value() 书名
    • 包含属性: double price() 价格,默认值为 100
    • 包含属性: String[] authors() 多位作者
    • 限制注解使用的位置:类和成员方法上
    • 指定注解的有效范围: RUNTIME
  • 定义 BookStore 类,在类和成员方法上使用 Book 注解
  • 定义 AnnotationDemo 测试类获取 Book 注解上的数据

示例代码

注解 Book

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

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    String value();
    double price() default 100;
    String[] author();
}

BookStore 类 和 AnnotationDemo类

import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
   目标:完成注解的解析
 */
public class AnnotationDemo {
    @Test
    public void parseClass(){
        // a.先得到类对象
        Class c = BookStore.class;
        // b.判断这个类上面是否存在这个注解
        if(c.isAnnotationPresent(Book.class)){
            //c.直接获取该注解对象
            Bookk book = (Book) c.getDeclaredAnnotation(Book.class);
            System.out.println(book.value());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.author()));
        }
    }

    @Test
    public void parseMethod() throws NoSuchMethodException {
        // a.先得到类对象
        Class c = BookStore.class;

        Method m = c.getDeclaredMethod("test");

        // b.判断这个方法上面是否存在这个注解
        if(m.isAnnotationPresent(Book.class)){
            //c.直接获取该注解对象
            Bookk book = (Book) m.getDeclaredAnnotation(Book.class);
            System.out.println(book.value());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.author()));
        }
    }
}

@Book(value = "《情深深雨濛濛》", price = 99.9, author = {"琼瑶", "dlei"})
class BookStore{

    @Book(value = "《三少爷的剑》", price = 399.9, author = {"古龙", "熊耀华"})
    public void test(){
    }
}

1.5 注解应用于 junit 框架


模拟 Junit 框架

需求:定义若干个方法,只要加了 MyTest 注解,就可以在启动时被触发执行

分析:

  • 定义一个自定义注解 MyTest ,只能注解方法,存活范围是一直都在。
  • 定义若干个方法,只要有 @MyTest 注解的方法就能在启动时被触发执行,没有这个注解的方法不能执

示例代码

自定义注解 MyTest

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

@Target({ElementType.METHOD,ElementType.FIELD}) // 元注解,规定其只能注解方法和成员变量
@Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失
public @interface MyTest {
}

测试类

import java.lang.reflect.Method;

public class AnnotationDemo {
    public void test1(){
        System.out.println("===test1===");
    }

    @MyTest
    public void test2(){
        System.out.println("===test2===");
    }

    @MyTest
    public void test3(){
        System.out.println("===test3===");
    }

    /**
      启动菜单:有注解的才被调用。
     */
    public static void main(String[] args) throws Exception {
        AnnotationDemo t = new AnnotationDemo();
        // a.获取类对象
        Class c = AnnotationDemo.class;
        // b.提取全部方法
        Method[] methods = c.getDeclaredMethods();
        // c.遍历方法,看是否有MyTest注解,有就跑它
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                // 跑它
                method.invoke(t);
            }
        }
    }
}

2 动态代理

2.1 问题引入


模拟企业业务功能开发,并完成每个功能的性能统计

需求:模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。

分析

  • 定义一个 UserService 表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。
  • 定义一个实现类 UserServiceImpl 实现 UserService ,并完成相关功能,且统计每个功能的耗
    时。
  • 定义测试类,创建实现类对象,调用方法。

本案例存在哪些问题?

  • 业务对象的的每个方法都要进行性能统计,存在大量重复的代码。

2.2 动态代理实现

代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。

关键步骤

  • 必须有接口,实现类要实现接口(代理通常是基于接口实现的)。
  • 创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。

在这里插入图片描述

示例代码

用户业务接口 UserService

/**
   模拟用户业务功能
 */
public interface UserService {
    String login(String loginName , String passWord) ;
    void selectUsers();
    boolean deleteUsers();
    void updateUsers();
}

实现类 UserServiceImpl

public class UserServiceImpl implements UserService{
    @Override
    public String login(String loginName, String passWord)  {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if("admin".equals(loginName) && "1234".equals(passWord)) {
            return "success";
        }
        return "登录名和密码可能有毛病";

    }

    @Override
    public void selectUsers() {
        System.out.println("查询了100个用户数据!");
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean deleteUsers() {
        try {
            System.out.println("删除100个用户数据!");
            Thread.sleep(500);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public void updateUsers() {
        try {
            System.out.println("修改100个用户数据!");
            Thread.sleep(2500);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
    public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces, InvocationHandler h)
    参数一:类加载器,负责加载代理类到内存中使用。
    参数二:获取被代理对象实现的全部接口。代理要为全部接口的全部方法进行代理
    参数三:代理的核心处理逻辑
 */
public class ProxyUtil {
    /**
      生成业务对象的代理对象。
     * @param obj
     * @return
     */
    public static <T> T  getProxy(T obj) {
        // 返回了一个代理对象了
        return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 参数一:代理对象本身。一般不管
                        // 参数二:正在被代理的方法
                        // 参数三:被代理方法,应该传入的参数
                       long startTimer = System .currentTimeMillis();
                        // 马上触发方法的真正执行。(触发真正的业务功能)
                        Object result = method.invoke(obj, args);

                        long endTimer = System.currentTimeMillis();
                        System.out.println(method.getName() + "方法耗时:" + (endTimer - startTimer) / 1000.0 + "s");

                        // 把业务功能方法执行的结果返回给调用者
                        return result;
                    }
                });
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        // 1、把业务对象,直接做成一个代理对象返回,代理对象的类型也是 UserService类型
        UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
        System.out.println(userService.login("admin", "1234"));
        System.out.println(userService.deleteUsers());
        userService.selectUsers();
        userService.updateUsers(); // 走代理
    }
}

动态代理的优点

  • 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。
  • 可以为被代理对象的所有方法做代理。
  • 可以在不改变方法源码的情况下,实现对方法功能的增强。
  • 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1148293.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 基于微信小程序的汉堡点餐系统的研究与实现

文章目录 1 简介2 相关技术介绍3 系统需求分析4 系统功能分析5 系统的详细设计与实现5.1 系统登录页面5.2 点餐系统后台首页页面5.3 商品信息管理页面5.4 会员管理页面5.5 购买信息管理页面5.6 小程序首页信息页面5.7 商品信息页面5.8 在线下单页面 6 推荐阅读 1 简介 基于微信…

笔记本电脑搜索不到wifi6 无线路由器信号

路由器更换成wifi6 无线路由器后&#xff0c;手机能搜索到这个无线信号&#xff0c;但是笔记本搜索不到这个无线信号&#xff0c;后网上搜索后发现是无线网卡驱动问题&#xff0c;很多无线网卡使用的是Intel芯片&#xff0c;Intel就此发布了公告&#xff0c;升级驱动就可以彻底…

【C】C语言文件操作

1.为什么使用文件 我们前面学习结构体时&#xff0c;写通讯录的程序&#xff0c;当通讯录运行起来的时候&#xff0c;可以给通讯录中增加、删除数据&#xff0c;此时数据是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯录中的数据自然就不存在了&#xff0c;等下…

ZYNQ连载08-Lwip网络组件

ZYNQ连载08-Lwip网络组件 1. 添加Lwip包 2. Lwip配置 我这里关闭ipv6和dhcp。 3. tcp客户端 #include "include/my_tcp.h" #include "lwip/ip.h"#define THREAD_STACKSIZE 1024 #define SERVER_PORT 8000 #define SERVER_ADDR "192.168.3.190&qu…

SHCTF2023 山河CTF Reverse方向week2weenk3部分WP【详解】

文章目录 [WEEK3]ststst[WEEK2]not gcc[WEEK2]Run?润&#xff01;[WEEK2]Authurs_box[WEEK2]pycode [WEEK3]ststst 64 bit 的 ELF 文件 sub_400763点进去看看 mprotect &#xff0c;这个 这一题是SMC TEA的考察&#xff0c;我写过一篇关于 SMC学习网鼎杯jocker 可以使用id…

设计模式(单例模式、工厂模式及适配器模式、装饰器模式)

目录 0 、设计模式简介 一、单例模式 二、工厂模式 三、适配器模式 四、装饰器模式 0 、设计模式简介 设计模式可以分为以下三种: 创建型模式&#xff1a;用来描述 “如何创建对象”&#xff0c;它的主要特点是 “将对象的创建和使用分离”。包括单例、原型、工厂方法、…

LabVIEW开发安捷伦液相色谱Agilent 1100 HPLC

LabVIEW开发安捷伦液相色谱Agilent 1100 HPLC 液相色谱是在科研中常用的仪器&#xff0c;尤其是安捷伦等大品牌。这里对1100的使用进行介绍。 LabVIEW安捷伦1100系列驱动程序&#xff0c;用于控制1100系列的所有仪器。在LabVIEW中使用串行&#xff0c;GPIB或以太网接口。参考…

【java爬虫】公司半年报数据展示

前言 前面有一篇文章介绍了使用selenium获取上市公司半年报的方法&#xff0c;这篇文章就给这些数据写一个简单的前端展示页面 上一篇文章的链接在这里 【java爬虫】使用selenium获取某交易所公司半年报数据-CSDN博客 首先来看一下整个页面的展示效果 前端页面采用vueeleme…

第89步 时间序列建模实战:时间序列融合模型

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们介绍融合模型模型。 &#xff08;1&#xff09;数据源&#xff1a; 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal Syndro…

创建进程中的内核操作

fork 是一个系统调用&#xff0c;流程的最后会在 sys_call_table 中找到相应的系统调用 sys_fork。 _do_fork 里面做的第一件大事就是 copy_process&#xff0c;咱们前面讲过这个思想。如果所有数据结构都从头创建一份太麻烦了&#xff0c;还不如使用惯用“伎俩”&#xff0c;…

shell脚本中数组

分界点&#xff1a;以下内容需要更改&#xff0c;正常放假更改 数组&#xff1a; 数组的作用&#xff1a;可以一次性的定义多个变量&#xff0c;数组的长度是不限的 数组的元素类型&#xff1a;int string float. 就是形成变量池&#xff0c;可以随时的遍历和使用 数组的写…

c#方法中的参数

无参 //无参数直接调用即可MethodWithoutParameters();/// <summary>/// 没有参数的方法/// </summary>private void MethodWithoutParameters(){Debug.Log("没有参数的方法");}有参 //有一个参数MethodWithOneParameters("我是参数");/// <…

【算法|动态规划 | 01背包问题No.2】AcWing 423. 采药

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【AcWing算法提高学习专栏】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&a…

JDBC与MySql数据库

一、系统开发前的环境准备 1.下载Mysql 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 文件解压缩到本地 在此路径下新增my.ini文件以及新建data文件夹 编辑my.ini文件 配置环境变量 注意是编辑系统变量的Path 以管理员身份运行cmd 输入命令&#xff1a…

【Go】格式化字符串指令大全 Redis常用命令

【Go】格式化字符串指令大全 && Redis常用命令 原创&#xff1a;As.Kai 博客地址&#xff1a;https://blog.csdn.net/qq_42362997 如果以下内容对您有帮助&#xff0c;点赞点赞点赞~ 目录 格式化格式化字符串指令大全%s 用于插入字符串%d 用于插入整数%f 用于插入浮点数…

Spring AOP源码解读

今天我们来分析Spring中AOP的源码&#xff0c;主要是关于SpringAOP是如何发挥作用的。 前期准备 首先我们需要有一个Spring AOP项目&#xff0c;添加好了SpringAOP的依赖。 <dependency><groupId>org.springframework</groupId><artifactId>spring-co…

项目基础配置

1、Spring整合MyBatis&#xff1a; 在子工程中加入所需要的依赖 准备jdbc.properties 创建spring的配置文件、整合spring和mybatis 在spring的配置文件中加载jdbc.properties 配置数据源 测试数据库连接 配置SqlSessionFactoryBean 装配数据源 指定XXXMapper.xml文件的位…

DVWA-SQL Injection SQL注入

概念 SQL注入&#xff0c;是指将特殊构造的恶意SQL语句插入Web表单的输入或页面请求的查询字符串中&#xff0c;从而欺骗后端Web服务器以执行该恶意SQL语句。 成功的 SQL 注入漏洞可以从数据库中读取敏感数据、修改数据库数据&#xff08;插入/更新/删除&#xff09;、对数据…

【已解决】VSCode运行C#控制台乱码显示

问题描述 如上图所示&#xff0c;最近在学习C#突然发现我在运行Hello World的时候出现这样的乱码情况。 分析原因 主要是因为VS Code 是UTF-8的编码格式&#xff0c;而我们的PC是Unicode编码&#xff0c;所以我们需要对其进行一个统一即可解决问题。那么知道这个的问题那就开…

“创新深化 数实融合”,AntDB数据库邀您参与“2023世界数字经济大会暨第十三届智慧城市与智能经济博览会”

10月13日&#xff0c;“2023世界数字经济大会暨第十三届智慧城市与智能经济博览会”将在浙江宁波国际会议展览中心隆重启幕&#xff0c;AntDB数据库受邀参会&#xff0c;并诚挚邀请各位领导嘉宾莅临会场参观指导。 图1&#xff1a;会议邀请函 “2023世界数字经济大会暨第十三届…