Spring系统学习 - 基于注解管理Bean

什么是基于注解的方式管理Bean

在 Spring 框架中,基于注解的方式管理 Bean 是一种非常流行且现代的方法。它允许你通过在类、方法或字段上添加特定的注解来声明 Bean 的创建和依赖注入,从而避免了在 XML 配置文件中定义 Bean 的繁琐工作。

注解和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

用通俗的话来说就是,不管是注解还是XML实际上就相当于,我们在现实生活中,假设你手上有一群人,然后你需要这群人去做三个任务,对于你标记为红色区域的,要放置红色的花朵,你标记为黄色的区域就放置黄色话多,标记为绿色的地方,就放置绿色草块,你标记完毕之后,剩下的放置花朵和草块的地方就交给你手上的那群人去完成就行。

扫描

上面我举了一个例子,用来标记不同颜色的地方,然后分别对不同颜色的地方做不同的事情,那么Spring是如何知道程序员在哪些地方标记了哪些注解呢?

spring是通过扫描的方式来进行检测,在检测成功之后,根据我们配置的注解来进行后续操作。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SSM</artifactId>
        <groupId>com.miaow</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-annotation</artifactId>
    <packaging>jar</packaging>

    <name>spring-annotation</name>
    <description>Spring基于注解管理Bean</description>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


</project>

@Component, @Service, @Repository, @Controller

这些是组件扫描时使用的注解,它们都是@Component的特殊化,用于标记特定类型的类。Spring会自动发现并注册这些带有注解的类为Bean。

  • @Component:通用注解,可以用于任何层次的类。
  • @Service:通常用于标注业务层服务类。
  • @Repository:通常用于标注数据访问层(如DAO类)。
  • @Controller:用于标注控制器层,主要用于处理HTTP请求。

我们通过看了@Controller注解为例子,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。

对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。

案例

目录结构

创建组件

创建控制层

java 复制代码
@Controller("controller") //自定义bean的id
//@Controller
public class UserController {


    /**
     * 能够找到唯一的bean:直接执行装配
     * 如果完全找不到匹配这个类型的bean:装配失败
     * 如果找到多个匹配这个类型的bean:
     * 1.如果这个类型的bean只有一个,那么直接装配
     * 2.如果这个类型的bean有多个,那么就需要通过@Qualifier注解来指定装配哪个bean
     *
     * 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配
     *
     */
    @Autowired
    @Qualifier("userServiceImpl")  //byName 根据@Qualifier注解中指定的名称作为bean的id进行匹配
    private UserService userService;

    public String getUser(){
        return "user";
    }

    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void savaUser(){
        userService.saveUser();
    }
}

创建Service接口

java 复制代码
public interface UserService {
    void add();

    void saveUser();
}

创建Service接口实现层

java 复制代码
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public void add() {
        System.out.println("添加成功");
    }

    @Override
    public void saveUser() {
        userDao.saveUser();
    }
}

创建Dao层接口

java 复制代码
public interface UserDao {

    /**
     * 保存用户
     */
    int saveUser();
}

创建Dao层接口实现层

java 复制代码
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public int saveUser() {
        System.out.println("保存成功");
        return 1;
    }
}

创建一个User类

java 复制代码
//其实没啥用
public class User {
    public void sayHello(){
        System.out.println("hello");
    }
}

创建一个spring-annotation.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 https://www.springframework.org/schema/context/spring-context.xsd">


<!--    扫码组件的几种方式  -->
<!--   1 基于包内的最基本的扫描方式 -->
    <context:component-scan base-package="com.miaow.spring">

        <!--    2:指定要排除的组件 -->
        <!-- context:exclude-filter标签:指定排除规则 -->
        <!--
            type:设置排除或包含的依据
            type="annotation",根据注解排除,expression中设置要排除的注解的全类名
            type="assignable",根据类型排除,expression中设置要排除的类型的全类名
        -->
        <!--    排查扫描控制层 建议配置方式-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>-->
        <!--    单个指定太麻烦了,不推荐方式    -->
<!--        <context:exclude-filter type="assignable" expression="com.miaow.spring.controller.UserController"/>-->


<!--        3 仅仅扫描指定的组件   use-default-filter="false" -->
        <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
        <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
        <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
        <!--
            type:设置排除或包含的依据
            type="annotation",根据注解排除,expression中设置要排除的注解的全类名
            type="assignable",根据类型排除,expression中设置要排除的类型的全类名
         -->
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>-->
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"></context:include-filter>-->

        <!--        <context:include-filter type="assignable" expression="com.miaow.spring.controller.UserController"/>-->

    </context:component-scan>
</beans>

创建测试类

java 复制代码
    @Autowired
    private UserService userService;

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
       // UserController controller = (UserController) context.getBean("userController");//这样发现我们获取不到
        UserController controller = (UserController) context.getBean("controller");//这样我们发现可以获到相关值
        System.out.println(controller);
    }

    //测试@AutrWired注解
    /**
     * Autowired工作流程
     * 1. 通过反射获取类中的属性
     * 2. 通过反射获取属性上的注解
     * 3. 通过注解获取属性的名称
     * 4. 通过名称获取bean
     * 5. 将bean设置到属性上
     * 6. 将bean设置到ioc容器中
     */

    @Test
    public void testAutowired(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
        // UserController controller = (UserController) context.getBean("userController");//这样发现我们获取不到
        UserController controller = (UserController) context.getBean("controller",UserController.class);//这样我们发现可以获到相关值
        System.out.println(controller);
        controller.savaUser();
    }

其他注解(拓展)

@Autowired

@Autowired注解用于自动装配Bean,Spring会自动将匹配的Bean注入到标记了该注解的字段或方法上。默认按类型匹配,如果需要按名称匹配 ,可以与@Qualifier一起使用。

java 复制代码
@Autowired
private SomeService someService;

首先根据所需要的组件类型到IOC容器中查找

  • 能够找到唯一的bean:直接执行装配
  • 如果完全找不到匹配这个类型的bean:装配失败
  • 和所需类型匹配的bean不止一个
    • 没有@Qualifier注解 :根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配

    • 能够找到:执行装配

    • 找不到:装配失败

    • 使用@Qualifier注解 :根据@Qualifier注解中指定的名称作为bean的id进行匹配

    • 能够找到:执行装配

    • 找不到:装配失败

@Autowired中有属性required,默认值为true,因此在自动装配无法找到相应的bean时,会装配失败,可以将属性required的值设置为true,则表示能装就装,装不上就不装,此时自动装配的属性为默认值,但是实际开发时,基本上所有需要装配组件的地方都是必须装配的,用不上这个属性

@Resource

@Resource注解来源于JDK,与@Autowired类似,也是用于依赖注入,但它可以根据名称进行注入,如果没有指定名称,则默认按照类型匹配

java 复制代码
@Resource
private SomeService someService;

@Configuration 和 @Bean

@Configuration类允许你通过Java类的方式提供Spring容器的配置,而@Bean注解告诉Spring这是一个Bean的定义,用来创建Bean实例。

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

@Value

@Value注解用于注入属性值,可以直接注入硬编码的值,也可以注入外部配置文件中的值。

java 复制代码
@Value("${property.name}")
private String propertyName;

@Scope

@Scope注解用于定义Bean的作用域,默认是singleton(单例),也可以设置为prototype(原型)、request、session等。

java 复制代码
@Scope("prototype")
@Service
public class MyPrototypeService {}

启用组件扫描

为了使Spring能够自动发现这些带有注解的类,需要在配置类或XML配置文件中启用组件扫描。在Java配置中,可以使用@ComponentScan注解来实现:

java 复制代码
@Configuration
@ComponentScan(basePackages = {"com.example.myapp"})
public class AppConfig {}

Sping和SpringBoot中的一些注解介绍

java 复制代码
    /**
     * @Contoller 添加控制层注解
     * @Service 添加service层注解
     * @Repository 添加dao层注解
     * @Component 添加普通bean注解
     * @Autowired 添加自动注入注解
     * @Qualifier 添加bean的名称注解
     * @Resource 添加自动注入注解
     * @Value 添加属性注入注解
     * @Primary 添加主要bean注解
     * @Lazy 添加延迟加载注解
     * @Scope 添加作用域注解
     * @Configuration 添加配置类注解
     * @Bean 添加bean注解
     * @Import 添加导入注解
     * @ImportResource 添加导入资源注解
     * @PropertySource 添加属性源注解
     * @ComponentScan 添加组件扫描注解
     * @Conditional 添加条件注解
     * @Profile 添加配置文件注解
     * @ConfigurationProperties 添加配置属性注解
     * @EnableAspectJAutoProxy 添加切面注解
     * @Aspect 添加切面注解
     * @Pointcut 添加切点注解
     * @Before 添加前置通知注解
     * @After 添加后置通知注解
     * @AfterReturning 添加返回通知注解
     * @AfterThrowing 添加异常通知注解
     * @Around 添加环绕通知注解
     * @Order 添加排序注解
     * @Transactional 添加事务注解
     * @EnableTransactionManagement 添加事务管理注解
     * @EnableAspectJAutoProxy 添加切面注解
     * @EnableCaching 添加缓存注解
     * @Cacheable 添加缓存注解
     * @CacheEvict 添加缓存注解
     * @CachePut 添加缓存注解
     * @Caching 添加缓存注解
     * @EnableCaching 添加缓存注解
     * @EnableScheduling 添加定时任务注解
     * @Scheduled 添加定时任务注解
     */
相关推荐
EterNity_TiMe_9 分钟前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
tatasix10 分钟前
MySQL UPDATE语句执行链路解析
数据库·mysql
sanguine__13 分钟前
java学习-集合
学习
lxlyhwl13 分钟前
【STK学习】part2-星座-目标可见性与覆盖性分析
学习
nbsaas-boot14 分钟前
如何利用ChatGPT加速开发与学习:以BPMN编辑器为例
学习·chatgpt·编辑器
一元咖啡17 分钟前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
南城花随雪。22 分钟前
硬盘(HDD)与固态硬盘(SSD)详细解读
数据库
儿时可乖了23 分钟前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
懒是一种态度25 分钟前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
天海华兮28 分钟前
mysql 去重 补全 取出重复 变量 函数 和存储过程
数据库·mysql