Java SE 学习笔记(十八)—— 注解、动态代理
目录
- 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 {@MyTestprivate String name;@MyTestpublic 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 {@Testpublic 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()));}}@Testpublic 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===");}@MyTestpublic void test2(){System.out.println("===test2===");}@MyTestpublic 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{@Overridepublic 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 "登录名和密码可能有毛病";}@Overridepublic void selectUsers() {System.out.println("查询了100个用户数据!");try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}}@Overridepublic boolean deleteUsers() {try {System.out.println("删除100个用户数据!");Thread.sleep(500);return true;} catch (Exception e) {e.printStackTrace();return false;}}@Overridepublic 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() {@Overridepublic 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(); // 走代理}
}
动态代理的优点
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。
- 可以为被代理对象的所有方法做代理。
- 可以在不改变方法源码的情况下,实现对方法功能的增强。
- 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
相关文章:
Java SE 学习笔记(十八)—— 注解、动态代理
目录 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语…...
虚拟内存之请求分页管理
一、与基本分页存储管理的区别 程序执行过程中,访问信息不在内存时,OS需要从外存调入内存。——>调页功能 内存空间不够时,OS需要将内存中暂时用不到的信息换出到外存。——>页面置换功能 二、页表机制 1.页表:需要知道页面…...
lazarus开发:提升sqlite数据插入速度
目录 1 前言 2 优化数据容器 3 开启事务插入数据 4 其他方面优化 1 前言 近期有一个需求是向数据库中插入excel文件中的10万多条数据,接近70个字段。最初整个插入数据时间是大约40分钟,经过优化调整后,大幅优化为大约5分钟。这里简单介绍…...
瑞萨RH850-P1X ECM和英飞凌TC3xx SMU对比
1.1 基本结构 P1X ECM(Error Control Module)收集从不同的错误源和监控电路发来的错误信号,并通过error pin(ERROROUTZ)对外输出、产生中断并发出ECM reset信号。 P1x-C系列根据产品型号不同,ECM个数也不相同,如下: 对应寄存器基地…...
Ajax学习笔记第三天
做决定之前仔细考虑,一旦作了决定就要勇往直前、坚持到底! 【1 ikunGG邮箱注册】 整个流程展示: 1.文件目录 2.页面效果展示及代码 mysql数据库中的初始表 2.1 主页 09.html:里面代码部分解释 display: inline-block; 让块元素h1变成行内…...
ESP32-C3 低功耗懒人开关:传统开关轻松上云和本地控制
项目背景 随着科技的快速发展,智能家居已经成为我们日常生活的一部分。而对于基础设施已经配备完毕的家庭而言,对家居设备的智能化改造是一项相对困难的工作。本文将分享一款基于 Wi-Fi 的低功耗懒人开关—— “ESP32-C3 管灯熊猫”。将智能的 “ESP32-…...
前端学习路线指南:从入门到精通【①】
前言 作为一个前端开发者,学习前端技术是必不可少的。然而,由于前端领域的广阔和不断演进的技术栈,对于初学者来说可能会感到困惑。本篇文章将为你提供一个清晰的前端学习路线,帮助你系统地掌握前端开发技能,并成为一名…...
Flash模拟EEPROM原理浅析
根据ST的手册,我们可以看到,外挂EEPROM和Dflash模拟EEPROM,区别如下: 很明显,模拟EEprom的写入速度要远远快于外挂eeprom(有数据传输机制); 其次,外挂EEPROM不需要擦除即可实现写入数据…...
Typora 最新激活方法
Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式,其目标是实现易读易写。而Typora则是一个非常不错的Markdown编辑器,它的界面非常的简洁直观,并且功能各…...
jenkins如何安装?
docker pull jenkins/jenkins:lts-centos7-jdk8 2.docker-compose.yml version: 3 services:jenkins:image: jenkins/jenkins:lts-centos7-jdk8container_name: my-jenkinsports:- "8080:8080" # 映射 Jenkins Web 界面端口volumes:- jenkins_home:/var/jenkins_h…...
从零开始的LINUX(三)
bc:进行浮点数运算 uname:查看当前的操作系统 ctrlc:中止当前正在执行的程序 ctrld:退出xshell shutdown:关机 reboot:重启 shell外壳: 作用:1、命令解释(将输入的程序…...
CleanMyMac2024永久免费版Mac系统磁盘清理工具
Cleanmymac对很多用户来说已经非常熟悉了,因为在网上如果你搜寻有关清理mac系统方面的软件时,占比非常多的会是cleanmymac的相关消息。许多刚从Windows系统转向Mac系统怀抱的用户,一开始难免不习惯,因为Mac系统没有像Windows一样的…...
HashSet 元素不重复
HashSet通过底层使用HashMap来保证元素不重复。具体来说,HashSet内部维护一个HashMap,其中元素存储在HashMap的key上,而所有的value都指向同一个共享的内部对象。在存储元素时,HashSet会根据元素的hashCode值来确定其在HashMap中的…...
基于SpringBoot的二手车交易系统的设计与实现
目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 商家管理 公告信息管理 论坛管理 商家功能实现 汽车管理 汽车留言管理 论坛管理 用户功能实现 汽车信息 在线论坛 公告信息 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 如今社会上各行…...
最短路径:迪杰斯特拉算法
简介 英文名Dijkstra 作用:找到路中指定起点到指定终点的带权最短路径 核心步骤 1)确定起点,终点 2)从未走过的点中选取从起点到权值最小点作为中心点 3)如果满足 起点到中心点权值 中心点到指定其他点的权值 < 起…...
基于UDP/TCP的网络通信编程实现
小王学习录 今日鸡汤Socket套接字基于UDP来实现一个网络通信程序DatagramSocket类DatagramPacket类基于UDP的服务器端代码基于UDP的客户端代码基于TCP来实现一个网络通信程序ServerSocket类Socket类基于TCP的服务器端代码基于TCP的客户端代码优化之后的服务器端代码补充TCP长短…...
springboot启动报错
...
Python中的split()函数
函数:split() Python中有split()和os.path.split()两个函数,具体作用如下: split():拆分字符串。通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list) os.path.split():…...
大数据-玩转数据-Python Sftp Mysql 数据
一、需求描述 1、从Mysql数据库表下载数据到服务器; 2、将数据已csv文件格式存储并对数据格式进行处理(添加表头,表头和数据均用竖线分隔符隔开,末尾也加分割符); 3、文件路径文件夹以天为单位,…...
Selenium3-当元素通过@FindBy获取时,返回元素为null
报错: 在获取元素的js属性时一直获取不到,报空指针,定位到元素时,发现是FindBy的元素没有找到 解决方法: 在page类的构造函数中加上了 界面初始化,让元素先隐式加载,这样就不会出现返回元素为空的情况辣 PageFactory…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
