【Spring学习笔记 五】Spring注解及Java类配置开发

简介: 【Spring学习笔记 五】Spring注解及Java类配置开发

前面我们提到过:按照注入的配置元数据来讲,Spring的配置开发一般分为三类:基于 XML 的配置文件,基于注解的配置,基于 Java 的配置【不推荐】,之前我们所有的概念和实践都是基于XML配置实现的,今天这篇Blog我们来基于注解进行Spring的开发,了解下注解的开发模式,这是一种可以大量减少XML配置的开发方式;同时顺带了解下基于Java类的配置开发方式,这种方式则完全不使用配置文件,同时以上两种模式我们对于注入内容目前只考虑属性注入,暂不考虑构造函数注入。

基于注解进行配置开发

从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean ,可以使用相关类,属性声明的注解,将 bean 配置移动到组件类本身,这样可以减少大量配置。当然我们需要完成两个前提条件

applicationContext.xml配置文件中引入注解约束

http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context.xsd

引入后配置文件头变为:

<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       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">

applicationContext.xml声明使用注解并添加包扫描器

如果没有包扫描器,而类本身又没有在xml中显式的配置为bean,那么会报错,报找不到该bean的错,相当于该扫描器会自动扫描目标区域使用注解定义的类bean并把它们装入IOC容器中。

<context:annotation-config/>
 <context:component-scan base-package="com.example.Spring.model"/>

属性注入的Bean注解

我们首先来看下如何在类中直接使用注解定义Bean。

注解处理属性注入

这里需要重点关注三个注解@Componen、@Value以及@scope,在model包下新增类People:

package com.example.Spring.model;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Data
@Component(value = "people")
@Scope("prototype")
// 相当于配置文件中 <bean id="people" class="当前注解的类" scope="prototype"/>
public class People {
    @Value("tml")
    private String name;    // 相当于配置文件中 <property name="name" value="tml"/>
    @Value("30")
    private int age;
    @Resource
    private Address address;
    public void show(){
        System.out.println("name: "+name);
        System.out.println("age: "+age);
        System.out.println("address: "+address);
    }
}

然后使用该类依赖应用的Address属性,这个我们之前就创建好了

package com.example.Spring.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
    private String cityName;
    private String cityCode;
}

这个Address是在XML中配置的

<bean id="address" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="北京"/>
    </bean>

测试类如下:

import com.example.Spring.model.People;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class PeopleTest {
    @Test
    public void PeopleTest(){
        //解析beans.xml文件,生成管理相应的bean对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        People people = (People) context.getBean("people");
        people.show();
    }
}

测试结果如下:

正常输出,说明了我们通过注解和xml混合开发也是可以的,甚至大多数场景下简单的用注解,复杂的用xml可以达到很好的效果,事实上,我们这些注解,就是替代了在配置文件当中配置步骤而已

Spring @Componen衍生注解

@Component有三个衍生注解,它们的具体实现时一样的,都是代表了一个Component,不同的命名只不过为了区分不同的层,为了更好的进行分层,Spring可以使用其它三个注解,目前使用哪一个功能都一样

  • @Controller:Controller层,也就是控制层,接口请求的入口
  • @Service:service层,业务逻辑层,具体的业务逻辑实现层
  • @Repository:dao层,持久化层,和数据库交互的层,事实上,这个层的实现我们上一个系列MyBatis刚介绍过。

写上这些注解,就相当于将这个类交给Spring管理装配了

自动装配的注解

上一篇Blog关于自动装配我们介绍过,Spring是如何通过自动装备将依赖自动进行装配的,具体实现细节是byName和byType,这里的自动装配注解也是为自动装配服务的。

Spring @Autowired 注解

需要注意的是,@Autowired是按类型byType自动转配的,不支持id也就是byName匹配:

package com.example.Spring.model;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Data
@Component(value = "people")
@Scope("prototype")
// 相当于配置文件中 <bean id="people" class="当前注解的类" scope="prototype"/>
public class People {
    @Value("tml")
    private String name;    // 相当于配置文件中 <property name="name" value="tml"/>
    @Value("30")
    private int age;
    @Autowired
    private Address address;  //虽然属性名不一致
    public void show(){
        System.out.println("name: "+name);
        System.out.println("age: "+age);
        System.out.println("address: "+address);
    }
}

XML中的配置为:

<bean id="address2" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="上海"/>
    </bean>

结果还是能正常打印出:

Spring @Qualifier 注解

@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配,@Qualifier不能单独使用,在使用@Qualifier之前,如果有两个类型一样的:

<bean id="address2" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="北京"/>
    </bean>
    <bean id="address1" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="上海"/>
    </bean>

那么会报类型发现多个的错误:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.Spring.model.Address' available: expected single matching bean but found 2: address2,address1
  at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)

配置上Qualifier 注解后,也可以使用多个相同类型了,每次可以指定一个使用:

package com.example.Spring.model;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Data
@Component(value = "people")
@Scope("prototype")
// 相当于配置文件中 <bean id="people" class="当前注解的类" scope="prototype"/>
public class People {
    @Value("tml")
    private String name;    // 相当于配置文件中 <property name="name" value="tml"/>
    @Value("30")
    private int age;
    @Autowired
    @Qualifier(value="address2")
    private Address address;
    public void show(){
        System.out.println("name: "+name);
        System.out.println("age: "+age);
        System.out.println("address: "+address);
    }
}

测试结果如下:

Spring @Resource 注解

@Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常。也就是@Resource的作用相当于@Autowired加上@Qualifier(value="address2"),我们设计XML配置如下:

<bean id="address2" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="指定name"/>
    </bean>
    <bean id="address" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="默认name"/>
    </bean>
    <bean id="address1" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="类型"/>
    </bean>

然后People使用Resource:

package com.example.Spring.model;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Data
@Component(value = "people")
@Scope("prototype")
// 相当于配置文件中 <bean id="people" class="当前注解的类" scope="prototype"/>
public class People {
    @Value("tml")
    private String name;    // 相当于配置文件中 <property name="name" value="tml"/>
    @Value("30")
    private int age;
    @Resource(name="address2")
    private Address address;
    public void show(){
        System.out.println("name: "+name);
        System.out.println("age: "+age);
        System.out.println("address: "+address);
    }
}

首先按照指定name装配,打印结果如下:

然后我们调整配置,干掉指定name的bean:

<bean id="address" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="默认name"/>
    </bean>
    <bean id="address1" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="类型"/>
    </bean>

同时调整Resource注解为:

@Resource
private Address address;

此时的打印结果如下:

然后我们把默认name的bean干掉,只留下类型bean在这里插入代码片

<bean id="address1" class="com.example.Spring.model.Address" >
        <property name="cityCode" value="100031"/>
        <property name="cityName" value="类型"/>
    </bean>

打印结果如下:

当然Resource注解不光能自动装配来自xml配置的bean,也能装配容器中管理的基于注解配置的bean,例如我们定义个model下该xml会扫描到的类:

package com.example.Spring.model;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class Room {
    @Value("豪宅")
    public String name;
}

然后再加上实现类配置:

package com.example.Spring.model;
import com.example.Spring.config.Zoom;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Data
@Component(value = "people")
@Scope("prototype")
// 相当于配置文件中 <bean id="people" class="当前注解的类" scope="prototype"/>
public class People {
    @Value("tml")
    private String name;    // 相当于配置文件中 <property name="name" value="tml"/>
    @Value("30")
    private int age;
    @Resource
    private Address address;
    @Resource
    private Room room;
    public void show(){
        System.out.println("name: "+name);
        System.out.println("age: "+age);
        System.out.println("address: "+address);
        System.out.println("room: "+room);
    }
}

实现结果如下:

所以Resource是万金油,推荐使用这个配置而不是Autowired

基于Java类进行配置开发

到目前为止,我们已经看到如何使用 XML 配置文件以及注解方式来配置 Spring Bean,可以发现即使是通过注解方式注入其实也是通过配置文件进行包扫描注入的,也就是说注解开发模式还是离不开XML配置,但其实我们接下来要说的这种基于Java类进行配置开发不需要任何XML配置文件,事实上,Config类代替了XML配置文件

1 JavaConfig类编写

我们首先编写一个JavaConfig类用来注册各种Bean,在包config下

package com.example.Spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration  //代表这是一个配置类
public class JavaConfig {
    @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
    public Zoom zoom(){
        return new Zoom();
    }
    @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
    public School school(){
        return new School();
    }
}

2 Bean的封装类编写

然后我们分别添加Bean的注册封装类:

Zoom类编写

package com.example.Spring.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class Zoom {
    @Value("动物园")
    public String name;
}

School 类编写

package com.example.Spring.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class School {
    @Value("学校")
    public String name;
}

注意这里我们的配置类和封装实现类都放置在config包下,并没有放到xml扫描的model包下,也就是说我们这里的实现方式和xml没有半毛钱关系。

3 测试实现

然后我们来测试下实现,注意我们这里不使用xml获取上下文,而是注解:AnnotationConfigApplicationContext

import com.example.Spring.config.JavaConfig;
import com.example.Spring.config.School;
import com.example.Spring.config.Zoom;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigTest {
    @Test
    public void JavaConfigTest(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
        School school = (School) applicationContext.getBean("school");
        System.out.println(school.name);
        Zoom zoom = (Zoom) applicationContext.getBean("zoom");
        System.out.println(zoom.name);
    }
}

打印结果如下:

总结一下

到这篇Blog为止,终于全面了解了基于元数据的IOC注入模式:XML配置、注解模式、Java配置类,这几种其实可以混合使用,在合适的场景使用合适的元数据注入模式,XML比较清晰,注解模式比较快捷,Java配置类模式完全不依赖配置文件,各有各的好吧,总的来说对于Spring我感觉还是以配置为主,因为没有很好的支持注解的实现方式。到后边的Spring Boot就是大量注解了,因为支持的比较好吧,逐渐的开始约定大于配置了。

相关文章
|
19天前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
94 7
|
1月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
63 5
|
1月前
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
114 2
|
1月前
|
人工智能 Java 数据库
飞算 JavaAI:革新电商订单系统 Spring Boot 微服务开发
在电商订单系统开发中,传统方式耗时约30天,需应对复杂代码、调试与测试。飞算JavaAI作为一款AI代码生成工具,专注于简化Spring Boot微服务开发。它能根据业务需求自动生成RESTful API、数据库交互及事务管理代码,将开发时间缩短至1小时,效率提升80%。通过减少样板代码编写,提供规范且准确的代码,飞算JavaAI显著降低了开发成本,为软件开发带来革新动力。
|
24天前
|
人工智能 Java 定位技术
Java 开发玩转 MCP:从 Claude 自动化到 Spring AI Alibaba 生态整合
本文以原理与示例结合的形式讲解 Java 开发者如何基于 Spring AI Alibaba 框架玩转 MCP。
679 87
|
1月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
226 70
|
24天前
|
人工智能 Java 定位技术
Java 开发玩转 MCP:从 Claude 自动化到 Spring AI Alibaba 生态整合
本文详细讲解了Java开发者如何基于Spring AI Alibaba框架玩转MCP(Model Context Protocol),涵盖基础概念、快速体验、服务发布与调用等内容。重点包括将Spring应用发布为MCP Server(支持stdio与SSE模式)、开发MCP Client调用服务,以及在Spring AI Alibaba的OpenManus中使用MCP增强工具能力。通过实际示例,如天气查询与百度地图路线规划,展示了MCP在AI应用中的强大作用。最后总结了MCP对AI开发的意义及其在Spring AI中的实现价值。
432 9
Java相关配置
Java相关配置
1315 0
|
3月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
240 60
【Java并发】【线程池】带你从0-1入门线程池
|
11天前
|
Java
java 多线程异常处理
本文介绍了Java中ThreadGroup的异常处理机制,重点讲解UncaughtExceptionHandler的使用。通过示例代码展示了当线程的run()方法抛出未捕获异常时,JVM如何依次查找并调用线程的异常处理器、线程组的uncaughtException方法或默认异常处理器。文章还提供了具体代码和输出结果,帮助理解不同处理器的优先级与执行逻辑。