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 添加定时任务注解
     */
相关推荐
云和数据.ChenGuang14 分钟前
Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
数据库·django·sqlite
woshilys42 分钟前
sql server 查询对象的修改时间
运维·数据库·sqlserver
Hacker_LaoYi42 分钟前
SQL注入的那些面试题总结
数据库·sql
建投数据2 小时前
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
数据库·腾讯云
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
你的微笑,乱了夏天3 小时前
linux centos 7 安装 mongodb7
数据库·mongodb
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
工业甲酰苯胺3 小时前
分布式系统架构:服务容错
数据库·架构