环境搭建

注解的方式是通过配置类的方式来注入组件,注解注入要比XML注入的方式简单,注解注入也需要在前者的基础上,添加一个spring-context的包,也是实际开发中常用的方式。

准备所需Jar包

Spring注解之组件注册

Spring提供了许多的注解配置,这样我们就可以通过注解的方式实现组件的注册,下图就是Spring中经常使用到的注解。

@ComponentScan和@Configurable

原先xml的方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 要扫描的包 -->
<context:component-scan base-package="model"></context:component-scan>
</beans>

使用配置类

@Configurable来标注该类为Spring中的配置类,@ComponentScan(“model”)是为该配置类指定要去扫描的参数。

package config;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import model.Product; /**
* @Configurable: 该注解是标注该类是配置类
* @ComponentScan:配置要扫描的包
* @author GaoYang
*/
@Configurable
@ComponentScan("model")
public class MainConfig { }

@Component

使用该注解就可以将Java对象@Component注册到Ioc容器中,@Component注解要是给属性赋值要配合@Value注解为属性赋值。

/**
@Componnt可以指定该对象的id,也可以不用指定
默认id为该类的类名首字母小写
*/
@Component("students")
public class Student {
@Value("01")
private int sid;
@Value("侯宁宁")
private String name;
@Value("男")
private String sex;

配置类

/**
* @Configurable: 该注解是标注该类是配置类
* @ComponentScan:配置要扫描的包
* @author GaoYang
*/
@Configurable
@ComponentScan("model")
public class MainConfig { }

使用@Configuration注入

@Component("students")
public class Student {
@Value("01")
private int sid;
@Value("侯宁宁")
private String name;
@Value("男")
private String sex; public Student() {
super();
}
public Student(int sid, String name, String sex) {
super();
this.sid = sid;
this.name = name;
this.sex = sex;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", name=" + name + ", sex=" + sex + "]";
} }

测试

@Bean

使用@Bean注解该可以在我们的spring注册类里标注,创建对象的方法,可以通过一个返回值为该对象的方法去创建该对象,并通过构造器为该对象的属性进行赋值。

// 配置类
@Configurable
@ComponentScan("model")
public class MainConfig {
// 默认id为方法名
@Bean
public Product product1() {
return new Product("张三","hashd",1);
}
// 可以指定id
@Bean("product2")
public Product product2() {
return new Product("张三","hashd",1);
} }

Java-Bean对象

public class Product {
private String name;
private String price;
private int num;
public Product() {
super();
}
public Product(String name, String price, int num) {
super();
this.name = name;
this.price = price;
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "Product [name=" + name + ", price=" + price + ", num=" + num + "]";
} }

测试

@TypeFilter

@TypeFilter注解

是通过设置条件来过滤一些资源,我们可以过滤一些资源不让它加载到ioc容器中。它的使用要在@ComponentScan这个注解中国去使用,通过excludeFilters参数传值,excludeFilters是一个数组,可以设定多个@TypeFilter。

@TypeFilter语法

@Configurable
@ComponentScan(value = "model",excludeFilters = {
// FilterType.ANNOTATION是通过注解的形式进行过滤
@Filter(type = FilterType.ANNOTATION,classes = {Controller.class}), // FilterType.ASSIGNABLE_TYPE 是通过给定的类型
@Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Product.class}), // FilterType.ASPECTJ 根据正则表达式
@Filter(type = FilterType.ASPECTJ,classes = {""}), // FilterType.CUSTOM 使用自定义规则
@Filter(type = FilterType.CUSTOM,classes = {TypeFilterImp.class}) })
public class MainConfig {
// @Bean == <bean></bean> }

@FilterType.CUSTOM自定义规则

使用自定义规则,我们必须给它创建一个制定规则的类,这个类要去实现TypeFilter这个接口,并实现match这个方法,过滤器就会根据match方法的返回值加载,如果去ture就去过滤不满足条件的,如果为false则不会去加载!

/**
* MetadataReader: 读取到的当前正在扫描的信息
* MetadataReaderFactory:可以获取到其他任何类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解的信息
AnnotationMetadata mr = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类的资源信息
Resource resource = metadataReader.getResource();
// 获取当前类的名字
String className = classMetadata.getClassName();
System.out.println("----"+className);
// contains包含“er”
if(className.contains("er")) {
return true;
}
return false;
} }

@Scope

Spring创建对象默认是单例的,使用@Scope来描述也就是scope=“singleton”,另外scope还有prototype、request、session、global session作用域。

各作用域的的作用

  • singleton单例模式,全局有且仅有一个实例。(默认值)
  • prototype原型模式,每次获取Bean的时候会有一个新的实例。
  • request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例:

    request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:

    如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可:
<web-app>
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
</web-app>
  • session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
  • global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。

案例演示

singleton

@Configurable
@ComponentScan("model")
public class MainConfig {
/**
* @Scope
* prototype: 多实例的 @Scope("prototype")
* singleton: 单实例的 @Scope("person")
* request: 一次请求创建一个实例
* session: 同一个session创建一个实例
* @return
*/
@Scope("singleton")
@Bean
public Product product() {
System.out.println("该实例已被创建");
return new Product("张三","hashd",1);
}
}

测试代码

public class text {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println("Ioc容器已创建完成!");
Product bean1 = applicationContext.getBean(Product.class);
Product bean2 = applicationContext.getBean(Product.class);
System.out.println(bean1== bean2); }
}

从下图可以看到,bean1 == bean2

Layz-bean

@Layz赖加载主要是针对的是单例模式下,单例模式下ioc容器初始化时,就将bean对象注入到了容器中,@Layz注解可以让容器创建时不去注册容器,而是等到第一次调用时才去注册bean对象。此时,创建的对象依然是单例模式!

使用语法

// 配置类
@Configurable
@ComponentScan("model")
public class MainConfig {
/**
* 懒加载:
* 针对的是单实例的bean,默认在容器启动的时候创建对象
* 赖加载:容器启动时不创建对象,当第一次被调用时被创建
*
*/
@Lazy
@Bean
public Product product() {
System.out.println("该实例已被创建");
return new Product("张三","hashd",1);
}

测试

@Conditional

@Conditional注解是根据制定条件来进行注册,需要我创建配置条件的配置类,如果条件满足就进行注册,不满足就不去注册。

语法

配置类

@Configurable
public class MainConfig { @Conditional({winCondition.class})
@Bean("wind")
public Product wind() {
System.out.println("该实例已被创建");
return new Product("张三","wind",1);
}

条件类必须去实现Condition接口,并添加为实现的方法!

public class winCondition implements Condition{

	@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {
Environment environment = context.getEnvironment();
// 获取当前操作系统的名字
String property = environment.getProperty("os.name");
if(property.contains("Windows")) {
return true;
}
return false;
}
}

案例

需求根据当前操作系统去注册组件。

// 配置类
@Configurable
@Import(Hero.class)
public class MainConfig {
// Windows系统
@Conditional({winCondition.class})
@Bean("wind")
public Product wind() {
System.out.println("该实例已被创建");
return new Product("张三","wind",1);
}
// Linux系统
@Conditional({linuxCondition.class})
@Bean("linux")
public Product linux() {
return new Product("李四","linux",2); } }

条件配置类

public class winCondition implements Condition{
// Windows系统,返回true
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")) {
return true;
}
return false;
} }
public class linuxCondition implements Condition{
/**
* ConditionContext: 判断条件能使用上下文环境
* AnnotatedTypeMetadata: 注释信息
*/ @Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 是否Linux系统
// 1、能获取到ioc使用的bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2、获取类加载器
ClassLoader clLoader = context.getClassLoader();
// 3、获取当前环境信息
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name"); // 5、bean注册类
BeanDefinitionRegistry registry = context.getRegistry();
if(property.contains("Linux")) {
return true;
} return false;
}

测试…

@import

  • @Import只能用在类上 ,@Import通过快速导入的方式实现把实例加入spring的IOC容器中
  • 加入IOC容器的方式有很多种,@Import注解就相对很牛皮了,@Import注解可以用于导入第三方包 ,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷
  • @Import注解有三种用法

第一种用法:直接填class数组

直接填对应的class数组,class数组可以有0到多个。对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名 ,比如com.yc.类名

@Import({ 类名.class , 类名.class... })
public class TestDemo { }

第二种用法:ImportSelector方式【重点】

这种方式的前提就是一个类要实现ImportSelector接口,假如我要用这种方法,目标对象是Myclass这个类,分析具体如下:

创建Myclass类并实现ImportSelector接口

public class Myclass implements ImportSelector {
//既然是接口肯定要实现这个接口的方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[0];
}
}
// 分析实现接口的selectImports方法中的: // 1、返回值: 就是我们实际上要导入到容器中的组件全类名【重点 】
// 2、参数: AnnotationMetadata表示当前被@Import注解给标注的所有注解信息【不是重点】
// 需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常!

以上分析完毕之后,具体用法步骤如下:

第一步:创建Myclass类并实现ImportSelector接口,这里用于演示就添加一个全类名给其返回值

public class Myclass implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.yc.Test.TestDemo3"};
}
}

第二步:编写TestDemo 类,并标注上使用ImportSelector方式的Myclass类

@Import({TestDemo2.class,Myclass.class})
public class TestDemo {
@Bean
public AccountDao2 accountDao2(){
return new AccountDao2();
} }

第三步:编写打印容器中的组件测试类

**
* 打印容器中的组件测试
*/
public class AnnotationTestDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestDemo.class); //这里的参数代表要做操作的类 String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
} }
}

第三种用法:ImportBeanDefinitionRegistrar方式

同样是一个接口,类似于第二种ImportSelector用法,相似度80%,只不过这种用法比较自定义化注册,具体如下:

public class Myclass2 implements ImportBeanDefinitionRegistrar {
//该实现方法默认为空
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { }
}
// 参数分析: // 第一个参数:annotationMetadata 和之前的ImportSelector参数一样都是表示当前被@Import注解给标注的所有注解信息
// 第二个参数表示用于注册定义一个bean

第二步:编写代码,自定义注册bean

public class Myclass2 implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//指定bean定义信息(包括bean的类型、作用域...)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
//注册一个bean指定bean名字(id)
beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
}
}

第三步:编写TestDemo 类,并标注上使用ImportBeanDefinitionRegistrar方式的Myclass2类

@Import({TestDemo2.class,Myclass.class,Myclass2.class})
public class TestDemo { @Bean
public AccountDao2 accountDao222(){
return new AccountDao2();
} }

@FactoryBean

编写配置类

// 标记这是一个Spring配置类
@Configuration
public class SpringConfiguration {
// 如果没有@Bean注解,则注入到容器中的id就是方法名(也就是myFactoryBean),但是如果显示的给了值,那么注入到容器中的就是factoryBean
@Bean("factoryBean")
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
}

测试类

public class SpringDemo {

    @Test
public void springTest01() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// 容器中获取的Bean,实际上就是工厂Bean(MyFactoryBean通过getObject()方法返回的对象)
Object factoryBean01 = context.getBean("factoryBean");
System.out.println("实际上注入到容器中的类型是:" + factoryBean01.getClass()); Object factoryBean02 = context.getBean("factoryBean");
System.out.println("注入到容器内的对象是否是单例:" + (factoryBean01 == factoryBean02)); Object factoryBean03 = context.getBean("&factoryBean");
System.out.println("如果想获取到MyFactoryBean的对象,使用&前缀:" + factoryBean03); // 输出打印Spring中的所有Bean名称
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

【建议收藏】阿里P7总结的Spring注解笔记,把组件注册讲的明明白白的更多相关文章

  1. 【Spring注解开发】组件注册-使用@Configuration和@Bean给容器中注册组件

    写在前面 在之前的Spring版本中,我们只能通过写XML配置文件来定义我们的Bean,XML配置不仅繁琐,而且很容易出错,稍有不慎就会导致编写的应用程序各种报错,排查半天,发现是XML文件配置不对! ...

  2. Spring注解开发系列Ⅰ--- 组件注册(上)

    传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点:1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分开.xml文件 ...

  3. Spring注解开发系列Ⅱ --- 组件注册(下)

    1.@Import注册组件 @Import主要功能是通过导入的方式实现把实例加入springIOC容器中, /** * 给容器注册组件 * 1.包扫描+组件标注注解(@Controller,@Serv ...

  4. 2019阿里P7最新总结Spring Boot面试问题

        Spring Boot一直是Spring生态系统的关键参与者.该项目通过其自动配置功能使我们的生活更加轻松.在本教程中,我们将介绍在求职面试中可能出现的一些与Spring Boot相关的最常见 ...

  5. 浅尝Spring注解开发_自定义注册组件、属性赋值、自动装配

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含自定义扫描组件.自定义导入组件.手动注册组件.自动注入方法和参数.使用Spring容器底层组件等 配置 @Confi ...

  6. 【建议收藏】缺少 Vue3 和 Spring Boot 的实战项目经验?我这儿有啊!

    缺少 Vue3 和 Spring Boot 的实战项目经验?缺少学习项目和练手项目?我这儿有啊! 从 2019 年到 2021 年,空闲时间里陆陆续续做了一些开源项目,推荐给大家啊!记得点赞和收藏噢! ...

  7. 【Spring Cloud笔记】Eureka注册中心增加权限认证

    在Spring Cloud通过Eureka实现服务注册与发现时,默认提供web管理界面,但是如果在生产环境暴露出来,会存在安全问题.为了解决这个问题,我们可以通过添加权限认证进行控制,具体步骤如下: ...

  8. 浅尝Spring注解开发_AOP原理及完整过程分析(源码)

    浅尝Spring注解开发_AOP原理及完整过程分析(源码) 浅尝Spring注解开发,基于Spring 4.3.12 分析AOP执行过程及源码,包含AOP注解使用.AOP原理.分析Annotation ...

  9. 【spring 注解驱动开发】spring组件注册

    尚学堂spring 注解驱动开发学习笔记之 - 组件注册 组件注册 1.@Configuration&@Bean给容器中注册组件 2.@ComponentScan-自动扫描组件&指定扫 ...

随机推荐

  1. windows注册redis为服务,zookeeper为服务

    windows注册redis为服务,zkserver为服务 1.redis部分 通过redis内置工具安装 进入redis安装目录 1.shift+鼠标右键打开菜单,点击"在此处打开命令窗口 ...

  2. airtest操作夜神模拟器adb冲突解决办法

    首先看错误日志明显是adb之间版本不对应互相干掉对方 第一步 去aritestIde所在目录\AirtestIDE\airtest\core\android\static\adb\windows 第二 ...

  3. Angular双向绑定简单理解

    在使用Antd的时候,一直很好奇里面的双向绑定的自定义组件是怎么做的. 因为之前一直用,没有去细看文档. 今天抽空来简单的撸一下. 在ng中,()是单向数据流,从视图目标到数据源,[()]这样就是双向 ...

  4. 线上问题排查,一不小心踩到阿里的 arthas坑了

    最近帮新来的校招同学排查一个线上问题,问题本身不是很难,但是过程中踩到了一个arthas的坑,挺有意思的. 同时,也分享下在排查过程中使用的一些比较实用的工具,包括tcpdump.arthas.sim ...

  5. 使用 Filebeat 对多行日志进行处理(multiline)

    Filebeat 收集日志的过程中,默认是按行收取的,也就是每一行都会默认是一个单独的事件并添加时间戳.但是在收集一些特殊日志的时候,往往一个事件包含有多行,例如 Java 的堆栈跟踪日志: 20-0 ...

  6. 【论文阅读】An Empirical Study of Architectural Decay in Open-Source Software

    2020-06-19这篇文章是我学习 软件架构与中间件 课程时分享的论文.可以说,这篇文章塑造了我基本的科研观,也养成了我如今看论文的习惯.感谢老师们,也感谢恒恒对我的帮助. 论文地址: https: ...

  7. 【Postman】使用Postman实现接口数据关联

    首先下载安装Postman直接打开官网,点击下载按钮即可完成下载https://www.getpostman.com/downloads/ 栗子业务场景:用户登录医生账户,查询自己的处方列表数据:用户 ...

  8. 想学 iOS 开发高阶一点的东西,从何开始?

    前言 如果你正在学习 iOS, 或者正在从事IOS开发? 还是一个一个迷茫的待就业大学生,或是公司的到一个半老员工? 现在到了开发的一个阶段了,基本的东西很熟了,想着提高技术? 学习难一点的东西,不知 ...

  9. SSM使用Ueditor

    富文本编辑器(UEditor) 1. 下载UEditor富文本编辑器 建议下载 utf8-jsp 版本的,结构目录如下: 下载地址:链接:https://pan.baidu.com/s/1Nq0oJB ...

  10. C++在C的基础上改进了哪些细节

    C++ 是在C语言的基础上改进的,C语言的很多语法在 C++ 中依然广泛使用,例如:  C++ 仍然使用 char.short.int.long.float.double 等基本数据类型:   ...