Spring基础笔记 (二)

简介: Spring基础笔记 (二)

Bean的生命周期#


通过第一阶段的学习,我们顺利把bean的创建权反转给了Spring,下面就来看看,IOC是如何控制Bean的生命周期的!


什么是bean的生命周期?


其实,就是下面的过程

Bean创建------> 初始化------> 销毁


在IOC管理bean生命周期的过程中,我们可以插手做什么?


  • 我们可以自定义bean的初始化和销毁方法,bean在到达相应的生命周期时,ioc会调用我们指定的方法,施加在bean上


原来的配置文件版本,需要我们写 init-method 和distroy-method


如何实现?


方法1: 使用@Bean注解完成#


@Bean(initMethod="init",destroyMethod = "destory")


测试类


@Test
    public void text6(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeConfig.class);
        Object car = applicationContext.getBean("car");
        System.out.println(car);
        applicationContext.close();
    }


单实例情况下: 对象创建后并赋值好了后,执行init方法,容器关闭对象销毁

多实例情况下: 对象创建后并赋值好了后,执行init方法,IOC不会管理bean,需要我们自己去销毁bean


用处?

在配置数据源是大量使用,对象创建后需要初始话很多的数据,对象销毁了,很多资源要释放


方法2: 让Bean实现spring提供的两个接口InitializingBeanDisposableBean,重写这两个接口的方法#


public interface InitializingBean {
    // Bean初始化后调用
  void afterPropertiesSet() throws Exception;
}
public interface DisposableBean {
  // Bean销毁后调用
  void destroy() throws Exception;
}


  • 单例模式下: 在IOC一初始化就进行bean构造,并且执行完了的afterPropertiesSet()初始化,容器关闭,bean执行销毁DisposableBean()


方法3: 使用JS205规范提供的两个注解@PostConstruct和@PreDestory完成#


  • @PostConstruct :在bean创建完成并且属性赋值好了后执行本初始化方法
  • @PreDestory: 在容器销毁bean之前,通知我们进行清理工作


这两个注解都是标注在方法上的!!!


@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}


方法4: 使用Spring提供给我们的组件,接口BeanPostProcessor#


这个组件很重要,Spring的底层,尤其是AOP底层大量使用它

里面有两个方法


public interface BeanPostProcessor {
   // 该方法,会在我们说的前三种初始化方法调用之前, 提前调用!!!
   // 返回值,可以是我们新创建好的这个bean,也可以包装一下bean 再返回     
    /* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
   * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
   * or a custom init-method). The bean will already be populated with property values.
   * The returned bean instance may be a wrapper around the original.
     */
  Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  // 在上面我们所说的三种初始化方法调用之后,立刻调用!!!
  Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}


实现自己的BeanPostprocessor


@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("postProcessBeforeInitialization执行了!!!"+"    beanname=="+beanName+"  bean=="+bean);
    return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("postProcessAfterInitialization执行了!!!"+"    beanname=="+beanName+"  bean=="+bean);
    return bean;
}
}


测试结果如下图


网络异常,图片无法展示
|


划重点!!! --- BeanPostprocessor意味bean的后置处理器,前面也说了,它是spring提供给我们的一个辅助类型的组件,我们可以自定义BeanPostProcessor,在所有满足加入IOC容器的bean初始化方法前后执行BeanPostProcessor接口里的postProcessAfterInitialization()postProcessBeforeInitialization()方法,完成对bean的增强,AOP的底层使用的 后置处理器 是spring自动给我们加载进来的,他的这种特性,为AOP,动态植入代码的实现,提供的前提


接口BeanPostProcessor运行原理#


  • 遍历得到容器中所有的BeanPostProcessor,挨个执行beforeInitialization,一旦返回值为null,说明ioc在没有这个对象,直接跳出for循环,不会执行BeanPostProcessor.postProcessBeforeInitialization()方法进行处理

populateBean(beanName,mdb,instanceWrapper);给bean的属性赋值
initlizeBean // 初始化
{
    applyBeanPostProcessorBeforeInitialization()  // 前置处理
    invokeInitMethods(beanName,wrappedBean,mdb); // 执行初始化方法
    applyBeanPostProcessorAfterInitialization() ; // 后置处理
}


网络异常,图片无法展示
|


通过这个继承图,可以看到,BeanPostProcessor作为后置处理器的顶级接口存在,程序运行打上断点,也能看到我们自定义的MyBeanPostProcessor,另外需要我们关心的一个实现是 InstantiationAwareBeanPosProcessor这个子接口,AOP的实现,就应用到了它(第二篇博客会记录)


使用配置文件给bean赋值#


<bean id="person" class="com.changwu.bean" scope="prototype">
    <property name="age" value="18"/>
    <property name="name" value="zhangsan"/>
</bean>


使用注解的方法,给bean赋值#


@Value#


1. 基本数值  
@Value("changwu")
private String name;
2. #{}  SPEL表达式
@Value(value ="#{2*2}")
private int  age;
3. ${}  取出配置文件中的值
@Value(value ="${student.age}")
private int  age;


其中取出配置文件中的值,要在主配置类上添加@PropertySource(value = "classpath:/bean.properties")指明配置文件的路径


@Value+@PropertySource#



前者使用${} 取出环境变量中的属性(程序运行后配置文件会加载进环境变量),后者给前者提供定位


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)  // 可以写多个
public @interface PropertySource {
String name() default "";
//支持同时加载多个配置文件
String[] value();
// 忽略文件找不到
boolean ignoreResourceNotFound() default false;
//设置编码
String encoding() default "";
/**
 * Specify a custom {@link PropertySourceFactory}, if any.
 * <p>By default, a default factory for standard resource files will be used.
 * @since 4.3
 * @see org.springframework.core.io.support.DefaultPropertySourceFactory
 * @see org.springframework.core.io.support.ResourcePropertySource
 */
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}


自动装配?#


  • 什么是自动装配?


自动装配就是,Spring利用依赖注入DI,完成对IOC容器中的各个组件依赖关系的赋值


@Autowired & @Qualifier & @Primary#


@Autowired是Spring自己的注解


那些Bean,都在IOC中,通过@Autowired注解可以完成自动装配

  • 默认按照类型(XXX.class)去IOC中找到对应的组件
  • 如果存在多个bean,就按照id找( @Autowired标记的引用名)
  • 使用@Autowired,默认如果IOC中不存在该bean,就会报错
  • 通过设置@Autowired(required=false) 设置组件不必需存在于,IOC
  • @Autowired+@Qualifier 明确指定装配的bean的id


@Qualifier("bookDao2")
@Autowired
private BookDao bookDao;


再强调一遍,如果是包扫描的话,Bean在IOC的id是类名首字母小写,@Qualifier("XXX")不能乱写,要么是类名首字母小写,要么是我们通过@Bean("XXX")指定的id


  • @Primary 标记在我们手动添加进去的bean上,强制,首选注入!!!


但是,如果同时存在@Primary@Qualifier 依然会装配我们明确指定的Bean


// 构造器, 方法,参数,属性,全能!!!
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
  /**
   * Declares whether the annotated dependency is required.
   * <p>Defaults to {@code true}.
   */
  boolean required() default true;
}


  1. @Autowired标注在方法上,方法的参数,默认从容器中获取
  2. @Autowired标注在构造器上,构造器需要的参数,默认从容器中获取
  3. @Bean标注的方法,方法的参数,默认从容器中获取,在参数位置的@Autowired可以省略不写


@Resources(JSR205) & @Inject(JSR330)#


java规范注解


@Resources

  • 作用: 默认按照组件名称装配
  • 缺点: 不能和@Qulifier@Primary一起使用


@Inject

  • 还麻烦! 需要我们导入依赖


<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>


@Profile#


Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能


比如在开发环境下,我们使用A数据库, 测试环境使用B数据库, 生产环境使用C数据库


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
  /**
   * The set of profiles for which the annotated component should be registered.
   */
  String[] value();
}


支持写到方法上!满足@profile指定的环境下,方法的中组件的注入才会生效

支持写到类上!满足@profile指定的环境下,整个类的中组件的注入才会生效


  1. 准备工作,注册三个数据源


@Configuration
@PropertySource("classpath:/dbProperlies.properties")
public class MainConfiguration {
    @Value("${db.user}")
    private String user;
    @Value("${db.driverClass}")
    private String driverClass;
    @Profile("text")
    @Bean("TextDBSource")
    public DataSource dataSourceText(@Value("${db.password}") String pwd) {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/text");
        try {
            dataSource.setDriverClass(driverClass);
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return dataSource;
    }
    @Profile("dev")
    @Bean("DevDBSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/person");
        try {
            dataSource.setDriverClass(driverClass);
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return dataSource;
    }
    @Profile("pro")
    @Bean("ProductDBSource")
    public DataSource dataSourceProduct(@Value("${db.password}") String pwd) {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssh1");
        try {
            dataSource.setDriverClass(driverClass);
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}


2 . 结合@Profile注解,在组件上做一个标识,只有满足@Profile标识条件的才会被注册进IOC

  • 加了环境标识的bean,只有那个环境被激活,才会注册到容器中,默认是@Profile("default")
  • 没有环境标识的bean,任何条件下都会加载进容器


如何改变环境?#


  1. 使用代码的方法


@Test
    public void text13(){
        // 创建上下文
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 设置需要激活的环境
        applicationContext.getEnvironment().setActiveProfiles("dev");    // 开发环境下注册组件
        // 加载主配置类
        applicationContext.register(MainConfiguration.class);
        // 启动刷新容器
        applicationContext.refresh();
        String[] names = applicationContext.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        applicationContext.close();
    }
相关文章
|
4月前
|
Java Spring
【编程笔记】在 Spring 项目中使用 RestTemplate 发送网络请求
【编程笔记】在 Spring 项目中使用 RestTemplate 发送网络请求
117 0
|
7天前
|
Java 数据库连接 API
【Java笔记+踩坑】Spring Data JPA
从常用注解、实体类和各层编写方法入手,详细介绍JPA框架在增删改查等方面的基本用法,以及填充用户名日期、分页查询等高级用法。
【Java笔记+踩坑】Spring Data JPA
|
7天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
3月前
|
NoSQL 前端开发 Java
技术笔记:springboot分布式锁组件spring
技术笔记:springboot分布式锁组件spring
42 1
|
3月前
|
Java Linux 程序员
技术笔记:Spring生态研习【五】:Springboot中bean的条件注入
技术笔记:Spring生态研习【五】:Springboot中bean的条件注入
|
3月前
|
XML Java 数据安全/隐私保护
技术笔记:Spring中的通知(Advice)和顾问(Advisor)
技术笔记:Spring中的通知(Advice)和顾问(Advisor)
28 0
|
4月前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
85 2
|
4月前
|
Java Spring 容器
【Spring系列笔记】IOC与DI
IoC 和 DI 是面向对象编程中的两个相关概念,它们主要用于解决程序中的依赖管理和解耦问题。 控制反转是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入和依赖查找。
70 2
|
4月前
|
缓存 监控 Java
【Spring系列笔记】AOP
面向切面编程就是面向特定方法编程。通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,提供一种更好的代码模块化和可维护性。 横切关注点指的是在应用程序中横跨多个模块或层的功能,例如日志记录、事务管理、安全性、缓存、异常处理等。
67 0
|
4月前
|
存储 缓存 Java
【Spring系列笔记】依赖注入,循环依赖以及三级缓存
依赖注入: 是指通过外部配置,将依赖关系注入到对象中。依赖注入有四种主要方式:构造器注入、setter方法注入、接口注入以及注解注入。其中注解注入在开发中最为常见,因为其使用便捷以及可维护性强;构造器注入为官方推荐,可注入不可变对象以及解决循环依赖问题。本文基于依赖注入方式引出循环依赖以及三层缓存的底层原理,以及代码的实现方式。
73 0