@Autowired
- 使用
@Autowired
的这个注解来进行自动装配 - Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
@Autowired
:自动注入,默认优先按照类型去容器中去找对应的组件:applicationContext.getBean(BookDao.class);如果找到了则进行赋值;如果找到了多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean(“bookDao”)
//在IOC容器里面默认就是类名的首字母小写
@Repository
public class BookDao {
private String lable = "1";
public String getLable() {
return lable;
}
public void setLable(String lable) {
this.lable = lable;
}
@Override
public String toString() {
return "BookDao{" +
"lable='" + lable + '\'' +
'}';
}
}
@Service
public class BookService {
@Autowired
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
/**
* 自动装配:
* Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
* 1) @Autowired:自动注入
* (1)默认优先按照类型去容器中去找对应的组件:applicationContext.getBean(BookDao.class);如果找到了则进行赋值;
* public class BookService {
* @Autowired
* BookDao bookDao;
* }
*/
@Configuration
@ComponentScan({"com.ldc.service","com.ldc.dao","com.ldc.controller"})
public class MainConfigOfAutowired {
@Bean("bookDao2")
public BookDao bookDao() {
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
@Test
public void test01() {
//1.创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
BookService bookService = applicationContext.getBean(BookService.class);
System.out.println(bookService);
}
BookService{bookDao=BookDao{lable=‘2’}}
方法上
@Autowired
注解标注在方法上:用的最多的方式就是在@Bean
注解标注的方法的参数,这个参数就是会从容器中获取,在这个方法的参数前面可以加上@Autowired
注解,也可以省略,默认是不写@Autowired
,都能自动装配
@Component
public class Boss {
private Car car;
public Car getCar() {
return car;
}
@Autowired //标注在方法上,Spring容器在创建当前对象的时候,就会调用当前方法完成赋值;
//方法使用的参数,自定义类型的值从IOC容器里面进行获取
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Boss{" +
"car=" + car +
'}';
}
}
@Test
public void test01() {
//1.创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = applicationContext.getBean(Boss.class);
Car car = applicationContext.getBean(Car.class);
System.out.println(car);
System.out.println(boss);
}
com.ldc.bean.Car@69930714
Boss{car=com.ldc.bean.Car@69930714}
构造器上
@Autowired
注解标注在构造器上,默认加在IOC容器中的组件,容器启动的时候会调用无参构造器创建对象,再进行初始化赋值操作,构造器要用的组件,也都是从容器中来获取:- 注意:如果组件只有一个有参的构造器,这个有参的构造器的
@Autowired
注解可以省略,参数位置的组件还是可以自动从容器中获取
//默认加在IOC容器中的组件,容器启动的时候会调用无参构造器创建对象,再进行初始化赋值操作
@Component
public class Boss {
private Car car;
//构造器要用的组件,也都是从容器中来获取
@Autowired
public Boss(Car car) {
this.car = car;
System.out.println("Boss的有参构造器"+car);
}
public Car getCar() {
return car;
}
//@Autowired //标注在方法上,Spring容器在创建当前对象的时候,就会调用当前方法完成赋值;
//方法使用的参数,自定义类型的值从IOC容器里面进行获取
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Boss{" +
"car=" + car +
'}';
}
}
- 还有一种用法:
public class Color {
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Color{" +
"car=" + car +
'}';
}
}
@Configuration
@ComponentScan({"com.ldc.service","com.ldc.dao","com.ldc.controller","com.ldc.bean"})
public class MainConfigOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao() {
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
//@Bean标注的方法创建对象的时候,方法参数的值从容器中获取
@Bean
public Color color(Car car) {
Color color = new Color();
color.setCar(car);
return color;
}
}
@Test
public void test01() {
//1.创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = applicationContext.getBean(Boss.class);
Car car = applicationContext.getBean(Car.class);
System.out.println(car);
System.out.println(boss);
Color color = applicationContext.getBean(Color.class);
System.out.println(color);
}
com.ldc.bean.Car@6e75aa0d
Boss{car=com.ldc.bean.Car@6e75aa0d}
Color{car=com.ldc.bean.Car@6e75aa0d}
参数上
@Autowired
注解标注在参数上:效果是一样的
//默认加在IOC容器中的组件,容器启动的时候会调用无参构造器创建对象,再进行初始化赋值操作
@Component
public class Boss {
private Car car;
//构造器要用的组件,也都是从容器中来获取
//我们也可以标注在参数上效果是一样的
public Boss(@Autowired Car car) {
this.car = car;
System.out.println("Boss的有参构造器"+car);
}
public Car getCar() {
return car;
}
//@Autowired //标注在方法上,Spring容器在创建当前对象的时候,就会调用当前方法完成赋值;
//方法使用的参数,自定义类型的值从IOC容器里面进行获取
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Boss{" +
"car=" + car +
'}';
}
}
@Qualifier
- 虽然,我们在属性名写了bookDao2,但是,我就想要装配bookDao;实际上也是可以的:我们可以使用
@Qualifier
这个注解 - 使用
@Qualifier
指定需要装配的组件的id,而不是使用属性名
@Service
public class BookService {
@Qualifier("bookDao")
@Autowired
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
BookService{bookDao=BookDao{lable=‘1’}}
- 而当我们的容器里面没有一个对应的bean的时候,这个时候,就是会报一个错 :
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘bookService’: Unsatisfied dependency expressed through field ‘bookDao2’; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘com.ldc.dao.BookDao’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=bookDao), @org.springframework.beans.factory.annotation.Autowired(required=true)}
不强制装配
- 那可不可以在使用自动装配的时候,这个bean不是必须的呢?如果容器里面没有对应的bean,我就不装配,实际上也是可以的:我们要
@Autowired
注解里面添加required = false
这个属性
@Service
public class BookService {
@Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
BookService{bookDao=null}
@Primary
- 我们还可以利用一个注解来让Spring在自动装配的时候,首选装配哪个bean:
@Primary
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Primary {
}
使用
@Configuration
@ComponentScan({"com.ldc.service","com.ldc.dao","com.ldc.controller"})
public class MainConfigOfAutowired {
//标注bookDao2是首选装配
@Primary
@Bean("bookDao2")
public BookDao bookDao() {
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
@Service
public class BookService {
//@Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
BookService{bookDao=BookDao{lable=‘2’}}
- 如果是使用了
@Qualifier("bookDao")
明确指定了的:那还是按照明确指定的bean来进行装配
@Service
public class BookService {
@Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
BookService{bookDao=BookDao{lable=‘1’}}
@Reource
- Spring还支持使用
@Resource
(JSR250)和@Inject
(JSR330) @Resource
:可以和@Autowired
一样实现自动的装配,默认是按照组件的名称来进行装配,没有支持@Primary
也没有支持和@Autowired(required = false)
一样的功能
@Service
public class BookService {
@Resource
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
@Test
public void test01() {
//1.创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
BookService bookService = applicationContext.getBean(BookService.class);
System.out.println(bookService);
}
BookService{bookDao=BookDao{lable=‘1’}}
- 我们也可以用
@Resource
注解里面的name属性来指定装配哪一个:
@Service
public class BookService {
@Resource(name = "bookDao2")
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
BookService{bookDao=BookDao{lable=‘2’}}
@Inject
@Inject
:需要导入javax.inject的包,和@Autowired
的功能一样,没有支持和@Autowired(required = false)
一样的功能AutowiredAnnotationBeanPostProcessor
是用来解析完成自动装配的功能的@Autowired
:是Spring定义的-
@Resource
和@Inject
都是java的规范 - 导入jar包
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
- 使用
@Service
public class BookService {
@Inject
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
BookService{bookDao=BookDao{lable=‘2’}}
Aware接口
- 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory…) 自定义组件实现xxxAware接口就可以实现,在创建对象的时候,会调用接口规定的方法注入相关的组件;
- 把Spring底层的一些组件注入到自定义的bean中;
- xxxAware等这些都是利用后置处理器的机制,比如ApplicationContextAware 是通过ApplicationContextAwareProcessor来进行处理的;
- Aware是一个总接口,他有很多的实现类
使用
@Component
public class Red implements ApplicationContextAware, BeanNameAware , EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//如果我们后来要用,我们就用一个变量来存起来
System.out.println("传入的IOC容器:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前bean的名字:"+name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String resolveStringValue = resolver.resolveStringValue("你好${os.name} 我是#{20*18}");
System.out.println("解析的字符串"+resolveStringValue);
}
}
@Test
public void test01() {
//1.创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
}
当前bean的名字:red
解析的字符串你好Windows 7 我是360
传入的IOC容器:org.springframework.context.annotation.AnnotationConfigApplicationContext@4141d797: startup date [Tue Jan 15 15:29:08 CST 2019]; root of context hierarchy
@Profile
-
使用@Profile进行环境搭建
-
源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
- 引入数据源和mysql驱动
<!--数据源-->
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--数据库驱动-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
- 再写一个dbconfig.properties
db.user=root
db.password=12358
db.driverClass=com.mysql.jdbc.Driver
- 配置文件
/**
* Profile:
* Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能;
* 开发环境,测试环境,生产环境
* 我们以切换数据源为例:
* 数据源:开发环境中(用的是A数据库)、测试环境(用的是B数据库)、而生产环境(用的又是C数据库)
*/
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver resolver;
private String driverClass;
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/prod");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
driverClass = resolver.resolveStringValue("${db.driverClass}");
}
}
@value
- 直接通过属性上面加上
@Value("${db.user}")
获取配置文件中的值
@Value("${db.user}")
private String user;
- 在参数上面使用
@Value("${db.password}")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
- 实现EmbeddedValueResolverAware接口
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
driverClass = resolver.resolveStringValue("${db.driverClass}");
}
测试
@Test
public void test01() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
Stream.of(beanNamesForType).forEach(System.out::println);
}
testDataSource
devDataSource
prodDataSource
环境注册
- Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能;
- 开发环境,测试环境,生产环境
- 我们以切换数据源为例: 数据源:开发环境中(用的是A数据库)、测试环境(用的是B数据库)、而生产环境(用的又是C数据库)
@Profile
: 指定组件在哪一个环境的情况下才能被注册到容器中,不指定任何环境都能被注册这个组件- 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境,如果指定了default,那么这个bean默认会被注册到容器中
@Profile
写在配置类上,只有是指定的环境,整个配置类里面的所有配置才能开始生效- 没有标注环境标识的bean,在任何环境都是加载的
/**
* @Profile:
* Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能;
* 开发环境,测试环境,生产环境
* 我们以切换数据源为例:
* 数据源:开发环境中(用的是A数据库)、测试环境(用的是B数据库)、而生产环境(用的又是C数据库)
* @Profile: 指定组件在哪一个环境的情况下才能被注册到容器中,不指定任何环境都能被注册这个组件
* 1)加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境,如果指定了
* default,那么这个bean默认会被注册到容器中
* 2)@Profile 写在配置类上,只有是指定的环境,整个配置类里面的所有配置才能开始生效
* 3)没有标注环境标识的bean,在任何环境都是加载的
*/
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver resolver;
private String driverClass;
@Profile("test")
@Bean
public Yellow yellow() {
return new Yellow();
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/prod");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
driverClass = resolver.resolveStringValue("${db.driverClass}");
}
}
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//1)使用无参构造器来创建applicationContext对象
//2)设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//3)加载主配置类
applicationContext.register(MainConfigOfProfile.class);
//4)启动刷新容器
applicationContext.refresh();
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
Stream.of(beanNamesForType).forEach(System.out::println);
}
devDataSource