一、定义Bean的5种方式:
声明式:
- <bean>标签 通过XML文件配置
- @Bean注解 创建配置类配合@Configuration使用
- @Component注解 配合@ComponentScan注解使用
编程式: - BeanDefinition接口
- FactoryBean接口
Spring中定义Bean的方式可以分为两类,分别是声明式和编程式。顾名思义,声明式可以理解为用一个的标记去标识相关的信息,代码底层再对这些标识符进行一个解析处理;编程式则更多侧重于使用代码的形式去处理相关逻辑。前者使用更友好,后者更底层。
1、通过<Bean>标签定义
创建类
public class Person {
private String name;
}
配置XML文件
<bean id="person" class="com.beanstudy.pojo.Person"></bean>
获取Bean
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
Person person =(Person) applicationContext.getBean("person");
System.out.println(person);
2、通过@Bean注解
Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。
在配置类里面,使用@Bean注解。类上要使用@Configuration注解,当然你不用也可以,只是我们希望配置类能有一个属于它的专属标识符.
beanName默认为@Bean修饰的方法名,可以通过@Bean(name="user")更改
@ComponentScan("com.beanstudy.pojo")
@Configuration
public class ConfigClass {
@Bean
public User getUser(){
return new User();
}
}
获取bean
AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext(ConfigClass.class);
User user = (User) applicationContext1.getBean("getUser");
System.out.println(user);
3、通过@Component注解定义
与<Bean>标签和@Bean注解的作用范围不同,该方式可批量向Spring中注入Bean,多用于controller、service包下类,前者更多的运用在单个实体类的定制化处理。
创建类:
public class Person {
private String name;
}
创建配置类
@ComponentScan("com.beanstudy.pojo")
@Configuration
public class ConfigClass {
}
获取Bean
AnnotationConfigApplicationContext applicationContext1=new AnnotationConfigApplicationContext(ConfigClass.class);
User user = (User)applicationContext1.getBean("user");
System.out.println(user);
4、通过BeanDefinition接口定义
该方式在启动的时候就可以不需要任何的配置文件。当我们在使用声明式方式配置Bean时,其底层都是使用的该方式进行配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 创建一个beanDefinition,并设置对应的beanDefinition对象的bean的类型
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(MoBian.class);
// 将beanDefinition注册到Spring容器中,并设置对应的Bean的名字
context.registerBeanDefinition("mobian",beanDefinition);
context.refresh();
System.out.println(context.getBean("mobian"));
5、通过实现FactoryBean接口定义
使用该方式会生成两个Bean对象,一个是getObject方法中返回对象对应的类生成的Bean,一个是实现了FactoryBean接口的类生成的Bean。我们注册到Spring容器中的Bean是实现了FactoryBean接口的类生成的Bean,但是我们在获取实现了FactoryBean接口的类的Bean时,却需要使用&+注册BeanDefinition时的名字,而用注册BeanDefinition时使用的名字获取到的则是getObject方法返回的对象。
创建对象
public class Car {
}
创建实现接口的类
public class FlippedFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new Car();
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
}
获取Bean
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 此处我们还是采用编程式的方式定义Bean,即第四种方式
// 当然,此处也可以将该Bean使用声明式的方式定义
AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
definition.setBeanClass(FlippedFactoryBean.class);
context.registerBeanDefinition("car",definition);
context.refresh();
// 获取getObject方法返回的对象
System.out.println(context.getBean("car"));
// 获取注册到Spring容器的beanDefinition的名字
System.out.println(context.getBean("&car"));
}
}
二、Bean实例化的三种方式
-
构造器实例化
spring容器通过bean对应的默认的构造函数来实例化bean。
注意:Spring默认使用类的无参构造方法进行实例化,但是没有无参构造方法时需要进行构造方法的推断:
public class User{
private String name;
}
构造方法能通过@Autowired 利用有参构造进行实例化的前提:
@ComponentScan("xxx")
public class Config{
@Bean
private User user()
{return new User();}
}
@Component
public class UserService {
private User user;
public UserService(User user) {
this.user = user;
System.out.println("一个参数的构造方法");
}
}
1、声明一个构造方法,则使用该构造方法
@Component
public class UserService {
private User user;
public UserService(User user) {
this.user = user;
System.out.println("一个参数的构造方法");
}
}
2、没有构造方法,则默认使用无参的
3、有多个构造方法
@Component
public class UserService {
private User user;
public UserService(User user) {
this.user = user;
System.out.println("一个参数的构造方法");
}
public UserService(User user, User user1) {
this.user = user;
System.out.println("2个参数的构造方法");
}
}
启动报错,因为spring此时不知道该用哪个构造方法,解决方法:
①加入无参的构造方法,spring会使用无参构造方法
②给其中一个构造方法加上@Autowired注解(默认required=true)
若2个都加上@Autowired注解,则也会报错
若2个都加上@Autowired(required=false)
则表示告诉spring这2个构造方法都可以用,这时候spring的选择逻辑:
首先看参数多的构造方法是否可用,2个参数的构造方法需要注入2个User类型的Bean,当容器中只有一个User类型的Bean时,可以根据类型注入2个参数,因此spring 判定2个参数的构造方法可用,优先选择该构造方法;
假如在容器中再增加一个User类型的Bean:
public class Config{
@Bean
private User user(){
return new User();
}
@Bean
private User user2(){
return new User();
}}
则此时spring除了根据类型匹配,还会根据名称匹配,在匹配第二个参数时发现不存在类型为User名称为user1的Bean,因此spring 判定2个参数的构造方法不可用,选择一个参数的构造方法;
假如有多个可用且参数长度一样的构造方法,则spring回选择写在前面的构造方法
-
静态工厂方式实例化
首先创建一个静态工厂类,在类中定义一个静态方法创建实例。 -
实例工厂方式实例化
该种方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时在配置文件中,需要实例化的Bean也不是通过class属性直接指向其实例化的类,而是通过factory-bean属性配置一个实例工厂,然后使用factory-method属性确定使用工厂中哪个方法。
转载:
Spring中定义Bean的6种方式(声明式+编程式)
Bean实例化的三种方式
Bean实例化的三种方式
推断构造方法