【Spring 基础注解】基础注解、高级注解、配置Bean

参考自B站视频《孙哥说Spring5》

注解基础概念

什么是注解编程?

或者方法上加入特定的注解 @Xxx 以完成特定功能的开发

@Component
public class XXX{}

为什么要使用注解编程?

  • 注解开发方便,代码简单,开发速度大大提高
  • 注解开发是 Spring 的开发潮流
Spring 2.x 引入注解 -> Spring 3.x 完善注解 -> Spring 4.x 出现 SpringBoot 开始普及、推广注解编程

注解的作用

  • 注解能够简化开发,最突出的就是替代了XML这种配置形式,简化配置

在这里插入图片描述

  • 还可以替换接口,实现调用双方的契约性

  • 接口可以让方法的调用者可以调用规定的方法,只需要提供者实现该接口,相当于是一种规范
    在这里插入图片描述

  • 但是接口一旦实现,就要实现接口下所有的方法,还是比较繁琐

  • 就可以通过注解的方式,在功能提供者和功能调用者之间达成契约,进而进行功能的调用。

  • 因为注解更为灵活方便,所以在现在的开发中,更推荐通过注解的方式完成

在这里插入图片描述

  • 例如之前学过的 注解方式实现AOP

在这里插入图片描述

注解的发展历程

Spring 2.x: 开始支持注解编程, 提供了 @Component、@Service、@Scope…

  • 目的:提供的这些注解只是为了简化某些 XML 的配置,作为 XML 开发的有益补充。

Spring 3.x: 完善注解,旨在替换XML开发 出现了:@Configuration、@Bean…

  • 目的:彻底替换 XML,基于纯注解

Spring 4.x: 衍生出 SpringBoot ,开始普及、提倡使用注解进行开发

思考:Spring 基于注解进行配置后,注解写在了代码中,如果需要修改,只能在代码中修改,那Spring还能否解耦合呢?

在 Spring 框架应用注解时,如果对注解配置的内容不满意,可以通过 Spring 配置文件覆盖

Spring 基础注解

这个阶段的注解,仅仅是简化 XML 的配置,并不能完全替代 XML。

对象创建相关注解

@Component

替换原有 Spring 配置文件中的 <bean> 标签

  • id 属性:在 @Component 中提供了默认的设置方式,首字母小写(UserDAO --> userDAO
  • class 属性:可以通过反射获得 的类的全限定名

在这里插入图片描述

  • 如何显式指定工厂创建对象的 id 值 :@Component("u")

  • Spring 配置文件覆盖注解配置内容

在 applicationContext.xml 文件中
<bean id="user" class="com.yusael.bean.User">
	<property name="id" value="10"/>
</bean>
id值、class值 要和 注解 中配置的一样才会覆盖, 
否则 Spring 会创建新的对象。

@Repository、@Service、@Contoller

@Repository@Service@Contoller 都是 @Component衍生注解

本质上这些衍生注解就是 @Component,通过源码可以看见他们都使用了 @Component

在这里插入图片描述

它们的存在是为了:更加准确的表达一个类型的作用

@Repository
public class UserDAO {}

@Service
public class UserService {}

@Controller
public class UserController {}
  • 注意:Spring 整合 Mybatis 开发过程中,不使用 @Repository@Component

@Scope

作用:控制简单对象创建次数singleton 或者 prototype

注意:不添加 @Scope,Spring 提供默认值 singleton

在原先的XML配置中:

<bean id="customer" class="com.yusael.Customer" scope="singleton / prototype"/>

注解简化之后:

创建单例对象
@Component
@Scope("singleton")
public class Customer {}

创建多例对象 通过原型模式创建
@Component
@Scope("prototype")
public class Customer {}

@Lazy

作用:延迟创建单实例对象,即懒加载

注意:一旦使用 @Lazy 注解后,Spring 会在使用这个对象的时候,再创建这个对象。

XML 配置:

<bean id="account" class="com.yusael.Account" lazy="true"/>

注解简化之后:

@Component
@Lazy
public class Account {
    public Account() {
        System.out.println("Account.Account");
    }
}

生命周期注解 @PostConstruct、@PreDestroy

初始化相关方法: @PostConstruct

InitializingBean
<bean init-method=""/>

销毁方法:@PreDestory

DisposableBean
<bean destory-method=""/>

注意:

  • 上述的两个注解并不是 Spring 提供的,由 JSR(JavaEE规范)520 提供
  • 再次验证,通过注解实现了接口的契约性(原来初始化方法要实现接口,现在只需要添加注解即可)

注入相关注解

用户自定义类型 @Autowired

使用效果:

在这里插入图片描述

细节分析

@Autowired 注解 基于类型 进行注入 [推荐]

  • 注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
@Autowired
private UserDAO userDAO;

@Autowired@Qualifier 注解联合实现 基于名字 进行注入 [了解]

  • 注入对象的 id 值,必须与 @Qualifier 注解中设置的名字相同,名字默认是类名的首字母小写
@Autowired
@Qualifier("userDAOImpl")
private UserDAO userDAO;

@Autowired 注解放置位置

  • 放置在对应成员变量的 setter 方法上,调用 setter 方法赋值
  • 直接放置在成员变量上,Spring 通过反射直接对成员变量进行赋值 [推荐]

JavaEE 规范中类似功能的注解:

  • JSR250 提供的 @Resource(name="userDAOImpl") 基于名字进行注入,
    等价于 @Autowired@Qualifier 联合实现的效果。
    注意:@Resource 注解如果名字没有配对成功或者没有写名字,会继续 按照类型 进行注入。
  • JSR330 提供的 @Injection 作用与 @Autowired 完全一样,一般用在 EJB3.0 中

JDK类型注入 @Value

之前的开发中,我们可以在XML文件中为属性注入值,代码会比较繁杂冗余

可以采用 properties 配置文件,在 Spring的配置文件 中引入该文件,就可以通过@Value注解引入该值

在这里插入图片描述

开发步骤

  1. 创建 xxxx.properties 文件
  2. 在该文件中设置值
  3. Spring 工厂中读取这个配置文件(applicationContext.xml 文件中引入)
  4. 代码中通过 @Value 注解导入属性

优化

首先,我们可以通过 @PropertySource 替换 在 Spring 的配置文件中的引入的<context:property-placeholder location=""/>

在类上添加 @PropertySource 文件指定一个配置文件

在这里插入图片描述

  • 拓展,导入 properties 文件还可以通过Bean的配置方式(XML配置文件和配置Bean)

XML

在这里插入图片描述

配置Bean

在这里插入图片描述

  • 底层就是通过 PropertySourcesPlaceholderConfigurer 类来完成

在使用过程中的细节

  • @Value 注解不可以用在静态成员变量(类变量)上,如果应用了该注解,那么赋值会失败

在这里插入图片描述

  • @Value 注解 + Properties文件 这种方式,不能注入集合类型
  • 所以 Spring 提供了新的配置形式 :YAML / YML (在SpringBoot中一种非常重要的配置形式)

在这里插入图片描述

Spring 高级注解

Spring 的高级注解是从 Spring 3.x 及以上后才开始投入使用的

@Configuration

加上了 @Configuration 后,该类就成为了一个配置Bean

@Configuration 在应用的过程中替换了XML具体内容?

  • 首先是可以替代XML文件进行配置
    在这里插入图片描述

  • 其次是工厂的创建方式发生了变化

在这里插入图片描述

@Configuration 注解的本质

本质其实也是 @Component 的衍生注解,可以查看一下源代码

在这里插入图片描述

@Bean

@Bean 注解在配置Bean中进行使用,等同于XML配置文件中的 <bean> 标签

对于对象,可以分为两类:简单对象(直接通过new的方式可以创建的对象)和复杂对象(不能通过new的方式可以创建的对象)

在这里插入图片描述

  • 不管是简单对象还是复杂对象,直接将创建对象的代码写在方法体内,然后返回即可

@Bean 创建复杂对象的注意事项

一般是通过一个类创建,然后在配置Bean中通过 @Bean 注解创建

先实现接口
在这里插入图片描述

在配置Bean中通过类实现的接口创建对象

在这里插入图片描述

  • 一般仍是直接在方法体内写创建的方法
  • 对于另外实现类来创建的方式一般是整合遗留系统

@Bean 注解自定义 id 值

@Bean("id")

@Bean 控制对象的创建次数

创建次数即之前我们使用的 @Scope 的 singletonprototype

在这里插入图片描述


@Bean 注解的注入

注入类型可以分为两类:用户自定义类型和JDK类型的注入

对于用户自定义类型的注入:

在这里插入图片描述
对于JDK类型的注入:

在这里插入图片描述


对于JDK类型注入的优化:解耦合,尽量不把值放在代码中

为了让代码解耦合,我们也可以采用 properties 文件和 @Value 注解搭配开发的方式完成值的注入

在这里插入图片描述

在这里插入图片描述

@ComponentScan

在XML中扫描包的配置:

扫描当前包及其子包
<context:component-scan base-package="..."/> 

但是该扫描方式是较为粗放的,不够灵活,不能面对更为复杂的需求

  • 例如该包下我不想扫描一些类

那该怎么做呢?

  • Spring 提供了更为细粒度的两种方式

排除方式

<context:component-scan base-package="..."/> 
	配置排除的类型和表达式
	<context:exclude-filter type="" expression=""/>
	提供了五种类型
	type : assignable  排除特定的类型 不进行扫描 只能一个
		   annotation  排除特定的注解 不进行扫描 只能一个
		   aspectj     切入点表达式完成 只能用 包、类的切入点表达式 较为常用
		   regex       正则表达式完成
		   custom      自定义排除策略 框架底层开发中较为常用
</context:component-scan>
  • 排除策略是可以叠加使用的,多个排除策略可以同时生效

包含方式

与排除相反,可以选择只扫描部分的类;当需要排除的比需要扫描的多的时候,可以采用此方法

use-default-filters="false" -> 关闭Spring默认的注解扫描方式 默认是全扫描
<context:component-scan base-package="..." use-default-filters="false"/> 
	指定扫描哪些注解
	<context:include-filter type="" expression=""/>
	也是五种类型
	type : assignable  只扫描特定的类型 只能一个
		   annotation  只扫描特定的注解 只能一个
		   aspectj     切入点表达式完成 只能用 包、类的切入点表达式 较为常用
		   regex       正则表达式完成
		   custom      自定义扫描策略
</context:component-scan>
  • 包含的方式也是支持叠加的

对于使用 @Component 注解来扫描一个包及其子包

  • 用于扫描某个包下的所有添加了 @Component 注解 及其 衍生注解 的类

在这里插入图片描述


@ComponentScan 注解排除策略

在这里插入图片描述

同样的,排除策略也是可以叠加的

在这里插入图片描述

对应的,也有五种类型

type = FilterType.ANNOTATION       value
                 .ASSIGNABLE_TYPE  value
                 .ASPECTJ          pattern
                 .REGEX            pattern
                 .CUSTOM           value

@ComponentScan 注解包含策略

在这里插入图片描述

同样的也是可以叠加的,而且也是有五种类型

type = FilterType.ANNOTATION       value
                 .ASSIGNABLE_TYPE  value
                 .ASPECTJ          pattern
                 .REGEX            pattern
                 .CUSTOM           value

对于注解开发的思考

配置互通

  • 对于 XML 配置文件和注解两种方式是可以互通的
  • 例如XML中可以引用注解配置的Bean
  • 不过现在更推崇少用或不用XML配置文件
  • 在SpringBoot中是更推荐 无XML配置文件 配置方式

什么情况下使用注解开发?什么情况下使用配置文件开发?

  • 例如 @Component 可以完全替换 <bean> 标签吗?显然是不可以的
  • 像这些基础注解(@Component @Autowired @Value只可以用于程序员开发类型的配置,也就是我们自己写的代码
  • 在目前的开发过程中,还有一些类不是我们写的,例如 Mybatis 中的 SqlSessionFactoryBean 这些类就不能够为之添加注解,那就只能使用 <bean> 标签了
  • 总结:在程序员开发的类型上,可以加入对应的注解进行对象的创建;应用其他非程序员开发的类型时,还是需要使用 <bean> 标签进行配置的

Spring工厂创建对象的多种配置方式

Spring工厂创建多项有三种配置方式:

  • 在类上添加 @Component 注解
  • 在配置Bean中使用 @Bean 注解
  • 在XML配置文件中通过 bean 标签配置

应用场景

对于 @Component注解 及其 衍生注解(包括 @Autowired):

  • 建议使用在程序员自己开发的类型上,可以在代码中添加注解
  • 例如 Service DAO Controller 层中自己开发的类

对于在 配置Bean 中使用 @Bean 注解:

  • 建议应用于一些没有源码的场景,无法直接在代码中添加 @Component 注解
  • 例如:框架提供的类型,别的程序员开发的类型
  • 例如 SqlSessionFactoryBeanMapperScannerConfigure

对于在 XML配置文件 中的 bean 标签:

  • 对于纯注解的开发过程中,是基本不会使用到的
  • 对于一些遗留系统或比较老的系统整合中,因为可能还没有应用到注解,所以还要使用XML配置文件

除此之外,Spring还提供了基于 @Import 注解的方式:

在这里插入图片描述

  • 一般是在Spring框架的底层中使用,在一般的开发中使用并不灵活
  • 也会应用在多配置Bean的整合中

优先级

对于多种配置方式,他们对应的优先级也是不一样的

@Component及其衍生注解  <  @Bean  <  配置文件的Bean标签
  • 优先级高的配置方式会覆盖优先级低的配置方式
  • 但是前提是 id值 要保持一致

对于注解在代码中的耦合问题

  • 基于注解配置需要在源代码中添加注解
  • 那这样修改的时候需要直接在代码中进行修改
  • 耦合性较大
  • 可以直接通过XML配置文件进行覆盖操作

整合多个配置信息

在一般的项目开发中,只是用一个配置文件,会导致该配置文件或配置类非常长

因此可以进行拆分,然后整合在一起,这样能降低维护的难度

  • 类似于模块化编程的思想

多个配置Bean的整合

多配置的信息汇总

  • 整合到一个配置类中(XML中通过 import 标签;注解中通过 @Import)

在这里插入图片描述

  • 直接通过包扫描的方式创建(扫描添加了 @Configuration 注解的类)

在这里插入图片描述

  • 还可以在创建工厂的时候指定多个配置Bean的Class对象
一般不会使用,因为当配置类多的时候要一个个导入,不仅麻烦,而且代码耦合度高
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class, AppConfig2.class)

跨配置进行注入

  • 当遇到不同配置Bean中的对象互相调用的时候,只需要在调用之前在该配置Bean中注入该对象即可
  • 只要配置Bean生效之后,就可以通过该方法调用,不管配置Bean是怎么整合的

在这里插入图片描述


配置Bean与 @Component 及其衍生注解的整合

首先有一个类添加了 @Component注解
@Component
// 或者使用 衍生注解 例如 @Repository
public class UserDaoImpl implements UserDao{
	...
}


然后在配置Bean上加上注解的扫描
@Configuration
@ComponentScan(basePackages = "com.edu.dao")   // 扫描该包下的类 若发现 @Component注解 会自动创建该对象
public class Appconfig{

	@Autowired    // 自动注入后就可以使用了
	private UserDao userDao;
	@Bean
	public UserService userService(){
		UserServiceImpl userService = new UserServieImpl();
		userService.setUserDao(userDao);  // 使用
		return userService;
}

创建工厂时指定配置Bean
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)

配置Bean与配置文件的整合

  • 与配置文件的整合,主要应用于 配置覆盖 以及 整合遗留系统
  • 当然,主流还是更推荐使用无XML配置文件的开发方式
  • 只要在配置Bean上添加 @ImportResource("applicationContext.xml") 注解,导入配置文件即可
  • 其实和 @Import 是一个道理,只是Java配置Bean 换成了 XML配置文件,使用 @ImportResource 注解而已

在这里插入图片描述

配置Bean的底层实现原理

  • 对于我们程序员而言,我们主要实现的是创建对象的核心代码
  • 而对于Spring而言,主要负责创建对象,也就是通过 Spring工厂去创建对象,其中主要就是控制对象的创建次数(单例或多例)
  • 对于开发者来说,控制创建的次数就是Spring提供的额外功能,而Spring最核心的就是IOC和AOP
  • 因此我们可以先大胆猜测,控制对象的创建次数是通过AOP代理来实现的(为创建对象这个原始方法添加控制创建次数的额外功能)
  • 对于AOP,有两种实现方式:JDK 和 CGLib,那么对于配置Bean使用的是哪种方式呢?
  • 我们可以调试一下

在这里插入图片描述

  • 很明显,Spring 的配置Bean 是通过 CGLib 动态代理创建的代理类

【总结】:Spring 在配置Bean中加入了 @Configuration 注解后,希曾就会通过 Cglib 的代理方式,来进行对象相关的配置

思维导图总结

在这里插入图片描述

  • 下载地址
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值