基于注解管理bean~

简介: 基于注解管理bean~

标记与扫描:

在以后的开发中,我们有service,control,dao等非常多的类型,需要交给IOC容器去管理,我们产品的一个功能模块对应的就是一个service,control,dao,如果我们现在都将其配置到spring文件中,当功能非常非常多的情况下,这样的配置方式可能会产生几十个甚至上百个bean标签,但如果我们使用了注解的方式,只需要在类上加上相对应的注解,经过spring的扫描之后,把有注解的类自动去匹配对应的bean,这一过程也就自动完成了将类交给IOC容器管理的过程,但若要通过基于注解的方式管理bean,那么这个注解是要添加至我们bean对象所对应的类上面的,如果我们想通过IOC容器去管理第三方jar包所提供的类,我们就不能通过注解来实现,因为该类不是我们自己编写的,我们不能在第三方jar包所提供的类的源码中添加注解,由此可说明,基于注解的方式虽然简单,但并不是万能的


注解:

和XML配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,它并不会在代码运行的过程中实现一些额外的功能,当我们加载或执行某个方法时,我们会去判断当前的类/成员变量/方法/参数上是否加了注解,如果有注解,那么则实现相对应的功能,具体的功能是:框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作


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


举例:


元旦联欢会要布置教室,蓝色的地方贴上元旦快乐四个字,红色的地方贴上花,黄色的地方贴上气球,如下所示


班长做了所有标记,同学们来完成具体工作,墙上的标记相当于我们在代码中使用的注解,后面的同学相当于框架中的具体操作


扫描:

spring为了知道程序员在那些地方标记了什么样的注解,就需要通过扫描的方式来进行检测,然后根据标记的注解进行后续的操作

实例演示如下:

新建一个工程或在当前工程中新建一个模块,完成三层架构的创建,具体的演示过程,可参考上篇文章


标识组件的常用注解:

所谓组件就是在IOC容器中,一个bean标签就是一个组件

//将该类作为一个bean被IOC管理
@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件

虽然上述的几个注解在名称上截然不同,但它的功能都是完全相同的,都是将类标识为一个组件,差别就在于标识的组件含义不同,在程序执行的过程中他们是没有任何区别的,但对于程序员来说,当看到某类上添加的注解为@Controller,那么便知该组件为控制层组件


它们之间的关系和区别,如下所示为其源码:



通过查看源码我们得知,@Controller,@Service,@Repository这三个注解只是在@Component注解的基础上新起的名字,对于spring使用IOC容器管理这些组件来说没有任何区别,所以@Controller,@Service,@Repository是给开发人员看的,让我们能够便于分辨组件的作用


注意:虽然他们本质一样,但为了代码的可读性和为了程序结构严谨性,我们不能随便标记,有什么样的功能就标识为什么样的组件,没有任何功能的组件,我们直接添加@Component注解即可


创建组件:

组件的注解只能加在类上,而不能加在接口上,原因是一个bean标签就是一个组件,而bean对象只能是某个类或者某个接口的实现类,如下所示,对于业务层,我们想添加@Service的注解,那么其注解不能添加在UserService上,而要添加至实现类上


其他的类或实现类我们都需要添加对应的注解:

//业务层实现类添加@Service注解将其标识为业务层组件
@Service
public class UserServiceImpl implements UserService {
//控制层类添加@Controller注解将其标识为控制层组件
@Controller
public class UserController {
//持久层实现类添加@Repository注解将其标识为持久层组件
@Repository
public class UserDaoImpl implements UserDao {

组件创建完成之后, spring必须通过扫描才能发挥其作用

扫描组件:

<!-- 通过context:约束进行扫描,base-package后面的值为我们要扫描的包名,当前包下的所有类都会被扫描到-->
<context:component-scan base-package="包名"></context:component-scan>

为了简化代码量,我们可以将所有的包都放在一个公共的包下,如下所示:

那么在其约束中,我们就可以直接写总包名即可,如果要扫描的类在不同的包下,那么需要写多个context约束对每个都进行扫描

<context:component-scan base-package="springIocAnnotation_packages"></context:component-scan>


编写测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springIocAnnotation_packages.spring_Service.UserService;
import springIocAnnotation_packages.spring_Dao.UserDao;
import springIocAnnotation_packages.spring_controller.UserController;
public class spring_ByAnnotationTest {
    @Test
    public void test(){
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
        UserController userController= ioc.getBean(UserController.class);
        System.out.println(userController);
        //当组件实现了接口,只要bean对象是唯一的,即使实现该接口的类不唯一,仍然可以使用该接口进行获得bean对象
        UserService userService= ioc.getBean(UserService.class);
        System.out.println(userService);
        UserDao userDao= ioc.getBean(UserDao.class);
        System.out.println(userDao);
    }
}

输出如下:

springIocAnnotation_packages.spring_controller.UserController@272ed83b
springIocAnnotation_packages.spring_Service.impl.UserServiceImpl@41fecb8b
springIocAnnotation_packages.spring_Dao.impl.UserDaoImpl@120f102b

我们成功的获取到了这三个对象,那么说明扫描和注解的工作已成功为完成,并且还能说明注解加扫描的这种方式的对象确实是被IOC容器在管理,否则我们根本无法通过IOC容器获取到它们


关于扫描组件其实还是存在一些问题的, 此后在进行SSM整合的时候,spring和spring MVC是要放在一起使用的,spring MVC需要扫描的是控制层,而spring需要扫描的是除控制层以外所有的组件,也就是说,当spring MVC扫描了控制层,而spring也扫描了控制层,这样就会导致控制层的组件被扫描了多次,那么有的小伙伴就会说,那我们在spring.xml文件中使用约束的话,我们通过代码设置不让他重复扫描控制层的包不就行了吗?


但我们说过,一个功能模块对应一个三层架构,如果真的使用上述的这种方法,岂不是很麻烦吗?


针对上述这种问题,spring为我们提供了方法去解决该问题!


排除扫描与只扫描:

spring在context标签中为我们提供了两个子标签,如下所示:

expression属性用来指定相关的类或者注解的全类名等,全类名如何查看?

根据注解进行排除扫描:

修改XML文件中的context标签:

<context:component-scan base-package="springIocAnnotation_packages">
          <!-- 假设我们规定不扫描控制层
        type属性的值为:annotation,expression属性的值为要排除或者要包含的注解的全类名-->
           <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

其他不做任何修改的情况下,进行测试:

抛出异常:没找到UserController的bean对象,由此可说明我们经过上面的在context的子标签中, 设置不扫描控制层,而我们的设置也实现了相对应的功能,因为其他地方我们并没有进行修改呀

那么这样进行设置时候是否会影响其他组件的扫描呢?

一测便知,我们将获取UserController类的bean对象的代码注释,尝试获取其他的bean对象,测试结果如下:

根据类类型进行排除扫描:

修改XML文件中的type属性的值:

<context:component-scan base-package="springIocAnnotation_packages">
          <!-- 假设我们规定不扫描控制层  type属性的值为:assignable,expression属性的值为要排除或者要包含的类的全类名-->
           <context:exclude-filter type="assignable" expression="springIocAnnotation_packages.spring_controller.UserController"/>
</context:component-scan>

我们通过类类型进行排除扫描控制层也是可以的,测试结果依然是抛出找不到控制层的bean对象

根据注解只对其进行扫描:

修改XML文件中的context标签:

<context:component-scan base-package="springIocAnnotation_packages">
          <!-- 假设我们规定只扫描控制层-->
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

测试结果如下,怎么三个都扫描到了呀,我们命名在context标签中设置只扫描控制层啊

原因如下:

那么为什么上面排除扫描并没有受这个规则的影响啊,原因是:该规则是默认扫描当前包下的所有类,而我们通过exclude去排除了不需要扫描的包,但此时我们想通过include去定义只扫描某个包,如果也仅仅使用这个include标签,那么矛盾点在于,该规则本身就是扫描当前包下的所有类啊,你通过include去指定再扫描某个类是没有任何意义的,它不仅会扫描你指定的,顺带将其他的也会被扫描


修改方法:


将use-default-filters 属性的值从默认的true修改为false即可

<context:component-scan base-package="springIocAnnotation_packages" use-default-filters="fal

测试结果如下所示:

因此在使用context:include-filter时,一定不要忘记将use-default-filters的值设置为false,在扫描组件的过程中,我们可以设置多个排除或者多个包含,而不能又设置多个排除又设置多个包含,这样是没有意义的,排除的反面就是包含呀,而且从代码角度来讲,如果一个项目的所有的包都位于一个总包下,那么我们设置包含就必须设置use-default-filters为false,如果我们设置排除,那么它的值必须为true,因此,二者是无法同时存在的


注解+扫描的方式是将注解标识的类经过扫描之后在IOC容器中自动配置一个相对应的bean,这个bean不需要我们配置,通过注解+扫描的方式配置的bean-->它是一个bean标签,class就是当前加上注解的类的全类名,但bean都是有id的呀,那通过注解+扫描的方式配置的bean的id是什么呀?

答案是:默认值为类的小驼峰[类名的首字母小写的结果]


举例:

UserController---->userController
UserService---->userService
自定义其id值:

举例:

//将控制层中的注解值设置为controller
@Controller("controller")
//测试类中尝试通过id+类的方式获取bean对象,注意这里我们的id是默认值
UserController userController= ioc.getBean("userController",UserController.class);

测试结果如下所示:

产生异常的原因就是因为我们已经通过修改注解值的方式将其bean对象的id修改了呀,继续使用默认的id值当然获取不到

尝试使用修改后的值获取bean对象:

UserController userController= ioc.getBean("controller",UserController.class);

测试结果如下:获取成功

相关文章
|
关系型数据库 BI 分布式数据库
PolarDB NL2BI解决方案,让你不懂SQL也能进行数据查询分析并生成BI报表
无需创建和开通资源,在预置环境中免费体验PolarDB MySQL及其NL2BI解决方案
PolarDB NL2BI解决方案,让你不懂SQL也能进行数据查询分析并生成BI报表
|
监控 Java Spring
Spring Boot中使用Actuator进行监控
Spring Boot中使用Actuator进行监控
|
分布式计算 大数据 开发者
Rdd 算子_转换_groupbykey | 学习笔记
快速学习 Rdd 算子_转换_groupbykey
215 0
Rdd 算子_转换_groupbykey | 学习笔记
|
18天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
9天前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
|
12天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
1062 36
|
12天前
|
机器学习/深度学习 人工智能 搜索推荐
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
近期发生了什么自 2025 年 2 月 OpenAI 正式发布Deep Research以来,深度研究/深度搜索(Deep Research / Deep Search)正在成为信息检索与知识工作的全新范式:系统以多步推理驱动大规模联网检索、跨源证据。
821 56
下一篇
开通oss服务