文章目录
学习来源:黑马程序员SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术
📚注解开发依赖注入
Spring为了使用注解简化开发,并没有提供
构造函数注入
、setter注入
对应的注解,只提供了自动装配的注解实现。
🐇环境准备
创建一个Maven项目
pom.xml添加Spring的依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
添加一个配置类
SpringConfig
@Configuration @ComponentScan("com.itheima") public class SpringConfig { }
添加BookDao、BookDaoImpl、BookService、BookServiceImpl类
public interface BookDao { public void save(); } @Repository public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } } public interface BookService { public void save(); } @Service public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
创建运行类App
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); BookService bookService = ctx.getBean(BookService.class); bookService.save(); } }
环境准备好后,运行后会发现有问题
原因是,在BookServiceImpl类中添加了BookDao的属性,并提供了setter方法,但是目前是没有提供配置注入BookDao的,所以bookDao对象为Null,调用其save方法就会报
控指针异常
。
🐇注解实现按照类型注入
- 在BookServiceImpl类的bookDao属性上添加
@Autowired
注解。@Autowired
可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉。@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; // public void setBookDao(BookDao bookDao) { // this.bookDao = bookDao; // } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
@Autowired
是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2,这个时候再次运行App,就会报错。此时,按照类型注入就无法区分到底注入哪个对象。@Repository public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao save ...2"); } }
- 解决方案:按照名称注入。先给两个Dao类分别起个名称↓
@Repository("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } } @Repository("bookDao2") public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao save ...2" ); } }
- 此时就可以注入成功,但,
@Autowired
是按照类型注入的,给BookDao的两个实现起了名称,还是有两个bean对象,为什么不报错? - 因为
@Autowired
默认按照类型自动装配,如果IoC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫bookDao
而容器中也有一个bookDao
,所以可以成功注入。 - 对应地,下边这种情况就没法实现注入,因为按照类型会找到多个bean对象,此时会按照
bookDao
名称去找,而IoC容器只有名称叫bookDao1
和bookDao2
的,所以找不到,会报NoUniqueBeanDefinitionException
。
- 此时就可以注入成功,但,
🐇注解实现按照名称注入
当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候就需要使用到
@Qualifier
来指定注入哪个名称的bean对象。@Service public class BookServiceImpl implements BookService { @Autowired @Qualifier("bookDao1") private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
@Qualifier
注解后的值就是需要注入的bean的名称。@Qualifier
不能独立使用,必须和@Autowired
一起使用。
🐇简单数据类型注入
简单类型注入的是基本数据类型或者字符串类型,下面在
BookDaoImpl
类中添加一个name
属性,用其进行简单类型注入,并使用@Value
注解,将值写入注解的参数中。@Repository("bookDao") public class BookDaoImpl implements BookDao { @Value("itheima") private String name; public void save() { System.out.println("book dao save ..." + name); } }
注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。
🔥这个注解似乎跟直接赋值是一个效果,存在的意义是什么?
@Value
一般会被用在从properties配置文件中读取内容,具体实现如下↓步骤1:resource下准备properties文件(jdbc.properties)。
name=itheima888
步骤2:使用注解加载properties配置文件,在配置类上添加
@PropertySource
注解。@Configuration @ComponentScan("com.itheima") @PropertySource("jdbc.properties") public class SpringConfig { }
步骤3:使用
@Value
读取配置文件中的内容。@Repository("bookDao") public class BookDaoImpl implements BookDao { @Value("${name}") private String name; public void save() { System.out.println("book dao save ..." + name); } }
步骤4:运行App类,查看运行结果,说明配置文件中的内容已经被加载到。
注意:
如果读取的properties配置文件有多个,可以使用
@PropertySource
的属性来指定多个@PropertySource({"jdbc.properties","xxx.properties"})
@PropertySource
注解属性中不支持使用通配符*
,运行会报错@PropertySource({"*.properties"})
@PropertySource
注解属性中可以把classpath:
加上,代表从当前项目的根路径找文件@PropertySource({"classpath:jdbc.properties"})
📚IoC/DI注解开发管理第三方bean
之前定义bean的时候都是在自己开发的类上面写个注解就好,但如果是第三方类,这些类都是在jar包中,没办法在类上面添加注解,这个时候就用到了一个全新的注解
@Bean
。
🐇环境准备
- 创建一个Maven项目。
- pom.xml添加Spring的依赖。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
- 添加一个配置类
SpringConfig
。@Configuration public class SpringConfig { }
- 添加BookDao、BookDaoImpl类。
public interface BookDao { public void save(); } @Repository public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } }
- 创建运行类App。
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); } }
🐇注解开发管理第三方bean
需求:在上述环境中完成对
Druid
数据源的管理。
步骤1:导入对应的jar包。
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
步骤2:在配置类中添加一个方法。该方法的返回值就是要创建的Bean对象类型。
@Configuration public class SpringConfig { 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; } }
步骤3:在方法上添加
@Bean
注解。@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象。@Configuration public class SpringConfig { @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; } }
不能使用
DataSource ds = new DruidDataSource()
,因为DataSource接口中没有对应的setter方法来设置属性。步骤4:从IoC容器中获取对象并打印。
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); DataSource dataSource = ctx.getBean(DataSource.class); System.out.println(dataSource); } }
如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加@Bean注解即可。
🐇引入外部配置类
把所有的第三方bean都配置到Spring的配置类
SpringConfig
中,虽然可以但不利于代码阅读和分类管理。对于数据源的bean,我们新建一个
JdbcConfig
配置类,并把数据源配置到该类下。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:在Spring的配置类上添加包扫描。
@Configuration @ComponentScan("com.itheima.config") public class SpringConfig { }
- 步骤2:在JdbcConfig上添加配置注解。JdbcConfig类要放入到
com.itheima.config
包下,需要被Spring的配置类扫描到即可。@Configuration 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; } }
- 步骤3:运行程序。这种方式虽然能够扫描到,但是不能很快的知晓都引入了哪些配置类,所以这种方式不推荐使用。
- 步骤1:在Spring的配置类上添加包扫描。
【法二】使用@Import引入(推荐)
步骤1:去除JdbcConfig类上的注解。
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; } }
步骤2:在Spring配置类中引入。
@Configuration //@ComponentScan("com.itheima.config") @Import({JdbcConfig.class}) public class SpringConfig { }
- 扫描注解可以移除。
@Import
参数需要的是一个数组,可以引入多个配置类。@Import
注解在配置类中只能写一次,下面的方式是不允许的。@Configuration //@ComponentScan("com.itheima.config") @Import(JdbcConfig.class) @Import(Xxx.class) public class SpringConfig { }
步骤3:运行程序。
🐇注解开发实现为第三方bean注入资源
资源类型一:简单数据类型
对应需求:对于下面代码关于数据库的四要素不应该写死在代码中,应该是从properties配置文件中读取。
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:类中提供四个属性。
public class JdbcConfig { private String driver; private String url; private String userName; private String password; @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; } }
步骤2:使用
@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.itheima.dao") @Import({JdbcConfig.class}) public class SpringConfig { }
步骤2:在JdbcConfig类的方法上添加参数。引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。
@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; }
步骤3:运行程序。
📚小结
知识点1:@Autowired
名称 @Autowired 类型 属性注解 或 方法注解(了解) 或 方法形参注解(了解) 位置 属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面 作用 为引用类型属性设置值 属性 required:true/false,定义该属性是否允许为null 知识点2:@Qualifier
名称 @Qualifier 类型 属性注解 或 方法注解(了解) 位置 属性定义上方 或 标准set方法上方 或 类set方法上方 作用 为引用类型属性指定注入的beanId 属性 value(默认):设置注入的beanId 知识点3:@Value
名称 @Value 类型 属性注解 或 方法注解(了解) 位置 属性定义上方 或 标准set方法上方 或 类set方法上方 作用 为 基本数据类型 或 字符串类型 属性设置值 属性 value(默认):要注入的属性值 知识点4:@PropertySource
名称 @PropertySource 类型 类注解 位置 类定义上方 作用 加载properties文件中的属性值 属性 value(默认):设置加载的properties文件对应的文件名或文件名组成的数组 知识点5:@Bean
名称 @Bean 类型 方法注解 位置 方法定义上方 作用 设置该方法的返回值作为spring管理的bean 属性 value(默认):定义bean的id 知识点2:@Import
名称 @Import 类型 类注解 位置 类定义上方 作用 导入配置类 属性 value(默认):定义导入的配置类类名,
当配置类有多个时使用数组格式一次性导入多个配置类