我们先直接通过一张图来理解注解在Spring开发中如和定义bean:
那么我们既然加了注解,相当于定义了bean可是Spring的配置文件怎么知道他的存在,于是我们引入component-scan进行包扫描即为了让Spring框架能够扫描到写在类上的注解,我们必需要在配置文件上进行包扫描
component-scan
- component:组件,Spring将管理的bean视作自己的一个组件
- scan:扫描
base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
@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是加载配置类
众所周知我们的Spring在创建bean时默认都是单例的,这个我们在之前的文章也进行过演示,当我们需要在注解开发时,使bean的创建变成非单例,只需要在其类上添加@Scope(“prototype”)注解
当然我们之前也在配置文件中提到过init-method等属性来定义初始化方法与销毁方法,那么我们现在通过注解开发,没有了配置文件该怎么解决?
@PostConstruct和@PreDestroy注解如果找不到,需要导入javax.annotation-api这个jar包,因为jdk9版本开始删掉了这个包
<dependency> <groupId>javax.annotationgroupId>
<artifactId>javax.annotation-apiartifactId>
<version>1.3.2version>
dependency>
Spring为了使用注解简化开发,并没有提供构造函数注入、setter注入对应的注解,只提供了自动装配的注解实现。
@Autowired(他是按类型装配的);如果类型相同使用@Qualifirt(“名称”),来制定名称注入
- @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将 setter方法删除掉,因为自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值;普通反射只能获取public修饰的内容,暴力反射除了获取public修饰的内容还可以获取private修改的内容所以此处无需提供setter方法
@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赋值更方便,那为什么还要专门注入捏?有没有一种可能,这个值还可以来自配置文件。
主要也就是针对@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 |
---|---|
类型 | 方法注解 |
位置 | 方法定义上方 |
作用 | 设置该方法的返回值作为spring管理的bean |
属性 | value(默认):定义bean的id |
@Import
名称 | @Import |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 导入配置类 |
属性 | value(默认):定义导入的配置类类名, 当配置类有多个时使用数组格式一次性导入多个配置类 |
嗯。。。其实就这俩
值得注意的是@Import 只能写一次,需要导入多个就跟在后面用数组格式
提个问题:在使用@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定义方法设置形参即可,容器会根据类型自动装配对象。