(a)Spring注解式开发,注册组件的@Repository,@Service,@Controller,@Component使用及说明

您所在的位置:网站首页 component和repository注解的区别 (a)Spring注解式开发,注册组件的@Repository,@Service,@Controller,@Component使用及说明

(a)Spring注解式开发,注册组件的@Repository,@Service,@Controller,@Component使用及说明

2024-06-16 18:04| 来源: 网络整理| 查看: 265

注解扫描原理 通过反射机制获取注解 @Target(value = {ElementType.TYPE})// 设置Component注解可以出现的位置,以上代表表示Component注解只能用在类和接口上 @Retention(value = RetentionPolicy.RUNTIME)// 设置Component注解的保持性策略,以上代表Component注解可以被反射机制读取 public @interface Component { // 注解中的一个属性, 该属性类型String,属性名是value String value(); }

假设我们现在只知道一个包名, 这个包下有多少个Bean我们不知道, Bean上有没有注解也不知道,如何通过程序自动将类上有注解的Bean实例化

@Component(value = "userBean")// 语法格式:@注解类型名(属性名=属性值, 属性名=属性值, 属性名=属性值......) public class User { } // 没有注解的Bean public class Vip { } public class ComponentScan { public static void main(String[] args){ // 存放Bean的Map集合,key存储beanId,value存储Bean Map beanMap = new HashMap(); // 通过包的名字扫描这个包下所有的类,当这个类上有@Component注解的时候实例化该对象,然后放到Map集合中 String packageName = "com.powernode.bean"; // 开始写扫描程序,将包名换成路径获取目录下的所有文件 // 在正则表达式中"."属于通配符代表任意字符,使用"\."代表一个普通的"."字符(java中"\"表示转义字符所以需要使用"\\.") String packagePath = packageName.replaceAll("\\.", "/"); String packagePath = packageName.replaceAll("\\.", "/"); // 从类的根路径下加载资源,自动返回一个URL类型的对象(路径) URL url = ClassLoader.getSystemClassLoader().getResource(packagePath); // 获去扫描的类所在的绝对路径 String path = url.getPath(); // 获取一个绝对路径下的所有文件 File file = new File(path); // 遍历这个路径下的所有文件,每个文件都是File对象,最后存到一个File类型的数组中 File[] files = file.listFiles(); Arrays.stream(files).forEach(f -> { try { // f.getName()获取com.powernode.bean包下的文件名User.class和Vip.class // f.getName().split("\\.")[0],先通过"."对文件名进行拆分,然后取数组的第一个元素即文件的简类名User和Vip // 拼接字符串得到文件的全类名com.powernode.bean.User和com.powernode.bean.Vip String className = packageName+ "." + f.getName().split("\\.")[0]; // 通过全类名获取类的字节码对象 Class aClass = Class.forName(className); // 判断类上是否有Component注解 if (aClass.isAnnotationPresent(Component.class)) { // 获取Component注解 Component annotation = aClass.getAnnotation(Component.class); // 获取Component注解的属性值及即bean的id String id = annotation.value(); // 有Component注解的都要创建对象 Object obj = aClass.newInstance(); // 将创建的对象放入Map集合当中 beanMap.put(id, obj); } } catch (Exception e) { e.printStackTrace(); } }); // Map集合中只有User对象,因为Vip类上没有Component注解 System.out.println(beanMap); } } 注册组件的四个注解

注解的存在主要是为了简化XML的配置, Spring6倡导全注解开发,所以只要使用了Spring的注解就要使用包扫描机制

使用注解一般加入的是自己写的组件 , 使用bean标签配置加入的是别人的写的组件 , 开发中常用注解和bean配置相结合的方式 标识Bean的四个注解

使用标识Bean的四个注解和使用XML配置的方式将组件加入到容器中后组件的默认行为都是一样的 , 组件都有id和默认作用域

四个注解都只有一个value属性用来指定bean的id,bean的名字默认是组件的简单类名首字母小写后得到的字符串 , 作用域默认就是单例的优点: 可读性比较好简化了bean的声明, 更符合MVC的设计理念, 这种声明bean的方式是目前企业中较为常见的bean的声明方式缺点: 没有任何一个地方可以查阅整体信息,只有当程序运行起来才能感知到加载了多少个bean

实际这四个注解用哪个都可以 , 它们的功能都是只能起到标识的作用,Spring并没有能力识别一个组件到底是不是它所标记的MVC架构类型

即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色,增强程序的可读性 注解名功能@Repository标识一个受Spring IOC容器管理的持久化层组件,给数据库层 (持久化层 , dao层)的组件添加这个注解@Service标识一个受Spring IOC容器管理的业务逻辑层组件,推荐给业务逻辑层的组件添加这个注解@Controller标识一个受Spring IOC容器管理的表述层控制器组件,推荐给控制层也就是Servlet包下的这些组件加这个注解@Component标识一个受Spring IOC容器管理的普通组件,给不属于以上几层的组件添加这个注解@Scope指定加入的组件是多实例的还是单实例的,默认是单实例的 , prototype属性表示指定的bean是多实例的 四个注解的源码

@Component源码: @Controller、@Service、@Repository这三个注解都是@Component注解的别名,使用别名的方式可读性更好

@Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Component { String value(); }

@Repository , @Controller ,@Service注解源码

@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { // 别名 @AliasFor( annotation = Component.class ) String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { // 别名 @AliasFor( annotation = Component.class ) String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { // 别名 @AliasFor( annotation = Component.class ) String value() default ""; } 使用Spring的IoC注解 使用步骤

第一步: 引入aop的依赖才支持注解模式(如果加入spring-context依赖之后会关联加入aop的依赖)

在这里插入图片描述

第二步:使用context:component-scan标签让Spring去指定包中扫描加了注解的组件,并将这些组件实例化后加入到IoC容器中同时管理这些bean对象

在xmlns头部信息中添加context命名空间的配置信息:xmlns:context="http://www.springframework.org/schema/context"在xsi约束文件中添加约束信息:http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

context:component-scan标签的属性

属性名功能base-package指定一个需要扫描的基类包,默认Spring容器会扫描这个指定的基类包及其子包中的所有类resource-pattern扫描基类包下特定的类,如仅希望扫描基类包下特定的类而非所有类(*表示匹配任意个字符)

第三步:在Bean类上使用注解, 虽然Component注解换成其它三个注解照样创建bean对象,但为了可读性应该根据属于MVC架构模式的哪一层加对应类型注解

//@Component(value = "userBean") @Component("userBean") public class User { } public class AnnotationTest { @Test public void testBean(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); User userBean = applicationContext.getBean("userBean", User.class); System.out.println(userBean); } } 使用细节

如果IoC注解没有设置value属性,Spring会为bean自动取名默认是bean首字母小写后的类名

@Repository// 等价于 public class BankDao { } public class AnnotationTest { @Test public void testBean(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); BankDao bankDao = applicationContext.getBean("bankDao", BankDao.class); System.out.println(bankDao); } }

扫描多个基类包下加了注解的类

第一种: 在配置文件中指定多个基类包使用逗号隔开第二种: 指定多个基类包的共同父包,但是这样扫描范围就大了肯定要牺牲一部分效率 选择性实例化包下的Bean

假设在某个包下很多类上都标注了不同类型的注解,现在由于特殊业务的需要只允许其中所有的加了Controller注解的类参与Bean管理,其他的都不实例化

@Component public class A { public A() { System.out.println("A的无参数构造方法执行"); } } @Controller class B { public B() { System.out.println("B的无参数构造方法执行"); } } @Service class C { public C() { System.out.println("C的无参数构造方法执行"); } } @Repository class D { public D() { System.out.println("D的无参数构造方法执行"); } }

context:component-scan标签的属性

属性值功能use-default-filters=“true”(默认)只要扫描的bean上有Component、Controller、Service、Repository中的任意一个注解都会进行实例化use-default-filters=“false”不再使用spring默认实例化规则, 让所有bean上的Component、Controller、Service、Repository注解全部失效

context:component-scan标签的子标签(指定包含与排除基类包下的哪些类)

标签名功能context:exclude-filter指定扫描基类包时要排除在外的目标类,type属性指定排除的规则 , 默认全部扫描进来并实例化context:include-filter指定扫描基类包时要包含在内的目标类,扫描时一定要禁用默认的过滤规则(默认全都扫描) , type属性指定排除规则

context:exclude-filter标签和context:include-filter标签的type属性和expression属性的值

Type属性的值expression属性的值说明annotation要过滤的注解的全类名按照bean上的注解类型进行过滤 (常用)assignable要过滤的类的全类名按照类的全类名过滤指定类和它的子类aspectjcom.atguigu.*Service根据AspectJ表达式进行过滤 (常用) , 过滤所有类名是以Service结束的类或其子类regexcom.atguigu.anno.*根据正则表达式匹配到的类名进行过滤 , 过滤com.atguigu.anno包下的所有类customcom.atguigu.XxxTypeFilter使用XxxTypeFilter类通过编码的方式自定义过滤规则该类必须实现org.springframework.core.type.filter.TypeFilter接口

spring的配置文件

Type其他属性的测试



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3