定义bean
我们先直接通过一张图来理解注解在Spring开发中如和定义bean:
那么我们既然加了注解,相当于定义了bean可是Spring的配置文件怎么知道他的存在,于是我们引入component-scan进行包扫描即为了让Spring框架能够扫描到写在类上的注解,我们必需要在配置文件上进行包扫描
<context:component-scan base-package="。。。"/
component-scan
- component:组件,Spring将管理的bean视作自己的一个组件
- scan:扫描
base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
- 包路径越多[如:com.taro.dao.impl],扫描的范围越小速度越快
- 包路径越少[如:com.taro],扫描的范围越大速度越慢
- 一般扫描到项目的组织名称即Maven的groupId下[如:com.taro]即可。
@Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取
对于@Component注解,还衍生出了其他三个注
@Controller : 表现层、@Service :业务层、@Repository :数据层来定义bean。
纯注解开发
顾名思义架空配置文件(建一个配置类),把权力掌握在我们自己手里,一张图理解一下
-@Configuration注解用于设定当前类为配置类
-@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式(用花括号把路径加起来)
可是问题来了主程序怎么知道配置在哪捏?
- 只需要重新建一个启动类加载配置类就好啦
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
我们注意一下下面这些小点哦
- 记住@Component、@Controller、@Service、@Repository这四个注解
- applicationContext.xml中< context:component-san />的作用是指定扫描包路径,注解为@ComponentScan
- @Configuration标识该类为配置类,使用类替换applicationContext.xml文件
- ClassPathXmlApplicationContext是加载XML配置文件
- AnnotationConfigApplicationContext是加载配置类
注解开发时bean的生命周期控制
众所周知我们的Spring在创建bean时默认都是单例的,这个我们在之前的文章也进行过演示,当我们需要在注解开发时,使bean的创建变成非单例,只需要在其类上添加@Scope(“prototype”)注解
当然我们之前也在配置文件中提到过init-method等属性来定义初始化方法与销毁方法,那么我们现在通过注解开发,没有了配置文件该怎么解决?
- @PostConstruct //在构造方法之后执行,替换 init-method
- @PreDestroy //在销毁方法之前执行(容器关闭时执行),替换 destroy-method
@PostConstruct和@PreDestroy注解如果找不到,需要导入javax.annotation-api这个jar包,因为jdk9版本开始删掉了这个包
<dependency> <groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
我们小小通过一张图总结下bean的注解:
依赖注入
Spring为了使用注解简化开发,并没有提供构造函数注入、setter注入对应的注解,只提供了自动装配的注解实现。
自动装配
@Autowired(他是按类型装配的);如果类型相同使用@Qualifirt(“名称”),来制定名称注入
- @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将 setter方法删除掉,因为自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值;普通反射只能获取public修饰的内容,暴力反射除了获取public修饰的内容还可以获取private修改的内容所以此处无需提供setter方法
- @Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫bookDao而容器中也有一个booDao,所以可以成功注入。
🍊个🌰:
这样呢就会报错,因为按照类型会找到多个bean对象,此时会按照bookDao名称去找,因为IOC容器只有名称叫bookDao1和bookDao2 ,所以找不到,会报NoUniqueBeanDefinitionException
指定名称注入
@Qualifier
明明就这一个为啥还要单独放一个标题捏?
@Qualifier注解后的值就是需要注入的bean的名称。
- 注意:@Qualifier不能独立使用,必须和@Autowired一起使用
🍊个🌰:
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
当然我们现在提到的都是应用类型的注入那么。。。。
简单类型的注入
@Value(“…”)
🍊个🌰:
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("taro")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
很明显明明加在name直接给name赋值更方便,那为什么还要专门注入捏?有没有一种可能,这个值还可以来自配置文件。
注解读取properties配置文件
主要也就是针对@Value
1、准备配置文件
2、在配置类使用注解加载properties配置文件(@PropertySource)
3、使用@Value读取配置文件中的内容
🍊个🌰:
1、准备配置文件
我们准备一个名为jdbc.properties的配置文件并添加“name=taro”
2、使用注解加载properties配置文件(@PropertySource)
@Configuration
@ComponentScan("com.itheima")
//@PropertySource加载properties配置文件
@PropertySource({"jdbc.properties"})
public class SpringConfig {
}
3、使用@Value读取配置文件中的内容
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
//@Value:注入简单类型(无需提供set方法)
@Value("${name}")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
-
如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个
🍊个🌰:
@PropertySource({"jdbc.properties","xxx.properties"})
-
@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件
-
🍊个🌰:
@PropertySource({"classpath:jdbc.properties"})
稍微总结一下注入的注解吧:
@Autowired
名称 | @Autowired |
---|---|
类型 | 属性注解 或 方法注解(了解) 或 方法形参注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面 |
作用 | 为引用类型属性设置值 |
属性 | required:true/false,定义该属性是否允许为null |
@Qualifier
名称 | @Qualifier |
---|---|
类型 | 属性注解 或 方法注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
作用 | 为引用类型属性指定注入的beanId |
属性 | value(默认):设置注入的beanId |
@Value
名称 | @Value |
---|---|
类型 | 属性注解 或 方法注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
作用 | 为 基本数据类型 或 字符串类型 属性设置值 |
属性 | value(默认):要注入的属性值 |
@PropertySource
名称 | @PropertySource |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 加载properties文件中的属性值 |
属性 | value(默认):设置加载的properties文件对应的文件名或文件名组成的数组 |
注解开发管理第三方bean
@Bean
名称 | @Bean |
---|---|
类型 | 方法注解 |
位置 | 方法定义上方 |
作用 | 设置该方法的返回值作为spring管理的bean |
属性 | value(默认):定义bean的id |
@Import
名称 | @Import |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 导入配置类 |
属性 | value(默认):定义导入的配置类类名, 当配置类有多个时使用数组格式一次性导入多个配置类 |
嗯。。。其实就这俩
值得注意的是@Import 只能写一次,需要导入多个就跟在后面用数组格式
注解开发实现为第三方bean注入资源
提个问题:在使用@Bean创建bean对象的时候,如果这个方法在创建的过程中需要其他资源捏?我们怎么把其他资源给他捏?
公布答案:注入咯,所以既然是注入,那么一样的,也就分两种简单类型和引用类型咯。
简单类型
其实看一下这个🌰就明白了:
使用@Value
注解引入值
public class JdbcConfig {
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("password")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
引用类型
提出需求:假设在构建DataSource对象的时候,需要用到BookDao对象,该如何把BookDao对象注入进方法内让其使用呢?
如下图所示:
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
解决方案:
🍊个🌰:
1、在SpringConfig中扫描BookDao;扫描的目的是让Spring能管理到BookDao,也就是说要让IOC容器中有一个bookDao对象
@Configuration
@ComponentScan("com.taro.dao")
@Import({JdbcConfig.class})
public class SpringConfig {
}
2、在JdbcConfig类的方法上添加参数
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。
最后对Spring注解开发做个总结吧: