首页 > 其他分享 >Spring IoC注解式开发

Spring IoC注解式开发

时间:2024-11-17 08:48:11浏览次数:3  
标签:Spring Component public Bean 注解 IoC class 注入

Java注解的前世今生

在Java开发中,你应该会经常看到一些怪怪的符号和字串,比如在代码上面有 @Override 或者 @Deprecated。这些就是我们谓之为“注解”的东西。今天我会带你了解一下Java注解的知识,以及为什么它们这么有用!

什么是Java注解?

注解实际上是一种特殊的标记,它可以被用来提供关于代码的信息、功能或者行为的描述。Java注解就是这种会让源码比较清晰的标记,它远远不只是对人有用,还对机器有用。

注解通常有一个 @ 符号开头,并应用在类、方法、变量上。常用的注解有多种,这里例如说明一些最基本的。

Spring注解的使用

如何使用注解呢?

  • 第一步:加入aop的依赖(加入spring-context依赖之后,会关联加入aop的依赖)
  • 第二步:在配置文件中添加context命名空间
  • 第三步:在配置文件中指定扫描的包
  • 第四步:在Bean类上使用注解
<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.1.5-SNAPSHOT</version>
    </dependency>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 在配置文件中指定扫描的包 -->
    <context:component-scan base-package="bean"/>
</beans>

几种最常用的注解

  1. @Override

    这是一个超级常用的注解,用来指出你正在重写一个方法。它不会改变代码的行为,但在你不小心写错时,它会告诉你,你可能还没有正确地实现要重写的那个方法。

  2. @Deprecated

    如果你看到一个方法还被标注了 @Deprecated,这个方法就是废弃了的意思。通常来说,你还是能用它,但是建议你找更新的方法来代替。这应该是置中时的“更新提示”吧。

  3. @SuppressWarnings

    此注解用来应对一些输出的警告信息。比如,在一个类或方法中有些输出的信息你并不想在之后看到,就可以使用这个注解来强制帮助隐藏一些不重要的警告。

自定义注解

我们还可以自定义注解,实现自己特定的功能。比如你要创建一个用来记录异常处理方法的注解,可以这样写:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    String value();
}
解释自定义注解的各个部分
  1. @Retention

    @Retention 用来定义注解的生命周期,也就是注解会保留到哪个阶段。它有三个可选值:

    • RetentionPolicy.SOURCE:注解只在源代码中保留,编译后就没了。
    • RetentionPolicy.CLASS:注解会保留到编译后的字节码中,但在运行时不可见。
    • RetentionPolicy.RUNTIME:注解不仅会保留到字节码中,而且在运行时也可以通过反射访问。这是自定义注解通常使用的值,因为它允许我们在运行时获取注解的信息。
  2. @Target

    @Target 用来定义注解可以应用于哪些程序元素。例如:

    • ElementType.TYPE:可以应用于类、接口、枚举。
    • ElementType.METHOD:可以应用于方法。
    • ElementType.FIELD:可以应用于字段(成员变量)。

    在上面的例子中,我们使用了 ElementType.METHOD,意味着这个注解只能应用于方法。

  3. 注解的定义和属性

    自定义注解通过 @interface 关键字定义。注解可以包含属性,这些属性类似于接口中的方法,可以用来传递一些元数据。属性的定义格式如下:

    public @interface MyAnnotation {
        String value();
        int number() default 0;
    }
    

    在这个例子中,MyAnnotation 有两个属性:valuenumber

    • String value():定义了一个名为 value 的属性,类型是 String。在使用注解时,必须为这个属性赋值。
    • int number() default 0:定义了一个名为 number 的属性,类型是 int,并且有一个默认值 0。这意味着在使用注解时,如果不为 number 赋值,它会自动使用默认值 0

    在使用自定义注解时,可以这样写:

    @MyAnnotation(value = "This is a test annotation", number = 5)
    public void myMethod() {
        // 方法体
    }
    

    如果注解只有一个属性,并且这个属性名是 value,那么在使用时可以省略属性名,直接赋值:

    @MyAnnotation("This is a test annotation")
    public void myMethod() {
        // 方法体
    }
    

    注解的属性可以是基本数据类型、String、枚举、其他注解,或者这些类型的数组。但注解的属性不能是复杂对象,因为注解的目标是提供一些简单的、可序列化的元数据。

如何通过反射机制读取注解

通过反射机制,我们可以在运行时获取注解的信息。这通常用于框架开发或运行时动态处理某些逻辑。下面是一个通过反射读取注解的例子:

public class Test {
    public static void main(String[] args) throws Exception {
        // 存放Bean的Map集合。key存储beanId。value存储Bean。
        Map<String,Object> beanMap = new HashMap<>();

        String packageName = "com.powernode.bean";
        //将包名中的.转换为路径中的/
        String path = packageName.replaceAll("\\.", "/");//com/powernode/bean
        // 正则表达式.代表所有,因此要加上转义
		
		/* 获取系统类加载器中的包路径
		此时,url的值为包在文件系统中的位置,例如:
		url   file:/project-root/target/classes/com/powernode/bean
		*/
        URL url = ClassLoader.getSystemClassLoader().getResource(path);
		/*将URL转换为文件并获取文件列表
		*/
        File file = new File(url.getPath());
        //这里包含了 User.class、Product.class、Order.class。
        File[] files = file.listFiles();
        Arrays.stream(files).forEach(f -> {
            String className = packageName + "." + f.getName().split("\\.")[0];
             // 例如,当f是User.class时,className就是"com.powernode.bean.User"
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(Component.class)) {
                    Component component = clazz.getAnnotation(Component.class);
                    String beanId = component.value();
                    Object bean = clazz.newInstance();
                    beanMap.put(beanId, bean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        System.out.println(beanMap);
    }
}

在上面的代码中:

  1. 使用 Class<?> clazz = MyClass.class; 获取类的字节码对象。
  2. 使用 clazz.getDeclaredMethods() 获取类中所有的方法。
  3. 通过 method.isAnnotationPresent(MyAnnotation.class) 来判断方法上是否存在特定的注解。
  4. 如果存在,通过 method.getAnnotation(MyAnnotation.class) 获取注解的实例,并可以访问注解的属性值。

这样,你就可以在运行时通过反射机制读取自定义注解的信息,从而实现一些动态的行为。

自定义注解的应用场景

自定义注解在实际开发中有很多应用场景,比如:

  1. 代码生成和配置:通过注解来简化配置,减少XML或其他配置文件的使用。例如,Spring 框架中大量使用注解来进行依赖注入和配置。

  2. 运行时处理:使用自定义注解可以在运行时通过反射获取注解信息,从而实现一些动态功能。例如,你可以定义一个 @LogExecutionTime 注解,在方法执行时自动记录其执行时间。

  3. 框架开发:自定义注解广泛应用于框架开发中,用来简化使用者的操作。例如,许多ORM框架(如 Hibernate)使用注解来映射数据库表和类。

为什么要用注解?

注解的优势就是为了缩短代码和提高代码的可读性。它使用很简单,但能帮助程序员于实现设计模块化,及自动化一些功能的配置。当你写代码时,一个适当的注解能让你了解代码的目的,也让未来的你或其他工程师能更快地理解代码的意图。

最后,希望这篇文章能让你更近一步了解Java注解,并且试着在你的项目中用一用。有问题的话,欢迎留言或来讨论!注解的世界还有很多驱动等你去探索呢。

探索 Spring Bean 的注解:让你的代码更优雅

声明Bean的注解
1. @Component

@Component 可以说是所有注解的“老大哥”。它告诉 Spring:嘿,这个类我需要你管理它!

比如:

@Component("userBean")
public class User {
    // User 类定义
}

这里 @Component("userBean") 就是告诉 Spring,创建一个叫 userBean 的实例,并且自动管理它的生命周期。是不是很简单?
如果没有写对应的value值,那么spring自动设置为Bean类名首字母小写。

2. @Service

@Service@Component 的“亲兄弟”,专门用于服务层。它的作用和 @Component 一样,只不过它表示这个类里包含的是业务逻辑。

@Service("userService")
public class UserService {
    // 业务逻辑代码
}
3. @Repository

@Repository 也是类似的,它用于数据访问层,通常是那些与数据库打交道的类。用这个注解还可以让 Spring 处理数据库异常。

@Repository("userRepository")
public class UserRepository {
    // 数据库访问代码
}
4. @Controller

如果你做 Web 开发,肯定用过 @Controller。它表示这是一个控制器类,用于处理用户的 HTTP 请求。

@Controller
public class UserController {
    // 处理请求的逻辑代码
}
总结

通过源码可以看到,@Controller、@Service、@Repository这三个注解都是@Component注解的别名。
也就是说:这四个注解的功能都一样。用哪个都可以。
只是为了增强程序的可读性,建议:

  • 控制器类上使用:Controller
  • service类上使用:Service
  • dao类上使用:Repository

他们都是只有一个value属性。value属性用来指定bean的id,也就是bean的名字。

选择性实例化Bean

假设在某个包下有很多Bean,有的Bean上标注了Component,有的标注了Controller,有的标注了Service,有的标注了Repository,现在由于某种特殊业务的需要,只允许其中所有的Controller参与Bean管理,其他的都不实例化。这应该怎么办呢?

@Component
public class A {
    public A() {
        System.out.println("A的无参数构造方法执行");
    }
}

@Controller
class B {
    public B() {
        System.out.println("B的无参数构造方法执行");
    }
}

@Service
class C {
    public C() {
        System.out.println("C的无参数构造方法执行");
    }
}

@Repository
class D {
    public D() {
        System.out.println("D的无参数构造方法执行");
    }
}

@Controller
class E {
    public E() {
        System.out.println("E的无参数构造方法执行");
    }
}

@Controller
class F {
    public F() {
        System.out.println("F的无参数构造方法执行");
    }
}

我只想实例化bean3包下的Controller。配置文件这样写:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.powernode.spring6.bean3" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
</beans>

use-default-filters="true" 表示:使用spring默认的规则,只要有Component、Controller、Service、Repository中的任意一个注解标注,则进行实例化。
use-default-filters=“false” 表示:不再spring默认实例化规则,即使有Component、Controller、Service、Repository这些注解标注,也不再实例化
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 表示只有Controller进行实例化。
也可以将use-default-filters设置为true(不写就是true),并且采用exclude-filter方式排出哪些注解标注的Bean不参与实例化:

<context:component-scan base-package="com.powernode.spring6.bean3">
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

负责注入的注解

在现代 Java 开发中,注入(Injection)是依赖管理的重要部分,也是实现松耦合架构的核心思想之一。Spring 作为一个广泛应用的 Java 企业级开发框架,提供了多种用于实现依赖注入的注解,让开发者能够更便捷地管理对象之间的依赖关系。本文将详细介绍 Spring 中用于依赖注入的常见注解,帮助你掌握这些工具并在开发中熟练运用。

1. @Value

@Value 注解用于注入外部的值,如配置文件中的属性。它可以为 Bean 的字段注入字符串、数值等类型的数据(简单类型的注入),适合需要从配置文件加载配置的场景。

@Component
public class AppConfig {
    @Value("张三")
    private String appName;
}

在此例中,@Value("张三") 用于将外部配置文件中的 app.name 值注入到 appName 字段中。
注意:如果我们并没有给属性提供setter方法,但仍然可以完成属性赋值。
如果提供setter方法,并且在setter方法上添加@Value注解,同样可以完成注入。
为了简化代码,以后我们一般不提供setter方法,直接在属性上使用@Value注解完成属性赋值。

总结:

@Value注解可以出现在属性上、setter方法上、以及构造方法的形参上。可见Spring给我们提供了多样化的注入。

2. @Autowired

@Autowired 是 Spring 最常用的注解之一,用于自动注入 Spring 容器中的依赖。它可以标注在属性、构造方法、Setter 方法、形参上、注解等位置。
@Autowired注解可以用来注入非简单类型。被翻译为:自动连线的,或者自动装配。
单独使用@Autowired注解,默认根据类型装配。【默认是byType】
在源码中::该注解有一个 required属性,默认值是true表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。

如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。

  • 属性注入

    @Component
    public class UserService {
        @Autowired
        private UserRepository userRepository;
    }
    

    在上述代码中,Spring 自动为 userRepository 赋值,将容器中的 UserRepository Bean 注入到 UserService 中。

  • 构造方法注入

    @Component
    public class UserService {
        private final UserRepository userRepository;
    
        @Autowired
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    

    构造方法注入在依赖于不可变字段时非常有用,有助于确保 UserService 实例一旦构造,userRepository 的值就不会改变。

  • Setter 方法注入

    @Component
    public class UserService {
        private UserRepository userRepository;
    
        @Autowired
        public void setUserRepository(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    

    Setter 方法注入适合在对象创建之后可能需要重新设置依赖的场景。

3. @Qualifier

当 Spring 容器中存在多个同类型的 Bean 时,@Autowired 无法决定应注入哪一个。@Qualifier 注解可以配合 @Autowired 使用,用于指定具体注入的 Bean。

@Component
public class UserService {
    @Autowired
    @Qualifier("customUserRepository")
    private UserRepository userRepository;
}

通过 @Qualifier("customUserRepository"),开发者可以明确指定要注入名为 customUserRepository 的 Bean,从而解决歧义问题。

3. @Resource

@Resource 是 JSR-250 规范的一部分,它既可以按名称也可以按类型进行注入,相当于 @Autowired + @Qualifier 的组合。与 @Autowired 不同的是,@Resource 默认按名称进行注入。

@Component
public class UserService {
    @Resource(name = "userRepository")
    private UserRepository userRepository;
}

在上述代码中,@Resource 将按名称查找容器中的 Bean,找到名为 userRepository 的对象并进行注入。

@Resource注解也可以完成非简单类型注入。那它和@Autowired注解有什么区别?
  • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
  • @Autowired注解是Spring框架自己的。
  • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
  • @Autowired注解默认根据类型装配 byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
  • @Resource注解用在属性上、setter方法上。
  • @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。

<dependency>
  <groupId>jakarta.annotation</groupId>
  <artifactId>jakarta.annotation-api</artifactId>
  <version>2.1.1</version>
</dependency>
全注解式开发

所谓的全注解开发就是不再使用spring配置文件了。写一个配置类来代替配置文件。

1. @Configuration 和 @Bean:

@Configuration 注解用于定义一个配置类,替代传统的 XML 配置文件。@Bean 注解则用于在配置类中定义需要注册到 Spring 容器中的 Bean。

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
}

在上述代码中,AppConfig 类使用 @Configuration 注解,定义了两个 Bean:userService userRepository,它们将被 Spring 容器管理。

2.@ComponentScan:

@ComponentScan 注解用于自动扫描指定包下的组件(如 @Component、@Service、@Repository 等注解标注的类),从而自动将它们注册到 Spring 容器中。

@Configuration
// 存在两个包下面的bean需要管理
@ComponentScan({"com.powernode.spring6.dao", "com.powernode.spring6.service"})
public class Spring6Configuration {
}

@ComponentScan 可以减少手动注册 Bean 的工作量,尤其在项目中存在大量组件时非常有用。
此时的测试程序:

@Test
public void testNoXml(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.save();
}
6. 使用注解进行注入的最佳实践
  1. 构造方法注入优先:构造方法注入有助于保证依赖的不可变性和类的线程安全,建议在可能的情况下优先使用构造方法注入。
  2. 避免字段注入:虽然字段注入很方便,但会使测试变得困难,因为它需要通过反射来注入依赖。优先选择构造方法注入或 Setter 方法注入。
  3. 使用 @Qualifier@Primary:当容器中有多个同类型 Bean 时,使用 @Qualifier@Primary 来解决依赖注入的歧义问题。
总结

Spring 提供了丰富的注解用于实现依赖注入,每个注解都有其特定的使用场景。@Autowired 是最常见的选择,结合 @Qualifier 能够解决多 Bean 注入的歧义。@Resource@Inject 则用于提供与标准化注入规范的兼容性。通过合理地使用这些注解,可以大幅提高代码的可读性和可维护性,并构建灵活松耦合的应用程序架构。

标签:Spring,Component,public,Bean,注解,IoC,class,注入
From: https://blog.csdn.net/gege_0606/article/details/142979498

相关文章