1、Spring 容器继承图
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL3lFRPRTW610dRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4QjM4IjN1AjMxEDNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2、控制反转和依赖注入
- 什么是控制反转?我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Principle )
- 什么是依赖倒置原则?假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。
从上图来看好像没什么问题,但万一轮胎尺寸改了,那么地盘需要改,地盘改了,车身也改了,让后整个汽车构造都改了,牵一发而动全身。但如果反过来假如汽车公司决定修改轮胎的 我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车 的设计了。
IOC容器的核心思想在于资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第 一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。举个例子,假如我们需要找个房子,我们需要租个房子,我们可以自己去找房源(但我们需要依赖房源等因素),也可以交由中介进行管理(这样子我们就是不直接依赖房源了,这个交给中介管理)。
3、Spring IOC 容器注解使用
1、我们很多时候通过基于xml的形式定义Bean的信息。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.cym.bean.compent.User"></bean>
</beans>
读取容器中的bean的方式
public class BeanMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
Object user = classPathXmlApplicationContext.getBean("user");
System.out.println(user);
// AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
// Object user1 = annotationConfigApplicationContext.getBean("person");
// System.out.println(user1);
}
}
2、基于读取配置类的形式定义Bean信息
@Configuration
public class BeanConfig {
@Bean(name = "person")
public User user(){
return new User();
}
}
注意 : 通过 @Bean 的形式是使用的话, bean 的默认名称是方法名,若 @Bean(value="bean 的名称 ") 那么 bean 的名称是指定的 去容器中读取 Bean 的信息( 传入配置类 )
public class BeanMain {
public static void main(String[] args) {
// ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
// Object user = classPathXmlApplicationContext.getBean("user");
// System.out.println(user);
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
Object user1 = annotationConfigApplicationContext.getBean("person");
System.out.println(user1);
}
}
3、在配置类上写 @CompentScan 注解来进行包扫描
@Configuration
@ComponentScan(basePackages = {"com.cym.componentscan"})
//@ComponentScan(basePackages = {"com.cym.componentscan"},
// excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})})
//@ComponentScan(basePackages = {"com.cym.componentscan"},
// includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})}, useDefaultFilters = false)
public class UserConfig {
}
从容器中获取bean
public class UserMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext acx = new AnnotationConfigApplicationContext(UserConfig.class);
String[] beans = acx.getBeanDefinitionNames();
for (String bean :beans){
System.out.println("bean定义的信息:"+bean);
}
}
}
排除用法 excludeFilters( 排除 @Controller 注解的 , 和UserService的bean )
@Configuration
//@ComponentScan(basePackages = {"com.cym.componentscan"})
@ComponentScan(basePackages = {"com.cym.componentscan"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})})
//@ComponentScan(basePackages = {"com.cym.componentscan"},
// includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})}, useDefaultFilters = false)
public class UserConfig {
}
包含用法 includeFilters , 注意,若使用包含的用法, 需要把 useDefaultFilters 属性设置为 false ( true 表 示扫描全部的)
@Configuration
//@ComponentScan(basePackages = {"com.cym.componentscan"})
//@ComponentScan(basePackages = {"com.cym.componentscan"},
// excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})})
@ComponentScan(basePackages = {"com.cym.componentscan"},
includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})}, useDefaultFilters = false)
public class UserConfig {
}
@ComponentScan.Filter type 的类型
注解形式的 FilterType.ANNOTATION | @Controller @Service @Repository @Compent |
指定类型的 FilterType.ASSIGNABLE_TYPE | |
aspectj 类型的 | FilterType.ASPECTJ( 不常用 ) |
正则表达式的 | FilterType.REGEX(不常用 ) |
自定义的 | FilterType.CUSTOM |
package org.springframework.context.annotation;
public enum FilterType {
//注解形式 比如@Controller @Service @Repository @Compent
ANNOTATION,
//指定的类型
ASSIGNABLE_TYPE,
//aspectJ形式的
ASPECTJ,
//正则表达式的
REGEX,
//自定义的
CUSTOM;
private FilterType() {
}
}
FilterType.CUSTOM 自定义类型如何使用
public class MyFilterType implements TypeFilter {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类的注解源信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前类的class的源信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源信息
Resource resource = metadataReader.getResource();
//扫描UserController、UserService
if (classMetadata.getClassName().contains("UserController") || classMetadata.getClassName().contains("UserService")) {
return true;
}
return false;
}
}
配置类
@Configuration
//@ComponentScan(basePackages = {"com.cym.componentscan"})
//@ComponentScan(basePackages = {"com.cym.componentscan"},
// excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})})
//@ComponentScan(basePackages = {"com.cym.componentscan"},
// includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class})}, useDefaultFilters = false)
@ComponentScan(basePackages = {"com.cym.componentscan"},
includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = MyFilterType.class)}, useDefaultFilters = false)
public class UserConfig {
}
4、配置 Bean 的作用域对象 在不指定 @Scope 的情况下,所有的 bean 都是单实例的 bean, 而且是饿汉加载 ( 容器启动实例就创建 好了 )
@Configuration
public class BeanConfig {
@Bean
public User user(){
return new User();
}
}
@Setter
@Getter
public class User {
public User() {
System.out.println("在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)");
}
private String userName;
private String age;
private String gender;
private String phone;
private String address;
private Date birthday;
}
获取容器中的bean
指定 @Scope 为 prototype 表示为多实例的,而且还是懒汉模式加载( IOC 容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建)
@Configuration
public class BeanConfig {
@Bean
@Scope(value = "prototype")
public User user(){
return new User();
}
}
@Scope 指定的作用域方法取值
singleton | 单实例的 ( 默认 ) |
prototype | 多实例的 |
request | 同一次请求 |
session | 同一个会话级别 |
5、Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象)
@Configuration
public class BeanConfig {
@Bean
@Lazy
public User user(){
return new User();
}
}
6、@Conditional进行条件判断等(可以用于自动装配)
场景,有二个组件Parents 和Childrents ,我的Childrents组件是依赖于Parents的组件
应用: 自己创建一个My Condition 的类 实现 Condition接口
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//判断容器中是否有Parent的组件
if (conditionContext.getBeanFactory().containsBean("parents")){
return true;
}
return false;
}
}
配置类
@Configuration
public class BeanConfig {
@Bean
public Parents parents() {
return new Parents();
}
@Bean
@Conditional(value = MyCondition.class)
public Childrens childrens() {
return new Childrens();
}
}
7、往IOC 容器中添加组件的方式
通过 @CompentScan [email protected] @Service @Respository @compent,适用场景 : 针对我们自己写的组件可以通过该方式来进行加载到容器中。 |
通过 @Bean 的方式来导入组件 ( 适用于导入第三方组件的类 ) |
通过 @Import 来导入组件 (导入组件的 id为全类名路径),通过 @Import 的 ImportSeletor 类实现组件的导入 ( 导入组件的 id 为全类名路径 ) |
通过实现 FacotryBean 接口来实现注册 组件 |
通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径,如:com.cym.Import.compent.Parents)
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.cym.Import.compent.Dog"};
}
}
通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)
public class MyBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
//创建一个bean定义对象
RootBeanDefinition parents = new RootBeanDefinition(Parents.class);
RootBeanDefinition childrens = new RootBeanDefinition(Childrens.class);
RootBeanDefinition dog = new RootBeanDefinition(Dog.class);
//把bean定义对象导入到容器中
registry.registerBeanDefinition("parents",parents);
registry.registerBeanDefinition("childrens",childrens);
registry.registerBeanDefinition("dog",dog);
}
}
配置类
@Configuration
//@Import({Parents.class,Childrens.class, MyImportSelector.class})
@Import(MyBeanDefinitionRegister.class)
public class BeanConfig {
}
通过实现 FacotryBean 接口来实现注册 组件
public class MyrFactoryBean implements FactoryBean<User> {
//返回bean对象
@Override
public User getObject() throws Exception {
return new User();
}
//返回bean的类型
@Override
public Class<?> getObjectType() {
return User.class;
}
//是否为单例
@Override
public boolean isSingleton() {
return false;
}
配置类
@Configuration
public class BeanConfig {
//注册我们自己factoryBean
@Bean
public MyrFactoryBean myrFactoryBean(){
return new MyrFactoryBean();
}
}
7、Bean的初始化方法和销毁方法
- 什么是bean的生命周期?bean的创建----->初始化----->销毁方法由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法。
public class User {
public User() {
System.out.println("我是构造器");
}
public void init(){
System.out.println("我是bean的生命周期的初始化方法init");
}
public void destroy(){
System.out.println("我是bean的生命周期的销毁方法destroy");
}
}
@Configuration
public class BeanConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public User user() {
return new User();
}
}
public class BeanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
Object user1 = annotationConfigApplicationContext.getBean("user");
System.out.println(user1);
annotationConfigApplicationContext.close();
}
}
针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法。针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受 IOC容器的管理
- 通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法
/**
* @description:
* @author: 通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法
* @date: 2020-04-12 12:27
*/
public class Person implements InitializingBean, DisposableBean {
public Person() {
System.out.println("我是Person构造器");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean的destroy()方法 ");
}
/**
* 集成了InitializingBean这个类的在bean创建完成后会自动调用该该方法
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean的 afterPropertiesSet方法");
}
}
@Configuration
public class BeanConfig {
@Bean
public Person person(){
return new Person();
}
}
/**
* @description: 基于读取配置类的形式定义Bean信息
* @author:
* @date: 2020-04-11 19:45
*/
public class BeanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
Object user1 = annotationConfigApplicationContext.getBean("person");
System.out.println(user1);
annotationConfigApplicationContext.close();
}
}
- 通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法
@Component
public class Dog {
public Dog() {
System.out.println("Dog构造方法");
}
@PostConstruct
public void init() {
System.out.println("Dog 的PostConstruct标志的方法");
}
@PreDestroy
public void destroy() {
System.out.println("Dog 的PreDestory标注的方法");
}
}
@Configuration
@ComponentScan(basePackages = {"com.cym.init_destroy"})
public class BeanConfig {
}
public class BeanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
Object dog = annotationConfigApplicationContext.getBean("dog");
System.out.println(dog);
annotationConfigApplicationContext.close();
}
}
- 通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程(postProcessBeforeInitialization在init方法之前调用 postProcessAfterInitialization 在 init 方法之后调用)
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor...postProcessBeforeInitialization:"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor...postProcessAfterInitialization:"+beanName);
return bean;
}
}
@Configuration
@ComponentScan(basePackages = {"com.cym.init_destroy"})
public class BeanConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public User user() {
return new User();
}
}
public class BeanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
// Object dog = annotationConfigApplicationContext.getBean("cat");
// System.out.println(dog);
annotationConfigApplicationContext.close();
}
}
8、通过@Value [email protected]来给组件赋值
@Setter
@Getter
public class User {
@Value("张三")//通过普通的方式
private String userName;
@Value("#{28-3}")//spel方式来赋值
private String age;
@Value("${user.gender}")//通过读取外部配置文件的值
private String gender;
@Value("${user.phone}")//通过读取外部配置文件的值
private String phone;
@Value("${user.address}")//通过读取外部配置文件的值
private String address;
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age='" + age + '\'' +
", gender='" + gender + '\'' +
", phone='" + phone + '\'' +
", address='" + address + '\'' +
'}';
}
}
@Configuration
@PropertySource(value = {"classpath:user.properties"})//指定外部文件的位置
public class BeanConfig {
@Bean
public User user(){
return new User();
}
}
public class BeanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
Object user1 = annotationConfigApplicationContext.getBean("user");
System.out.println(user1);
}
}
9、自动装配@AutoWired的使用
@Component
public class UserDao {
public void printUserName(String userName){
System.out.println("my name is " + userName);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void printName(String userName){
userDao.printUserName(userName);
}
}
@Configuration
@ComponentScan(basePackages = {"com.cym.value"})
public class BeanConfig {
@Bean
public User user(){
return new User();
}
}
public class BeanMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
UserService userService = (UserService) annotationConfigApplicationContext.getBean("userService");
userService.printName("张三");
}
}
- 自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配比如,我容器中有二个UserDao类型的组件 一个叫UserDao 一个叫UserDao2那么我们通过@AutoWired 来修饰的属性名称时UserDao,那么拿就加载容器的UserDao组件,若属性名称为UserDao2 那么他就加载的时UserDao2组件。
- 假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier("UserDao")来指定装配的组件或者在配置类上的@Bean加上@Primary注解
@Autowired
@Qualifier("userDao")
private UserDao userDao2;
- 假设我们容器中即没有userDao 和userDao2,那么在装配的时候就会抛出异常若我们想不抛异常 ,我们需要指定 required为false的时候可以了
@Autowired(required = false) @Qualifier("userDao") private UsergDao userDao2;
10、@Resource(JSR250规范)
- 功能和@AutoWired的功能差不多一样,但是不支持@Primary 和@Qualifier的支持
11、@InJect(JSR330规范)
- 需要导入jar包依赖功能和支持@Primary功能 ,但是没有Require=false的功能
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
12、通过@Profile注解 来根据环境来激活标识不同的Bean
- @Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效,@Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活没有标志为@Profile的bean 不管在什么环境都可以被激活。
@Configuration
@PropertySource(value = {"classpath:ds.properties"})
public class MainConfig implements EmbeddedValueResolverAware {
@Value("${ds.username}")
private String userName;
@Value("${ds.password}")
private String password;
private String jdbcUrl;
private String classDriver;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}");
this.classDriver = resolver.resolveStringValue("${ds.classDriver}");
}//标识为测试环境才会被装配
@Bean
@Profile(value = "test")
public DataSource testDs() {
return buliderDataSource(new DruidDataSource());
}
//标识开发环境才会被激活
@Bean
@Profile(value = "dev")
public DataSource devDs() {
return buliderDataSource(new DruidDataSource());
}
//标识生产环境才会被激活
@Bean
@Profile(value = "prod")
public DataSource prodDs() {
return buliderDataSource(new DruidDataSource());
}
private DataSource buliderDataSource(DruidDataSource dataSource) {
dataSource.setUserName(userName);
dataSource.setPassword(password);
dataSource.setClassDriver(classDriver);
dataSource.setJdbcUrl(jdbcUrl);
return dataSource;
}
}
激活切换环境的方法 方法一 : 通过运行时 jvm 参数来切换 -Dspring.profiles.active=test|dev|prod 方法二 : 通过代码的方式来激活
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("test", "dev");
ctx.register(MainConfig.class);
ctx.refresh();
Object testDs = ctx.getBean("testDs");
System.out.println(testDs);
Object devDs = ctx.getBean("devDs");
System.out.println(devDs);
// String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
// for (String item : beanDefinitionNames){
// System.out.println(item);
// }
}
}