spring bean生命周期回调
官网描述
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-lifecycle
一共三种方式
- 在回调方法上加
@PostConstruct
或@PreDestroy
注解 - 实现
InitializingBean
或DisposableBean
接口中的回调方法 - 在xml配置文件中配置bean的时候加上
init-method
或destroy-method
属性
方法一(注解)
在初始化的回调方法上加@PostConstruct
注解,在bean销毁时的回调方法上加@PreDestroy
注解
@PostConstruct
public void init(){
System.out.println("DemoServiceImpl初始化");
}
@PreDestroy
public void destroy(){
System.out.println("DemoServiceImpl被销毁了");
}
方法二(接口)
bean初始化的回调,实现下面的接口
bean销毁的回调,实现下面的接口
这种方法需要注入spring的接口,侵入性太强,不建议使用
public class DemoServiceImpl implements DemoService, InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
}
@Override
public void afterPropertiesSet() throws Exception {
}
方法三(xml)
在配置文件中的bean中定义init-method
属性,方法名可以自定义,初始化回调
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
销毁回调
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
单例中的多例问题
如果在单例的类中注入了一个多例的类,那么多例的类并不会因为第二次得到单例的类而产生新的对象
这样就在单例的类中注入了一个多例的类
@Repository
@Scope("prototype")
public class DemoDaoImpl1 implements DemoDao{
@Override
public void test() {
System.out.println("zzz1");
}
}
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoDao demoDaoImpl1;
@Override
public void test() {
demoDaoImpl1.test();
System.out.println("dao"+demoDaoImpl1.hashCode());
System.out.println("service"+this.hashCode());
}
}
获得三次
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(Springcfg.class);
DemoService demoService= (DemoService) annotationConfigApplicationContext.getBean("demoServiceImpl");
demoService.test();
DemoService demoService1= (DemoService) annotationConfigApplicationContext.getBean("demoServiceImpl");
demoService1.test();
DemoService demoService2= (DemoService) annotationConfigApplicationContext.getBean("demoServiceImpl");
demoService2.test();
}
下面是结果
zzz1
dao1800659519
service1691538257
zzz1
dao1800659519
service1691538257
zzz1
dao1800659519
service1691538257
可以看出,DemoDao依然是同一个对象,并没有体现出多例表现
那么如果想要每一次都能获得不一样的对象,必须对DemoServiceImpl做一些处理
可以改变注入的方式,这样每次拿都是重新去创建一个对象,@Lookup
注解也是先根据返回值的类型注入的,所以如果只有一个实现类就可以无需关心括号内的值,但是如果有两个实现类,那么就会根据括号内的值找到相同类名的实现类注入
@Service
public abstract class DemoServiceImpl implements DemoService {
@Lookup("demoDaoImpl")
protected abstract DemoDao createDemoDao();
@Override
public void test() {
System.out.println("dao"+createDemoDao().hashCode());
System.out.println("service"+this.hashCode());
}
}
@Autowired和@Resource的区别
- @Autowired默认是根据类型注入的,但如果有相同的实现类,那么会根据属性名来注入
- @Resource默认是根据属性名注入的
依赖注入时的重复依赖问题
如果依赖注入时注入的依赖同时有两个实现类,这种情况下就会出现问题
@Autowired
private DemoDao demoDao;
解决办法如下
@Primary
在需要注入的实现上加注解@Primary
@Primary
public class DemoDaoImpl implements DemoDao{
@Qualifier
在注入时加注解@Qualifier(“需要注入的实现类名”)
@Autowired
@Qualifier("demoDaoImpl")
private DemoDao demoDao;
@Profile
@Profile可以加在类的上面,设置一个名字,这样在初始化容器时注册其中一个,这样另外的实现就不会生成bean
@Profile("demoDaoImpl")
public class DemoDaoImpl implements DemoDao{
在配置时要激活要注入的类,其他的实现类会直接忽略不会生成bean
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext();
//激活要注入的类
annotationConfigApplicationContext.getEnvironment().setActiveProfiles("demoDaoImpl");
//注册spring配置类
annotationConfigApplicationContext.register(Springcfg.class);
//重新刷新配置,这样才能实现配置
annotationConfigApplicationContext.refresh();
DemoService demoService= (DemoService) annotationConfigApplicationContext.getBean("demoServiceImpl");
demoService.test();
}
加快bean初始化的速度
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.1.9.RELEASE</version>
<optional>true</optional>
</dependency>
如果加上这个依赖就可以更快地生成bean(适合需要加载很多bean的场景),原理是在编译时会生成bean目录的索引,这样spring在扫描的时候就可以更快找到生成bean的类
@Bean
用注解和javaConfig的方式配置bean
如果在配置bean的时候要注入属性,那么需要在方法上加@AutoWired注解,但是spring5之后可以不加
在javaConfig类中配置了mybatis的相关参数
@Configuration
@ComponentScan("com.zdd")
public class Springcfg {
@Bean
@Autowired
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
return sqlSessionFactory;
}
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306//mysql01");
dataSource.setDriverClassName("com.mysql.jdbc.Dirver");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
bean中的循环引用
分几种情况
-
两者都是单例的
因为有缓冲区
先把baen创建出来放在缓存中,需要引用的时候拿出来 -
两者都是多例
因为没有缓存,每一个都是原型,所以会在引用时反复创建,这样就会造成溢出泄露问题,时不允许的 -
一个单例和一个多例同两者都是单例
原因在上面中的单例中引用了多例的情况