@Bean指定初始化和销毁方法
bean的生命周期:bean的创建——初始化——销毁的过程
容器来管理bean的生命周期。
我们可以自定义初始化和销毁方法;容器在进行到当前生命周期时来调用我们自定义的初始化和销毁方法。
1. 指定初始化和销毁方法
xml方式
<bean id="person" class="bean.Person" init-method="" destroy-method="">
注解方式
// 初始化方法和销毁方法一定要在bean中定义
@Bean(initMethod = "initCar",destroyMethod = "destoryCar")
public Car getCar(){
return new Car();
}
单例情况下,容器关闭时,销毁bean,多例情况下,bean创建后交给你自己管理,即使容器关闭也不会销毁(即创建后就不管了)
2. 让Bean实现InitializingBean接口(初始化)、实现DisposableBean接口来(销毁)
InitializingBean的接口方法是在构造对象赋值完成后调用(afterPropertiesSet())
package bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class cat implements InitializingBean, DisposableBean {
public cat() {
System.out.println("constructor....");
}
public void destroy() throws Exception {
System.out.println("destroy....");
}
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet....");
}
}
3. 使用JSR250
@PostConstruct:在bean创建完成,并且属性赋值完成后,调用此方法
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
package bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class Dog {
public Dog() {
System.out.println("constructor....");
}
// 销毁
@PreDestroy
public void destroy() throws Exception {
System.out.println("destroy....");
}
// 初始化
@PostConstruct
public void init() throws Exception {
System.out.println("afterPropertiesSet....");
}
}
4.BeanPostProcessor接口
对象初始化前后,对对象进行拦截处理。
package bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization....::"+beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization...::"+beanName);
return bean;
}
}
属性赋值
@value
- 可直接赋值
- 可使用SPEL表达式,如#{systemProperties.myProp}
- 获取配置文件中的值,如${my.app.myProp}
public class Person {
@Value("佩奇")
private String name;
@Value("#{20-2}")
private Integer age;
......
}
@PropertySources 加载外部配置文件
如需使用${my.app.myProp}获取配置文件的值,需要配合使用@PropertySources 来引入外部文件。
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class ConfigOfLifecycle {
}
自动装配
spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值。
@Autowired:自动注入
- 默认优先按照类型去匹配,若相同类型的对象找到多个,再用属性名称作为组件的ID去容器中匹配。
@Qualifier
- 指定需要装配的组件iD,而不是使用属性名。
这两种都要求自动注入的bean要存在,否则,项目报错。但如果当前bean不是必须的,怎么处理呢?
可以在使用@Autowired(required = false)
@Primary:设置首选装配的对象
当容器中有多个同一类型的对象时,添加@Primary可以优先装配此对象。
/*
@Autowired
private Person person; //优先装配李四
当有多个person对象在容器中,又没有指定哪个对象时,优先装配此对象(李四)
*/
@Primary
@Bean
public Person person() {
System.out.println("调用获取对象方法");
return new Person("李四", 25);
}
@Resource[java规范的注解]
同@Autowired,区别是它首先使用名称去匹配容器中的对象,再使用类型去匹配;可以直接指定名称(name=xxx);
因为是Java的注解,所以,不支持@primary,以及其他spring相关的注解
@Resource(name="personDao")
private PersonDao dao;
@Inject[java规范的注解]
要使用此注解,需要新增依赖javax.inject
.
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
使用同@Autowired,但是此注解不可以传参。
@Autowired不仅可以标注在属性上,还可以用在构造方法、方法、参数上。
如果组件只有一个有参构造器,这个参数上的@Autowired可以省略。
public PersonDao(Dog dog) { //参数还是可以自动装配成功 this.dog = dog; } //和如下代码一致 public PersonDao(@Autowired Dog dog) { this.dog = dog; } // 或者 @Autowired public PersonDao( Dog dog) { this.dog = dog; }
Aware注入
自定义组件想要使用spring容器底层的一些组件(ApplicationContext,BeanFactory,XXX),只需自定义组件实现XXXAware;
在创建对象的时候,会调用接口规定的方法。
@Profile
spring给我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能。
例如:数据源(开发环境——开发库A、测试环境——测试库B、生产环境——正式库C)
1. 添加mysql和C3P0依赖
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
2.配置类
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/*
根据不同的环境切换不同的数据源
*/
@Configuration
public class ConfigOfProfile {
@Bean
public DataSource dataSourceDev() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("thinkon123");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
@Bean
public DataSource dataSourceTest() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("thinkon123");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/sakila");
return dataSource;
}
@Bean
public DataSource dataSourceProd() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("thinkon123");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/creditacess");
return dataSource;
}
}
@Profile:指定哪个组件在哪个环境下才能被注册到容器中,不指定,任何环境下都会注册组件。
加了环境标识的bean,只有这个环境被激活才能注册到容器中。
@Profile("dev")
@Bean
public DataSource dataSourceDev() throws Exception
@Profile("test")
@Bean
public DataSource dataSourceTest() throws Exception
@Profile("prod")
@Bean
public DataSource dataSourceProd() throws Exception
以上添加环境标识后,都不会被注册到容器中。默认的环境标识是default
.
3.激活环境
方式一:使用命令行动态设置
在虚拟机参数位置添加-Dspring.profiles.active=test
即可激活test环境,dev、prod同理
方式二:代码设置
@Test
void testProfile2() {
// 1.创建无参applicationContext
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2.设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev","test"); //开发和测试环境被激活
// 3.注册主配置类
applicationContext.register(ConfigOfProfile.class);
// 4.启动刷新容器
applicationContext.refresh();
/*
* 以上的1、3、4 步其实就是有参applicationContext方法中所做的;
* 我们就是设置了第二步的环境
* */
String[] beans = applicationContext.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
@Profile不仅可以添加在方法上,还可以添加在配置类上,当在类上添加此注解时,就说明只有profile所指定的环境被激活,整个配置类才生效。
- POJO上也可以添加@Profile注解。
没有标注任何环境标识的类,在任何环境下都是会被加载的