3. Spring 注解存储对象 & Bean的命名规范

从Java5.0开始,Java开始支持注解。Spring做为Java生态中的领军框架,从2.5版本后也开始支持注解。相比起之前使用xml来配置Spring框架,使用注解提供了更多的控制Spring框架的方式。

SpringFramework版本对应jdk版本重要特性
SpringFramework 1.xjdk 1.3基于 xml 的配置
SpringFramework 2.xjdk 1.4改良 xml 文件、初步支持注解式配置
SpringFramework 3.xJava 5注解式配置、JavaConfig 编程式配置、Environment 抽象
SpringFramework 4.xJava 6SpringBoot 1.x、核心容器增强、条件装配、WebMvc 基于 Servlet3.0
SpringFramework 5.xJava 8SpringBoot 2.x、响应式编程、SpringWebFlux、支持 Kotlin

1. 配置扫描路径

添加下面配置语句到配置文件中:

<content:component-scan base-package="org.example"></content:component-scan>

base-package="org.example"表示注册扫描的包

image.png

对Bean对象的查找只会在这个路径以及它的所有子路径中。设计者的目的是为了缩小扫描的范围,节省开销,提高性能。

配置作用:

  1. 扫描包内及其子包内的所有“类”(不包含接口),并为添加了@Service、@Component、@Controller、@Repository修饰的类创建对象并存入IOC容器

  2. @Service、@Component、@Controller、@Repository修饰的类中含有@Autowired修饰的成员变量,则创建对象时会从IOC容器中取值为该成员变量赋值

这里先提一嘴,后续慢慢道来。。

此时的整个配置文件是长这样的:

<?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:content="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">
    <content:component-scan base-package="org.example"></content:component-scan>
</beans>

2. 添加注解存储 Bean 对象

想要将对象存储在Spring中,有两种注解类型可以实现:

五大类注解:@Controller、@Service、@Repository、@Configuration、@Component

以及方法注解:@Bean

通过注解存储bean对象:

@Controller
public class UserController {
    public void doController() {
        System.out.println("I'm controller.");
    }
}

3. 取出Bean

这里我们使用下面这种getBean()方法,和只传String的参数差不多,但是下面这种方式不需要强转类型,可读性更高:

image.png

3.1 Bean的默认命名

在使用getBean方法的时候开始犯难了,这个Bean对象的id是什么呢?

在Spring中,以注解形式存储的对象,id默认为类名的小驼峰形式,比如"UserController"类,它的id就是"userController"

3.2 使用Bean

public class Application {
   public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
       UserController userController =
               context.getBean("userController", UserController.class);
       userController.doController();

   }
}

image.png

3.3 Bean的特殊命名【3.1补充】

学会使用Bean之后,我们需要给Bean的命名规则进行一个填坑

特殊情况:当我把类命名为"UController"的时候,再用上面那种方式去找"uController"就会找不到 Bean:


public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    UserController userController =
            context.getBean("userController", UserController.class);
    userController.doController();

    UController uController =
            context.getBean("uController", UController.class);
}

image.png

查看一下Bean名称命名的源码:

protected String buildDefaultBeanName(BeanDefinition definition) {
    String beanClassName = definition.getBeanClassName();
    Assert.state(beanClassName != null, "No bean class name set");
    String shortClassName = ClassUtils.getShortName(beanClassName);

//发现这里调用了jdk中的方法,传入BeanName的参数:

    return Introspector.decapitalize(shortClassName);
}

点进去查看一下decapitalize()方法:

public static String decapitalize(String name) {
    //如果name 为空,直接返回原name
    if (name == null || name.length() == 0) {
        return name;
    }
    //如果第一个字母和第二个字母都是大写的情况下,直接返回原name
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    //否则将第一个字符转为小写
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

看了上面的源码,我们终于豁然开朗,原来当第一个字母和第二个字母都是大写的情况下,Bean的Id并不需要改变:

4. 五大类注解的关系

  1. @Controller【控制器】:前后端交互的第一层 ,前端的数据一定是先到Controller层的
  2. @Service【服务】:第二层,负责服务的编排和汇总
  3. @Repository【仓库】:第三层,负责直接操作数据库
  4. @Configuration【配置】:项目的所有配置
  5. @Component【组件】:通用化的工具类

查看下面四种类注解的实现,它们都带着一个@Component的注解 :

结论:@Controller、@Service、@Repository、@Configuration都是基于@Component实现的,它们都作用都是将 Bean 存到 Spring 中

5. 方法注解@Bean

方法注解是用来修饰方法的,它会将该方法所返回的对象存入Spring中

注意事项:

  • 方法注解必须配合五大类注解使用,否则在取 Bean 的时候会出现找不到 Bean 对象的问题
  • 方法必须是无参的

5.1 方法注解存取对象

创建一个user的model类:

public class User {
    private int userId;
    private String userName;
    private int age;
    //记得generate一下getter和setter以及toString方法
    //...
}

存储Bean:

//搭配任意类注解
@Component
public class GetUserBean {

    @Bean
    User user() {
        User user = new User();
        user.setUserId(1);
        user.setUserName("zhangsan");
        user.setAge(19);
        return user;
    }
}

取出Bean:

image.png

5.2 @Bean的命名

跟五大类注解不一样,Bean的Id是根据方法名按照前面所提到的命名规则得到的:
为什么Bean根据方法名来命名呢?

因为不同的方法返回相同类型的对象是一件很常见的事情,设计者考虑到这点,因此通过@Bean注解存储的Bean的Id是根据方法名来命名。

5.2.1 常见问题

但是与类名不同,在不同类中,有同样的方法名也不是一件奇怪的事,遇到了这种情况会怎么样呢?

@Component
public class GetUserBean {
    @Bean
    User user() {
        User user = new User();
        user.setUserId(1);
        user.setUserName("GetUserBean类:zhangsan");
        user.setAge(19);
        return user;
    }
}
@Component
public class GetUserBean_cp {
    @Bean
    User user() {
        User user = new User();
        user.setUserId(1);
        user.setUserName("GetUserBean_cp类:zhangsan");
        user.setAge(19);
        return user;
    }
}

结果发现并没有报错,结果如下:

image.png

但是这种结果并不是我们想要的,既然我们存了两个bean对象,应该在不同场景需要用不同的bean对象,接下来我们将这个问题的解决方案。

5.2.2 解决方案

通过给@Bean注解传参数取别名:
valuename参数都是用来修改被标注bean在IOC容器中的id属性。并且取别名后原来的方法名的id就用不了了。

@Component
public class GetUserBean {
    @Bean(value = "user_default")
    User user() {
        //....省略代码
    }
}
@Component
public class GetUserBean_cp {
    @Bean(value = "user_cp")
    User user() {
        //....省略代码
    }
}

列举所有取别名写法:

  • value:@Bean(value = "user_cp")
  • name:@Bean(name = "user_cp")
  • 无参:@Bean("user_cp")
  • 多个id:@Bean({"user_cp", "student", teacher"})

使用不同的id取Bean:

image.png

6. 总结

本章节我们学习了:

  • Spring通过注解的方式存储对象,只需要在注解中配置一个扫描路径,简化了在xml文件中配置bean对象的步骤
  • 初步了解了五大类注解@Controller、@Service、@Repository、@Configuration、@Component以及一个方法注解@Bean的使用
  • 类注解和方法注解命名规范以及区别
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

干脆面la

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值