基于注解管理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);

测试结果如下:获取成功

相关文章
|
3月前
|
Java Spring
Spring的Bean生命周期中@PostConstruct注解
【8月更文挑战第3天】在Spring框架中,`@PostConstruct`注解标示Bean初始化完成后立即执行的方法。它在依赖注入完成后调用,适用于资源加载、属性设置等初始化操作。若方法中抛出异常,可能影响Bean初始化。与之对应,`@PreDestroy`注解的方法则在Bean销毁前执行,用于资源释放。
125 0
|
XML 设计模式 Java
Spring——Bean管理-注解方式进行属性注入
简化xml方式开发,只需要注解就可以完成在配置文件中的配置
|
6月前
|
XML Java 数据格式
spring之基于注解管理Bean
【1月更文挑战第14天】 从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。 Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。 Spring 通过注解实现自动装配的步骤如下: 1. 引入依赖 2. 开启组件扫描 3. 使用注解定义 Bean 4. 依赖注入
95 1
|
6月前
|
XML Java 数据格式
Spring 管理 Bean-IOC--基于注解配置 bean
Spring 管理 Bean-IOC--基于注解配置 bean
72 0
|
XML Java 数据格式
配置bean的4种情况
只有聪明人才能看见的摘要~( ̄▽ ̄~)~
78 2
|
XML 前端开发 Java
Spring-基于注解的配置[01定义Bean+扫描Bean]
Spring-基于注解的配置[01定义Bean+扫描Bean]
121 0
|
Java Spring 容器
88. 你能说说 Spring 中,接口的bean是如何注入的吗
88. 你能说说 Spring 中,接口的bean是如何注入的吗
172 1
88. 你能说说 Spring 中,接口的bean是如何注入的吗
|
XML 数据格式
Sping5学习(五):IOC操作Bean管理 之 基于注解方式的属性注入
Sping5学习(五):IOC操作Bean管理 之 基于注解方式的属性注入
Sping5学习(五):IOC操作Bean管理 之 基于注解方式的属性注入
|
Java 容器 Spring
Spring基础篇:利用注解将外部Properties属性注入到Bean中的方法
利用注解将外部Properties属性注入到Bean中的方法
164 0
|
Java Spring
Spring—Bean的装配方式—基于注解的七种装配方式
Spring—Bean的装配方式—基于注解的七种装配方式
166 0