Spring5应用之基础注解开发

作者简介 :☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏Spring5应用专栏_Aomsir的博客-CSDN博客

文章目录

参考文献

前言

在之前的内容中,我们已经详细探讨了Spring的一些核心概念。从本篇开始,我们将转向更为现代和简洁的开发方式------注解开发 。通过注解,我们能更加直观和简洁地定义和配置Spring组件,极大地提高开发效率。本文将详细介绍Spring框架中的基础注解以及与注解相关的核心概念

注解的概念

什么是注解编程?

注解编程是使用注解为代码添加元数据信息的一种编程方法。注解以@符号开始,如@Component

通过注解,我们可以为类、方法或变量提供额外的信息。这些信息可以被框架或库用来执行特定的操作或改变代码的行为。

在下面例子中,@Component是一个注解,它告诉Spring框架,这个类User应被视为一个Bean,交由Spring管理。

注解编程的优势在于它可以简化配置、提高代码清晰度,并帮助实现与Spring框架的紧密集成。但适量使用是关键,以保持代码的可读性。

java 复制代码
@Component
public class User {
    
}

为什么学习注解编程?

  1. 代码整洁与开发便利性:使用注解可以大大简化配置,使代码变得更加简洁。这不仅可以提高代码的可读性,还可以加速开发过程,因为开发者不再需要编写冗长的XML配置或其他形式的配置。
  2. Spring的演进与注解:
    • Spring 2.x:注解被引入到Spring中,这标志着Spring开始从传统的XML配置转向基于注解的配置。
    • Spring 3.x:Spring进一步完善了基于注解的开发,为开发者提供了更多的注解选项,并优化了其与框架的集成。
    • Spring Boot:Spring Boot进一步推动了基于注解的开发。借助Spring Boot, 开发者可以通过注解轻松地配置和启动应用程序,实现"约定优于配置"的理念。

综上所述,学习注解编程不仅可以使开发过程变得更加便捷和高效,而且对于现代的Spring开发来说,掌握注解已经变得不可或缺

注解的作用

  1. 替换XML或其他形式的配置:
    • 简化配置:使用注解,开发者可以直接在代码中指定配置,无需跳转到外部的XML或其他配置文件中。
    • 提高可读性:由于配置与代码在同一位置,其他开发者在查看类或方法时可以轻松地理解其配置和功能。
    • 降低出错率:减少了外部配置,可以降低配置错误或不一致的可能性。
  2. 替换接口,强化契约性:
    • 定义契约:注解可以作为方法、类或组件之间契约的一部分,明确其期望的行为或用法。
    • 灵活性:与固定的接口相比,注解提供了更大的灵活性。例如,一个方法可以有多个注解,但只能实现一个接口。
    • 明确意图:使用注解可以明确地表示某个类、方法或字段的意图或特殊行为

Spring注解发展历程

Spring框架在其各个版本中逐步强化了对注解编程的支持,下面是Spring注解发展的历程:

  1. Spring 2.x
    • 初步支持注解编程:这一版本开始引入了一系列的基础注解,例如@Component@Service@Scope等。
    • 目标:初衷是为了简化XML配置,在某些情况下提供一个更简洁的选择,被视为XML配置的有益补充。
  2. Spring 3.x
    • 增强注解支持:在Spring 2.x的基础上,引入了更多的注解,例如@Bean@Configuration等,增强了框架对注解编程的支持。
    • 目标转变:此时,Spring的目标开始转向希望开发者能够使用纯注解编程,意图逐步替换XML配置。
  3. Spring 4.x
    • Spring Boot的诞生:Spring 4.x版本中,Spring Boot作为一个子项目崛起。它进一步简化了Spring应用的初始化和开发流程,大大加强了对注解编程的支持。
    • 官方推荐:从这个版本开始,官方更加倾向于推荐开发者使用注解编程,因为它可以更简洁、更高效地开发Spring应用。

这一发展历程展示了Spring如何从初步地支持注解编程,到逐渐推崇其作为首选的开发方式。随着版本的迭代,Spring通过引入新的注解和工具,使注解编程变得更加强大和灵活

注解开发所带来的疑问

问题:使用注解开发后,由原先的XML配置转移到了注解中,那是否意味着代码的耦合度会增加?

答案:的确,将配置直接嵌入到代码中可能会增加某种程度的耦合,因为配置与业务逻辑现在都在同一处。但是,这种耦合大多数时候是可以接受的,因为它带来的便利性和直观性往往超过了这个小小的劣势。

更重要的是,Spring框架提供了足够的灵活性来应对这种情况。如果开发者对某个注解的配置不满意或希望在不同的环境中使用不同的配置,仍然可以使用Spring的XML配置文件来覆盖或调整这些注解设置。这种方式结合了注解的简洁性和XML配置的灵活性,确保了框架的强大和应用的可维护性

基础注解

本篇文章主要讲的是Spring2.x提供的基础注解

⚠️注意:这个阶段的注解,仅仅是简化XML的配置,并不能够完全替代XML配置文件

环境搭建

  • 创建一个新的配置文件,开启扫描包
xml 复制代码
<?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.aomsir.basic.annotation" />
</beans>

对象创建相关注解

@Component

当我们在Spring中使用@Component注解,相当于告诉Spring框架:请为这个类创建一个实例并将其放入IoC容器中。但是,与传统的XML配置方式相比,这种注解方式隐藏了很多细节。让我们来解开这其中的奥秘。

  1. class属性:
    • 传统的XML配置中,我们使用class属性来告诉Spring需要创建哪个类的实例。
    • 当使用@Component注解时,Spring通过扫描指定的包来找到所有带有此注解的类。在找到这些类后,Spring通过反射来创建它们的实例。
    • 因此,注解的class属性实际上是通过Spring的自动扫描和Java的反射机制隐式得到的。
  2. id属性:
    • 在XML配置中,我们通常使用id属性为bean指定一个唯一的名称。
    • 使用@Component注解时,如果没有明确指定bean的名称,Spring会使用一个默认的命名策略:将类名的首字母变为小写。例如,UserDao类的默认bean名称就是userDao。

下面我创建一个User类,并写了一个测试,测试结果也如下。

细节分析:

  • 自定义Bean ID:如您所述,@Component注解允许您通过其属性值来自定义Bean的ID。在上述例子中,我们使用@Component("u")来设置Bean的ID为"u"。
  • XML配置优先:如果在Spring的XML配置文件中有一个与注解Bean ID相同的Bean定义,XML中的定义会覆盖注解中的定义。这提供了一个方法,使开发者可以在必要时覆盖基于注解的配置,给予了更大的灵活性
java 复制代码
@Data
@Component
public class User implements Serializable {
    private Integer id;
    private String name;
    private String password;
}
java 复制代码
public class TestAnnotation {

    private static final Logger log = LoggerFactory.getLogger(TestAnnotation.class);

    /**
     * 用于测试 @Component注解
     */
    @Test
    public void test1() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext4.xml");

        User user = ctx.getBean("user", User.class);

        log.info("{}", user);
    }
}

衍生注解

从@Component注解衍生出了多个特殊化的注解,包括@Repository@Service@Controller等。

这些注解在本质上都是@Component,也可以称之为复合注解。它们在使用和功能上与@Component完全一致。推出这些派生注解的主要目的是为了在项目中区分不同的组件层级,从而使Bean的功能和责任更加明确。例如,我们通常在处理用户请求的控制器上使用@Controller注解。

另外,一个值得注意的细节是,在整合MyBatis时,我们通常不使用@Component或@Repository来标注Dao接口。这是因为在这种特定的整合场景下,有更专用的方法来标识和管理DAO接口。

@Scope

注解@Scope主要用于控制Bean对象的创建次数。具体来说,它决定了每次请求Bean时是否创建一个新的实例,还是每次都使用已经创建的同一个实例。在传统的XML配置中,我们可以通过scope属性来设置这一行为;而在注解开发中,我们可以直接在类上使用@Scope注解。该注解接受的值可以是"singleton"(表示每次都返回同一个实例)或"prototype"(表示每次返回一个新实例)。需要注意的是,如果不指定值,那么默认行为是"singleton"。

java 复制代码
@Component
@Scope("prototype")
public class Customer {
}
java 复制代码
public class TestAnnotation {

    private static final Logger log = LoggerFactory.getLogger(TestAnnotation.class);

    /**
     * 用于测试 @Scope注解
     */
    @Test
    public void test2() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext4.xml");

        for (int i = 0; i < 3; i++) {
            Customer customer = ctx.getBean("customer", Customer.class);
            log.info("{}", customer);
        }

    }
}


@Lazy

@Lazy注解是用于延迟创建单实例Bean的。当我们使用@Scope注解并将其值设置为"singleton"时,这些Bean在Spring容器启动时会立即被实例化。然而,有些情况下,我们可能希望Bean只有在首次被真正请求时才被创建,以节省资源和初始化时间。为了实现这一目标,我们可以在类上直接添加@Lazy注解。这样,即使Bean是单实例的,也只会在首次请求时进行实例化,而不是在容器启动时。

java 复制代码
@Component
@Lazy
public class Account {

    private static final Logger log = LoggerFactory.getLogger(Account.class);

    public Account() {
        log.error("Account被创建了");
    }
}
java 复制代码
public class TestAnnotation {

    private static final Logger log = LoggerFactory.getLogger(TestAnnotation.class);

    /**
     * 用于测试 @Lazy注解
     */
    @Test
    public void test3() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext4.xml");


        // 下面三行第一次测试的时候先注释,第二次测试的时候再取消注释
        for (int i = 0; i < 3; i++) {
            Account account = ctx.getBean("account", Account.class);
            log.info("{}", account);
        }
    }
}


生命周期相关注解

@PostConstruct

该注解用于标识某个方法作为Bean的初始化方法。当Bean被Spring容器实例化后,该方法会立即执行。这样,我们可以在该方法中执行一些初始化操作。通过使用@PostConstruct,我们可以避免实现InitializingBean接口或在XML配置文件中配置初始化方法,从而大大简化了代码的结构

@PreDestory

该注解用于标记类的销毁方法。当Bean的生命周期结束,即将被Spring容器销毁时,该方法会被调用。这为我们提供了在Bean销毁之前执行清理操作的机会。利用@PreDestroy,我们可以避免实现特定的DisposableBean接口或在XML配置文件中指定销毁方法,使代码更为简洁

使用

  • 在实体类中编写两个方法,分别添加上@PostConstruct注解与@PreDestory注解
  • 编写测试方法用于测试其正常执行
java 复制代码
@Component
public class Product {

    private static final Logger log = LoggerFactory.getLogger(Product.class);

    @PostConstruct
    public void myInit() {
        log.info("Product.myInit()");
    }

    @PreDestroy
    public void myDestroy() {
        log.info("Product.myDestroy()");
    }
}
java 复制代码
public class TestAnnotation {

    private static final Logger log = LoggerFactory.getLogger(TestAnnotation.class);

    /**
     * 用于测试 @PostConstruct注解与@PreDestroy注解
     */
    @Test
    public void test4() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext4.xml");

        Product product = ctx.getBean("product", Product.class);

        ((ClassPathXmlApplicationContext) ctx).close();
    }
}

注意

@PostConstruct 和 @PreDestroy 注解是JavaEE规范中的一部分,具体来说,它们由JSR-250标准定义。这两个注解不是Spring原生的,但Spring选择支持并整合它们,以增强其与JavaEE的兼容性。这样的选择进一步强调了通过注解来实现接口契约性的重要性。它证明了不同的技术和框架可以依赖统一的标准,从而为开发者提供一致的开发体验

依赖注入相关注解

在传统的基于XML的配置方式中,我们通常使用标签下的标签来实现依赖注入。然而,随着注解驱动开发的流行,这种繁琐的配置方式已经不再是唯一的选择。使用注解进行依赖注入,我们可以更加直观和简洁地在代码中标识组件之间的依赖关系

通过这种方式,开发者可以直接在Java类中明确地指定依赖关系,无需跳转到XML文件中进行配置。这不仅提高了开发的效率,还使代码的结构更加清晰

用户自定义类型

@Autowired

@Autowired是Spring提供的一个核心注解,用于实现自动的类型依赖注入。该注解基于类型进行匹配,这意味着要注入的组件必须是目标成员变量的类型、子类或其实现类

细节分析:

  1. 基于类型的注入:当一个接口有多个实现类,并且这些实现类都已经被Spring管理时,仅仅通过类型是无法确定注入哪一个实现类的。为了解决这个问题,我们可以使用@Qualifier注解来指定需要注入的具体Bean名称,从而结合名称和类型进行注入。
  2. 注入方式的区别
    • 当@Autowired注解直接加在成员变量上时,Spring会使用反射技术来实现属性的注入。
    • 若@Autowired注解加在Setter方法上,则注入是通过这个Setter方法实现的。

通过引入注解方式的依赖注入,Spring为开发者提供了一种更加直观和简洁的方式来描述Bean之间的依赖关系。

java 复制代码
public interface UserDAO {
    public void save();
}

@Repository
public class UserDAOImpl implements UserDAO {

    private static final Logger log = LoggerFactory.getLogger(UserDAOImpl.class);

    @Override
    public void save() {
        log.info("UserDAOImpl.save()");
    }
}
java 复制代码
public interface UserService {
    public void register();
}

@Service
public class UserServiceImpl implements UserService {

    private UserDAO userDAO;

    @Autowired
    @Qualifier("userDAOImpl")
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Override
    public void register() {
        this.userDAO.save();
    }
}
java 复制代码
public class TestAnnotation {
    /**
     * 用于测试 @Autowired注解
     */
    @Test
    public void test5() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext4.xml");

        UserService userService = ctx.getBean("userServiceImpl", UserService.class);

        userService.register();
    }
}
@Resource

@Resource 是一个用于依赖注入的注解,但它并非Spring原生提供,而是由JSR-250标准定义的。可以看作是@Autowired和@Qualifier两个注解的结合体

细节分析

  1. 名称注入:通过@Resource注解的name属性,我们可以直接指定Bean的名称进行注入,这提供了一种基于名称的注入方式。
  2. 类型注入:若通过名称没有找到合适的Bean进行注入,@Resource注解会回退到基于类型的注入。它会尝试匹配容器中的相同类型、子类或实现类的Bean。

这种注解提供了一种灵活的方式,允许开发者根据具体的需求选择基于名称或类型的注入方式,使得依赖注入更加灵活且简洁。

JDK类型

上述讨论的都是关于自定义类型的依赖注入。但如果我们尝试使用@Autowired或@Resource注解在如Integer这样的JDK基础类型上,我们会发现这是行不通的。那么,如何为这些基本类型或者说原始数据类型注入值呢?

不用担心,Spring已经为我们考虑到了这一点。我们可以将这些基础类型的值存放在一个.properties文件中,然后通过Spring提供的特定注解来读取并注入这些值。

@Value

使用步骤

  1. 配置属性文件:首先,编辑.properties文件并列出需要的键值对。
  2. 引入属性文件:在Spring的配置文件中引入此.properties文件。
  3. 配置Spring:确保Spring的配置中已启用注解驱动的注入

使用细节

  1. 静态变量限制:@Value注解不能应用于静态成员变量。
  2. 集合类型限制:虽然@Value注解可以用于基本数据类型和字符串,但它不能直接用于注入集合类型。为了满足这种需求,您可以考虑使用YAML格式的配置文件和其他Spring提供的方法
xml 复制代码
<?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.aomsir.basic.annotation" />

  <!-- 加载配置文件 -->
  <context:property-placeholder location="classpath:init.properties" />
</beans>
java 复制代码
@Component
@Data
public class Category {

    @Value("${id}")
    private Integer id;

    @Value("${name}")
    private String name;
}
properties 复制代码
id = 1
name = Aomsir
java 复制代码
public class TestAnnotation {
    /**
     * 用于测试:@Value注解
     */
    @Test
    public void test6() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext4.xml");

        Category category = ctx.getBean("category", Category.class);

        log.info("{}", category);
    }
}
@PropertySource

尽管我们在前面的步骤中已经通过配置文件加载了.properties文件,但这种方法相对繁琐。幸运的是,Spring为我们提供了一个更简洁的方式来加载属性文件,那就是使用@PropertySource注解。

@PropertySource注解可以直接应用于配置类上,用于指定需要加载的属性文件的路径。这样,我们可以省去在XML配置中手动加载属性文件的步骤。此外,当我们迁移到SpringBoot进行开发时,这个注解还可以直接应用于主启动类上

java 复制代码
@Data
@Component
@PropertySource("classpath:init.properties")
public class Category {

    @Value("${id}")
    private Integer id;

    @Value("${name}")
    private String name;
}
相关推荐
IOT.FIVE.NO.17 分钟前
Linux实操笔记2 Ubuntu安装Nginx的不同方法
linux·笔记·ubuntu
拾木20020 分钟前
常见的限流算法
java·开发语言
处处清欢24 分钟前
MaintenanceController
java·开发语言
不染_是非25 分钟前
Django学习实战篇四(适合略有基础的新手小白学习)(从0开发项目)
数据库·后端·学习·django·web
飞翔的佩奇36 分钟前
Java项目: 基于SpringBoot+mybatis+maven校园资料分享平台(含源码+数据库+答辩PPT+毕业论文)
java·spring boot·spring·毕业设计·maven·mybatis·校园资料分享平台
不平衡的叉叉树40 分钟前
Java对象列表属性映射工具类
java
akbar&41 分钟前
计算机三级 - 数据库技术 - 第十三章 大规模数据库架构 笔记
数据库·笔记
架构师ZYL42 分钟前
node.js+Koa框架+MySQL实现注册登录
前端·javascript·数据库·mysql·node.js
酷帅且洋仔1 小时前
Redis——常用数据类型List
数据库·redis·list
缘友一世1 小时前
mac系统安装最新(截止2024.9.13)Oracle JDK操作记录
java·macos·oracle