iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Spring@Component自定义注解实现详解
  • 563
分享到

Spring@Component自定义注解实现详解

2024-04-02 19:04:59 563人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录引子Java里的注解注解是什么元注解注解的保存策略自定义注解注解元素注解处理器注解是什么自己写一个@Component注解准备工作代码实现准备@MyComponent注解准备一个

引子

Java里的注解

注解是什么

注解是Java SE5中引入的总要特性,注解的出现使得我们能够以将有编译器来测试和验证的格式,存储有关程序的额外信息.

元注解

Java 中的元注解主要有以下四个

  • @Target 用来定义你的注解将应用于什么地方,例如类,方法,字段
  • @Retention用来定义该注解在哪个级别可用 Source,Class Runtime.
  • @Document将此注解包含在Javadoc中
  • @Inherited 允许子类继承父类中的注解

注解的保存策略

上面提到@Retention注解的取值有Source,Class,Runtime这三个,这三个值的意思如下

  • Source注解只保留在源码中,被编译器丢弃,实际开发中几乎没有用到
  • Class 注解保留在Class文件中,但不在运行时由虚拟机保留
  • Runtime注解保留在Class文件中,并且在运行时由虚拟机保留,我们知道反射是运行时动态修改类信息的技术,如果我们自定义的注解保存策略声明为Runtime,那么我们就能通过反射在运行时动态获取注解,然后通过注解处理器来实现对应的逻辑。

自定义注解

有了Java中提供的元注解,我们就可以依赖元注解来编写自定义注解了,我们只需要指定两个关键信息:注解在哪个级别可用,以及注解作用的位置就可以了,即给我们的注解打上@Target和@Retention注解。我们来看一个例子,下面的代码声明了一个定义注解MyAnnotion

package com.nightcat.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

可以看到@Target制定了我们自定义的注解可以使用在类上,@Rentention中的保存策略指定了我们的注解将在运行期保留(这很重要)。

注解元素

首先我们为自定义注解添加一些属性:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String title() default "";
    int type() default 0;
}

我们向自定义注解中添加了一个String类型的title属性和一个int类型的type属性。

那么自定义注解中可以使用的元素类型有哪些呢?

  • 所有基本类型
  • String
  • Class
  • enum
  • Annotation
  • 以上所有类型的数组

注解处理器

我们定义好了注解,它能做些什么?这就必须提到注解处理器,因为如果没有用来处理注解的工具,那注解也不会有什么用处,那么什么是注解处理器呢?其实就是由我们自己编写的处理自定义注解的程序。下面我们就来编写一个注解处理器,用来处理我们的自定义注解。

由于我们的注解保存策略指定为Runtime,那么我们就能通过反射技术来获取注解。我们先来看一段代码

package com.nightcat.bean;
import com.nightcat.annotaion.MyAnnotation;
@MyAnnotation(title = "《好好学习》", type = 1)
public class Article {
}

我们声明了一个Article类,并且给它打上了我们自定义的注解,下面我们通过自定义的注解处理器来获取注解中属性的值。

package com.nightcat.test;
import com.nightcat.annotaion.MyAnnotation;
import com.nightcat.bean.Article;
public class AnnotationTest {
    public static void main(String[] args) {
        Class<Article> articleClass = Article.class;
        MyAnnotation annotation = articleClass.getAnnotation(MyAnnotation.class);
        System.out.println("annotation.title() = " + annotation.title()); //《好好学习》
        System.out.println("annotation.type() = " + annotation.type());// 1
    }
}

你会发现,我们用了反射技术获取到了定义注解的属性值,这就是自定义注解的使用方法:

  • 将保存策略声明为Runtime.
  • 通过反射获取注解信息,执行对应的业务逻辑.

注解是什么

学习了上面的知识,我们对注解有了一个基本认识,那么注解究竟是什么呢?

其实注解就是继承了Annotion接口的一个接口而已,拿我们自定义的MyAnnotation举例子:

我们先来看一下MyAnnotation.class文件

//
// Source code recreated from a .class file by IntelliJ idea
// (powered by Fernflower decompiler)
//
package com.nightcat.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String title() default "";
    int type() default 0;
}

这是IDEA替我们处理好的,接着我们用javap -c命令反编译一下看看

通过反编译我们发现注解其实就是一个接口。

自己写一个@Component注解

准备工作

有了上面的基础我们终于要开始写自己的@Component注解了。我们知道springFramework中常用的注解有 @Component @Controller @Service @Bean @Repository 等等,这些注解有一个基础的功能,将注解标记的类加到Spring容器中,交给Spring管理。那么这就涉及到两个问题:

  • 扫描指定路径下被这些注解标记的类
  • 把这些类加入到ioc容器中

好了,接下来我们就开始写代码了

代码实现

准备@MyComponent注解

package com.nightcat.annotaion;
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.TYPE)
public @interface MyComponent {
}

你会发现我们的这个注解的保存策略是Runtime,原因不再赘述。

准备一个自定义类

package com.nightcat.bean;
import com.nightcat.annotaion.MyComponent;
@MyComponent
public class Student {
    private Integer age;
    private String stuName;
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", stuName='" + stuName + '\'' +
                '}';
    }
}

注意这个类上打上了我们自定义的@MyComponent注解,方便检测哪些类需要加入到ioc容器,哪些不需要。

准备一个简易的ApplicationContext

我们在使用ioc容器时,主要使用的是ClassPathXmlApplicationContext或者AnnotationConfigApplicationContext,通过这两个类的构造得到ioc容器,然后通过getBean方法获取指定的bean,这个过程涉及到BeanFactory,需要判断以下内容

  • Bean的scope是单例的还是多例的
  • 是不是IOC容器中已经有这个Bean了

等等。

我们这里不这么麻烦,直接通过一个线程安全HashMap来模拟oc容器

主要实现思路如下

  • 用ConcurrentHashMap来模拟容器
  • 扫描传入的包路径及其子路径
  • 如果该路径下存在我们自定义注解@MyComponent标记的类,就获取该类的全类名,然后通过反射创建对象,并将其放入我们自定义的ioc容器中
  • 测试我们自定义的注解是否起作用

实现代码

package com.nightcat;
import com.nightcat.annotaion.MyComponent;
import java.io.File;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
public class ApplicationContext {
    private Logger logger = Logger.getLogger(ApplicationContext.class.toString());
    private Map<String, Object> ioc = new ConcurrentHashMap();
    public Object getBean(String beanName) {
        return ioc.get(beanName);
    }
    public ApplicationContext(String packagePath) {
        scanPackagePath(packagePath);
    }
    private void scanPackagePath(String packagePath) {
        File file = getFile(packagePath);
        File[] allFile = getAllFile(packagePath, file);
        handleClassFile(packagePath, allFile);
    }
    private void handleClassFile(String packagePath, File[] allClassFile) {
        for (File file : allClassFile) {
            String classFileName = file.getName();
            String fileName = classFileName.substring(0, classFileName.lastIndexOf("."));
            String beanName = String.valueOf(fileName.charAt(0)).toLowerCase() + fileName.substring(1);
            String fullClassName = packagePath + "." + fileName;
            try {
                Class<?> aClass = Class.forName(fullClassName);
                if (aClass.isAnnotationPresent(MyComponent.class)) {
                    Object o = aClass.newInstance();
                    ioc.put(beanName, o);
                }
            } catch (Exception e) {
                logger.warning("实例化异常");
            }
        }
    }
    private File[] getAllFile(String packagePath, File file) {
        File[] files = file.listFiles(file1 -> {
            if (file1.isDirectory()) {
                scanPackagePath(packagePath + "." + file1.getName());
            } else {
                String name = file1.getName();
                if (name.endsWith(".class")) return true;
                else return false;
            }
            return false;
        });
        return files;
    }
    private File getFile(String packagePath) {
        String dirPath = packagePath.replaceAll("\\.", "/");
        URL url = this.getClass().getClassLoader().getResource(dirPath);
        String fileName = url.getFile();
        return new File(fileName);
    }
}

通过上面的代码,我们会发现,我们传入的basePackage也就是包名,是以点号分割的,而我们磁盘上的文件是以斜线分割的,所以我们要将点号替换成斜线

再接着我们获取packagePath对应的url资源,通过它获取文件的全路径名,有些小伙伴可能会说,哪里用这么麻烦,我直接用packagePath new File(String path)的形式创建文件不行么,干嘛用url啊,这是因为你传入的类似com.nightcat这种包名,你去直接newFile ,是不是发现少了一些东西?没错,就是你的磁盘路径,发现了么,而Java提供的Url类可以解决这个问题。

剩下的工作就简单了,我们去扫描传入的包路径和自路径,并且检测类上是否有我们自定义的注解,如果有,我们就用创建对象,同时将类名首字母小写,作为ConcurrentHashMap的key,创建的对象作为Value就可以了。

有些小伙伴可能会问,你这个Bean无法保证单例呀,也很简单,你写代码的时候判断一下容器里有没有,有了就不添加了。至于其他的知识,不在这篇文章的范畴,可以自行研究。

测试自定义注解和容器

package com.nightcat.test;
import com.nightcat.ApplicationContext;
import com.nightcat.bean.Student;
public class CodeTest {
    public static void main(String[] args) {
        ApplicationContext context = new ApplicationContext("com.nightcat");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
    }
}

通过测试,发现我们自定义的@MyComponet注解生效了。

到此这篇关于Spring @Component自定义注解实现详解的文章就介绍到这了,更多相关Spring @Component内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Spring@Component自定义注解实现详解

本文链接: https://www.lsjlt.com/news/167174.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring@Component自定义注解实现详解
    目录引子Java里的注解注解是什么元注解注解的保存策略自定义注解注解元素注解处理器注解是什么自己写一个@Component注解准备工作代码实现准备@MyComponent注解准备一个...
    99+
    2024-04-02
  • Spring Boot 注解方式自定义Endpoint详解
    目录概述准备编写自定义Endpoint配置启动&测试注意Spring Boot 常用endpoint的使用Actuator一些常用 Endpoint如何访问 Actuator...
    99+
    2024-04-02
  • 详解Java如何实现自定义注解
    目录概念作用JDK中预定义的一些注解注解生成文档案例自定义注解格式本质属性:接口中的抽象方法元注解:用于描述注解的注解在程序使用(解析)注解:获取注解中定义的属性值案例:通过自定义注...
    99+
    2024-04-02
  • Spring AOP 实现自定义注解的示例
    目录1. 注解如下:2. 切面自工作后,除了一些小项目配置事务使用过 AOP,真正自己写 AOP 机会很少,另一方面在工作后还没有写过自定义注解,一直很好奇注解是怎么实现他想要的功能...
    99+
    2024-04-02
  • Java注解详解及实现自定义注解的方法
    目录概念‍♀️作用⛹JDK中预定义的一些注解注解生成文档案例自定义注解格式本质属性:接口中的抽象方法元注解:用于描述注解的注解‍♂️在程序使用(解析)注解:获取注解中...
    99+
    2024-04-02
  • Spring如何自定义注解
    这篇文章将为大家详细讲解有关Spring如何自定义注解,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。字段注解字段注解一般是用于校验字段是否满足要求,hibernate-validate依赖就提供了很多校验...
    99+
    2023-06-15
  • 使用Spring AOP 如何实现自定义注解
    这期内容当中小编将会给大家带来有关使用Spring AOP 如何实现自定义注解,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。在Maven中加入以下以依赖:<!-- Spring AOP + Aspe...
    99+
    2023-05-31
    springaop 注解
  • Spring注解@Configuration和@Component区别详解
    目录@Configuration 注解:加载过程@Bean 注解方法执行策略@Component 注意总结Spring @Configuration 和 @Component 区别 ...
    99+
    2023-05-14
    spring configuration component configuration和component的区别 spring @cconfiguration
  • Feign利用自定义注解实现路径转义详解
    目录背景解决方案最后背景 近期由于项目中需要,所以需要通过Feign封装一个对Harbor操作的sdk信息。 在调用的过程中发现,当请求参数中带有"/"时,Fei...
    99+
    2024-04-02
  • SpringBoot 实现自定义的 @ConditionalOnXXX 注解示例详解
    目录实现一个自定义的 @Conditional 派生注解Conditional 派生注解的类如何注入到 spring 容器实现一个自定义的 @Conditional 派生注解 自定义...
    99+
    2024-04-02
  • Java自定义注解的详解
    Java自定义注解Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容。在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解。Java1.5引入了注解,当前许多java框架中大量使用...
    99+
    2023-05-31
    java 自定义 注解
  • JAVA自定义注解详情
    目录原理:元注解:@Retention参数讲解:案例:给一个类的String属性设置默认值总结原理: 注解的本质是继承Annotation的特殊接口,其具体实现类是Java运行时生成...
    99+
    2024-04-02
  • Spring Data JPA框架的Repository自定义实现详解
    目录1. Spring Data Repository自定义实现1.1 自定义特殊repository1.2 配置类1.3 解决歧义1.4 手动装配1.5 自定义Base Repos...
    99+
    2024-04-02
  • SpringBoot 自定义注解之脱敏注解详解
    目录自定义注解之脱敏注解一、脱敏后的效果二、代码1.脱敏注解2.定义脱敏类型3.敏感工具类4.脱敏序列化信息小结一下自己手写的一个高效自定义字符串脱敏注解自己写了个 仅供参考自定义注...
    99+
    2024-04-02
  • Java怎么实现自定义注解
    本文小编为大家详细介绍“Java怎么实现自定义注解”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java怎么实现自定义注解”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。概念概念:说明程序的。给计算机看的注释:用...
    99+
    2023-07-02
  • SpringBoot自定义注解之实现AOP切面日志详解
    通过自定义注解的方式(如:@SysLog(obj = "操作对象", text = "操作内容"),在 SpringBoot 中来实现 AOP...
    99+
    2024-04-02
  • Nestjs自定义注解实现接口权限控制详解
    目录正文定义角色枚举声明自定义注册(装饰器)实现角色守卫 RolesGuard在 providers 中引入 RolesGuard使用注解正文 当业务接口开发完成之后,正式上线之前还...
    99+
    2022-12-08
    Nestjs注解接口权限 Nestjs自定义注解权限控制
  • 使用Spring自定义实现IOC和依赖注入(注解方式)
    目录大致思路:注解实现方式:xml实现方式:1. 引入相关jar2. 定义注解类ExtService是注解类的, ExtResource是注解属性的3.定义一个借口4. 接口和使用注...
    99+
    2024-04-02
  • java中怎么自定义注解详解
    在Java中,可以使用`@interface`关键字来定义注解。自定义注解的语法如下: public @interface Cust...
    99+
    2023-10-28
    java
  • Spring Boot之FilterRegistrationBean-自定义Filter详解
    Spring Boot之FilterRegistrationBean-自定义Filter 项目老的用spring写的,新的升级到了springboot,原代码中有在web.xml中定...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作