[Spring]-组件注入
注入组件
注入组件的各种方式
实验1
理解自动装配
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入**/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");UserService UserController = ioc.getBean(UserService.class);System.out.println("UserController=" + UserController);}}
package com.guigu.spring.ioc.controller;@Controller
public class UserController {/*** 自动装配, 原理: Spring调用容器的getBean方法* 自动装配流程: 先按照类型查找, 再按照名称查找* 1. 先按照类型查找组件* --> 有且只有一个, 直接注入* --> 如果找到多个, 再按照名称查询, 变量名就是名称* 2. 按照名称查询* --> 如果找到, 直接注入* --> 如果找不到, 报错*/@Autowired // UserService这个类型的组件只有一个, Spring直接按注入按类型注入, 名字无所谓UserService abc;@Autowired // Person这个类型的组件有多个, bill就要是组件的名字, 否则就无法匹配了, 会报错Person bill;@Autowired // 把Person类型的组件都拿到list集合中List<Person> personList;@Autowired // 把Person类型的组件都拿到map集合中, 组件的名字是key, 组件就是keyMap<String, Person> personMap;@Autowired // 拿到整个容器ApplicationContext applicationContext;}
package com.guigu.spring.ioc.service;@Service
public class UserService {
}
package com.guigu.spring.ioc.config;@Configuration // 告诉spring容器, 这是一个配置类
public class PersonConfig {@Bean("joseph")public Person joseph() {return new Person("乔布斯", 18, "男");}@Bean("bill")public Person bill() {return new Person("比尔盖茨", 18, "男");}}
实验2
如果容器中同一类型的bean存在多个, 如果注入失败, 可以使用以下注解解决
最佳实践: 强烈建议组件注册的名字 和 组件注入的名字符合规范, 这样可以避免不必要的麻烦
使用@Primary注解 指定默认组件
package com.guigu.spring.ioc.config;@Configuration // 告诉spring容器, 这是一个配置类
public class PersonConfig {@Primary // 主组件(默认组件)@Bean("joseph")public Person joseph() {return new Person("乔布斯", 18, "男");}@Bean("bill")public Person bill() {return new Person("比尔盖茨", 18, "男");}
}
package com.guigu.spring.ioc.controller;@Controller
public class UserController {@Autowired // Person这个类型的组件有多个, Spring无法按类型匹配唯一组件, 但是设置了默认组件, 可以正确匹配Person abcd;}
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入**/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");UserService UserController = ioc.getBean(UserService.class);System.out.println("UserController=" + UserController);}}
使用@QualiFier注解, 具名注入组件
package com.guigu.spring.ioc.config;@Configuration // 告诉spring容器, 这是一个配置类
public class PersonConfig {@Bean("joseph")public Person joseph() {return new Person("乔布斯", 18, "男");}@Bean("bill")public Person bill() {return new Person("比尔盖茨", 18, "男");}
}
package com.guigu.spring.ioc.controller;@Controller
public class UserController {@Qualifier("bill") // 精确指定组件的名字, 可以解决组件类型不唯一的问题, 如果设置了默认组件, 该注解还可以用于切换默认组件@Autowired // Person这个类型的组件有多个, Spring无法按类型匹配唯一组件Person abcde;}
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入**/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");UserService UserController = ioc.getBean(UserService.class);System.out.println("UserController=" + UserController);}}
实验3
了解 @Resource注解
package com.guigu.spring.ioc.controller;@Controller
public class UserController {/*** 面试题: @Resource 和 @Autowired 区别?* 1. @Resource 和 @Autowired 都是自动装配的注解* 2. @Resource是javax提供的注解,所有的容器框架都支持该注解 ,具有更强的通用性* 3. @Autowired 是Spring提供的注解, 现在spring框架一家独大, 所以一般都用这个注解* 4. @Autowired 默认按照类型装配, 默认情况下依赖对象必须存在, 否则报错, 可以设置required属性为false, 对象不存在时设置为null* 5. @Resource 注解不提供组件可选属性*/@Resource // Person这个类型的组件有多个, Spring无法按类型匹配唯一组件Person abcdef;}
实验4
通过构造器注入bean
- 让容器中有dog这个组件
package com.guigu.spring.ioc.bean;public class Dog {}
package com.guigu.spring.ioc.config;@Configuration
public class DogConfig {@Beanpublic Dog dog() {return new Dog();}
}
- 利用构造器注入组件
package com.guigu.spring.ioc.dao;@Repository
public class UserMapper {Dog dog;/*** 使用构造器注入组件(官方推荐)* 前提: 1组件中不要声明无参构造器,声明有参构造器* 2组件要交给容器管理* 原理: Spring 自动去容器中找到构造器需要的对象, 并且注入到构造器中*/public UserMapper(Dog dog) {System.out.println("UserMapper的构造器," + dog);this.dog = dog;}
}
- 检查一下: Dog组件有了, 并且在UserMapper创建时, 也被注入了
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入**/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");Dog bean = ioc.getBean(Dog.class);System.out.println("bean=" + bean);}}
实验5
通过setter方法注入组件
- 让容器中有dog这个组件
package com.guigu.spring.ioc.bean;public class Dog {}
package com.guigu.spring.ioc.config;@Configuration
public class DogConfig {@Beanpublic Dog dog() {return new Dog();}
}
- 利用setter方法注入组件
package com.guigu.spring.ioc.dao;@Repository
public class UserMapper {Dog dog;/*** 通过setter方法注入组件*/@Autowiredpublic void stetDog(Dog dog) {System.out.println("UserMapper的setter方法," + dog);this.dog = dog;}
}
- 检查一下: Dog组件有了, 并且在UserMapper创建时, 也被注入了
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入**/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");Dog bean = ioc.getBean(Dog.class);System.out.println("bean=" + bean);}}
实验6
理解感知接口:
- Aware是Spring定义的一个接口, 官方为Aware接口定义了很多实现类
- ctrl + h 查看接口的所有实现, 这些实现都是感知接口, 想用啥实现啥就行, 下面有例子
- 如果组件中需要环境变量, 就直接实现EnvironmentAware, 重写setEnvironment()方法, 就可以拿到环境信息
package com.guigu.spring.ioc.service;@Service
public class TestService implements EnvironmentAware, BeanNameAware {// 保存系统环境变量private Environment environment;// 保存组件名称private String myName;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}public String getOsType() {return environment.getProperty("OS");}@Overridepublic void setBeanName(String name) {this.myName = name;}public String getMyName() {return myName;}
}
- 运行结果
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入*/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");TestService testService = ioc.getBean(TestService.class);System.out.println("testService=" + testService);String osType = testService.getOsType();System.out.println("osType=" + osType);String myName = testService.getMyName();System.out.println("myName=" + myName);}
}
实验7
使用@Value注解获取配置文件的数据
package com.guigu.spring.ioc.bean;@Data
@Component
public class Dog {/*** 1.@Value("字面量"): 直接赋值* 2.@Value("#{}): 从配置文件中读取数据* 3.@Value("#{SpEL}"): 写Spring表达式语言*/@Value("旺财")private String name;//private String name = "旺财";@Value("${dog.age}")private Integer age;@Value("#{10*20}")private String color;@Value("#{T(java.util.UUID).randomUUID().toString()}")private String id;@Value("#{'Hello World!'.substring(0,5)}")private String msg;@Value("#{1>2? 'heihei' : 'haha'}")private String flg;public Dog() {System.out.println("Dog的构造器...");}public Dog(String name, Integer age, String color) {this.name = name;this.age = age;this.color = color;}
}
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入*/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");Dog bean = ioc.getBean(Dog.class);System.out.println("bean=" + bean);}
}
实验8
指定属性注入的数据源
cat.name="Tom"
cat.age=12
package com.guigu.spring.ioc.bean;/*** @PropertySource指定数据来源: 把指定的文件导入容器中, 供我们使用* 1.classpath:cat.properties: 从自己的项目类路径下找* 2.classpath*:Log4j-charsets.properties: 从所有包的类路径下找*/
@PropertySource("classpath:cat.properties")
@Data
@Component
public class Cat {@Value("${cat.name:Tom}") //:后面的值是取值失败时的默认值private String name;@Value("${cat.age:20}")private String age;
}
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入*/public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");Cat bean = ioc.getBean(Cat.class);System.out.println("bean=" + bean);}}
实验9
获取资源
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入*/public static void main(String[] args) throws IOException {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);System.out.println("===ioc容器创建完成===");File file = ResourceUtils.getFile("classpath:DE.jpg");System.out.println("file=" + file);int available = new FileInputStream(file).available();System.out.println("文件大小="+available);}}
实验10
理解多环境
- 准备一个类, 用来模拟数据库连接
package com.guigu.spring.ioc.datasource;@Data
public class MyDataSource {private String url;private String username;private String password;
}
- 通过配置类和环境注解, 创建数据库对象
package com.guigu.spring.ioc.config;//@Profile("dev") // 该注解也可以加载类上, 控制整体激活
@Configuration
public class DataSourceConfig {/*** 利用条件注解, 只在某种环境下激活一个组件* @Profile(环境标识) 当这个环境激活时, 才会加载这个组件* 使用:* 1.定义环境标识: 默认存在default标识, 我们自定义 dev test prop 三个标识* 2.激活环境标识: 告诉Spring当前处于什么环境, 默认时default环境*/@Profile("dev")@Beanpublic MyDataSource dev() {System.out.println("模拟dev环境连接数据库");MyDataSource dataSource = new MyDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/dev");dataSource.setUsername("dev_root");dataSource.setPassword("dev_root");return dataSource;}@Profile("test")@Beanpublic MyDataSource test() {System.out.println("模拟text环境连接数据库");MyDataSource dataSource = new MyDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("test_root");dataSource.setPassword("test_root");return dataSource;}@Profile("prod")@Beanpublic MyDataSource prod() {System.out.println("模拟prod环境连接数据库");MyDataSource dataSource = new MyDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/prod");dataSource.setUsername("prod_root");dataSource.setPassword("prod_root");return dataSource;}}
- 在配置文件中, 激活环境
spring.profiles.active=dev
- 运行代码, 不同环境下, 注入不同的数据库连接对象, 实现多环境
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 组件注入*/public static void main(String[] args) throws IOException {ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);}
}
实验11
使用原生spring的方式启动容器
package com.guigu.spring.ioc;/*** 主入口文件*/
@SpringBootApplication
public class Spring01IocApplication {/*** 使用原生方式启动容器*/public static void main(String[] args) {// SpringApplication这个类是boot包下的类, 其实是springboot提供的管理容器的类//ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);// 没有springboot之前, 是如何启动容起的呢?// 1.自己创建: 在类路径下找配置文件, 配置文件中声明需要的组件ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:ioc.xml");// 2.自己创建: 在其他盘中找配置文件// FileSystemXmlApplicationContext ioc = new FileSystemXmlApplicationContext("D:/");// 查看组件for (String definitionName : ioc.getBeanDefinitionNames()) {System.out.println("组件名称:"+ definitionName);}}
}
新建配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 声明默认值的组件: id是组件的唯一名称 --><bean id="zhngsan" class="com.guigu.spring.ioc.bean.Person"></bean><!-- 声明组件: 设置属性, 注意Person类中要提供set方法 --><bean id="lisi" class="com.guigu.spring.ioc.bean.Person"><property name="name" value="李四"></property><property name="age" value="#{10 + 10}"></property></bean><!-- 批量扫描 --><context:component-scan base-package="com.guigu.spring" /><!--导入属性文件 --><context:property-placeholder location="application.properties" />
</beans>
运行项目, 容器启动, 注册正常注册