springBoot中bean的注册方式
springboot加载bean的5种方式
方式一:配置文件+<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"> <!--xml方式声明自己开发的bean--> <bean id="cat" class="Cat"/> <bean class="Dog"/> <!--xml方式声明第三方开发的bean--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/> <bean class="com.alibaba.druid.pool.DruidDataSource"/> <bean class="com.alibaba.druid.pool.DruidDataSource"/> </beans>
方式二:配置文件扫描+注解定义bean
由于方式一种需要将spring管控的bean全部写在xml文件中,对于程序员来说非常不友好,所以就有了第二种方式。哪一个类要受到spring管控加载成bean,就在这个类的上面加一个注解,还可以顺带起一个bean的名字(id)。这里可以使用的注解有@Component以及三个衍生注解@Service、@Controller、@Repository。
@Component("tom") public class Cat { }
@Service public class Mouse { }
@Component public class DbConfig { @Bean public DruidDataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); return ds; } }
上面提供的仅仅是bean的声明,spring并没有感知到这些东西,可以通过下列xml配置设置spring去检查哪些包,发现定了对应注解,就将对应的类纳入spring管控范围,声明成bean。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!--指定扫描加载bean的位置--> <context:component-scan base-package="com.itheima.bean,com.itheima.config"/> </beans>
方式二声明bean的方式是目前企业中较为常见的bean的声明方式,但是也有缺点。方式一中,通过一个配置文件,你可以查阅当前spring环境中定义了多少个或者说多少种bean,但是方式二没有任何一个地方可以查阅整体信息,只有当程序运行起来才能感知到加载了多少个bean。
方式三:注解方式声明配置类
@ComponentScan({"com.itheima.bean","com.itheima.config"}) public class SpringConfig3 { @Bean public DogFactoryBean dog(){ return new DogFactoryBean(); } }
使用FactroyBean接口
补充一个小知识,spring提供了一个接口FactoryBean,也可以用于声明bean,只不过实现了FactoryBean接口的类造出来的对象不是当前类的对象,而是FactoryBean接口泛型指定类型的对象。如下列,造出来的bean并不是DogFactoryBean,而是Dog。有什么用呢?可以在对象初始化前做一些事情,下例中的注释位置就是让你自己去扩展要做的其他事情的。
public class DogFactoryBean implements FactoryBean<Dog> { @Override public Dog getObject() throws Exception { Dog d = new Dog(); //......... return d; } @Override public Class<?> getObjectType() { return Dog.class; } @Override public boolean isSingleton() { return true; } }
有人说,注释中的代码写入Dog的构造方法不就行了吗?干嘛这么费劲转一圈,还写个类,还要实现接口,多麻烦啊。还真不一样,你可以理解为Dog是一个抽象后剥离的特别干净的模型,但是实际使用的时候必须进行一系列的初始化动作。只不过根据情况不同,初始化动作不同而已。如果写入Dog,或许初始化动作A当前并不能满足你的需要,这个时候你就要做一个DogB的方案了。然后,就没有然后了,你就要做两个Dog类。当时使用FactoryBean接口就可以完美解决这个问题。
通常实现了FactoryBean接口的类使用@Bean的形式进行加载,当然你也可以使用@Component去声明DogFactoryBean,只要被扫描加载到即可,但是这种格式加载总觉得怪怪的,指向性不是很明确。
@ComponentScan({"com.itheima.bean","com.itheima.config"}) public class SpringConfig3 { @Bean public DogFactoryBean dog(){ return new DogFactoryBean(); } }
注解格式导入XML格式配置的bean
再补充一个小知识,由于早起开发的系统大部分都是采用xml的形式配置bean,现在的企业级开发基本上不用这种模式了。但是如果你特别幸运,需要基于之前的系统进行二次开发,这就尴尬了。新开发的用注解格式,之前开发的是xml格式。这个时候可不是让你选择用哪种模式的,而是两种要同时使用。spring提供了一个注解可以解决这个问题,@ImportResource,在配置类上直接写上要被融合的xml配置文件名即可,算的上一种兼容性解决方案,没啥实际意义。
@Configuration @ImportResource("applicationContext1.xml") public class SpringConfig32 { }
proxyBeanMethods属性
前面的例子中用到了@Configuration这个注解,当我们使用AnnotationConfigApplicationContext加载配置类的时候,配置类可以不添加这个注解。但是这个注解有一个更加强大的功能,它可以保障配置类中使用方法创建的bean的唯一性。为@Configuration注解设置proxyBeanMethods属性值为true即可,由于此属性默认值为true,所以很少看见明确书写的,除非想放弃此功能。
@Configuration(proxyBeanMethods = true) public class SpringConfig33 { @Bean public Cat cat(){ return new Cat(); } }
下面通过容器再调用上面的cat方法时,得到的就是同一个对象了。注意,必须使用spring容器对象调用此方法才有保持bean唯一性的特性。此特性在很多底层源码中有应用,
public class App33 { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig33.class); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } System.out.println("-------------------------"); SpringConfig33 springConfig33 = ctx.getBean("springConfig33", SpringConfig33.class); System.out.println(springConfig33.cat()); System.out.println(springConfig33.cat()); System.out.println(springConfig33.cat()); } }
方式四:使用@Import注解注入bean
@Import({Dog.class,DbConfig.class}) public class SpringConfig4 { }
使用@Import注解注入配置类
除了加载bean,还可以使用@Import注解加载配 置类。其实本质上是一样的,不解释太多了。
@Import(DogFactoryBean.class) public class SpringConfig4 { }
方式五:编程形式注册bean
前面介绍的加载bean的方式都是在容器启动阶段完成bean的加载,下面这种方式就比较特殊了,可以在容器初始化完成后手动加载bean。通过这种方式可以实现编程式控制bean的加载。
public class App5 { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); //上下文容器对象已经初始化完毕后,手工加载bean ctx.register(Mouse.class); } }
其实这种方式坑还是挺多的,比如容器中已经有了某种类型的bean,再加载会不会覆盖呢?这都是要思考和关注的问题。新手慎用。
public class App5 { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); //上下文容器对象已经初始化完毕后,手工加载bean ctx.registerBean("tom", Cat.class,0); ctx.registerBean("tom", Cat.class,1); ctx.registerBean("tom", Cat.class,2); System.out.println(ctx.getBean(Cat.class)); } }
方式六:导入实现了ImportSelector接口的类
public class MyImportSelector implements ImportSelector { @Override //medata 元数据 public String[] selectImports(AnnotationMetadata metadata) { //各种条件的判定,判定完毕后,决定是否装载指定的bean boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration"); if(flag){ return new String[]{"com.itheima.bean.Dog"}; } return new String[]{"com.itheima.bean.Cat"}; } }
方式七:导入实现了ImportBeanDefinitionRegistrar接口的类
public class MyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //BeanDefinition创建bean的 //创建一个BeanDefinition对象, BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition(); //设置是否单例 beanDefinition.setScope(); //使用registry注册bean; //参数一 bean的名字 registry.registerBeanDefinition("bookService",beanDefinition); } }
方式八:导入实现了BeanDefinitionRegistryPostProcessor接口的类
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition(); registry.registerBeanDefinition("bookService",beanDefinition); } }
总体上来说,上面介绍了各种各样的bean的注册加载初始化方式,脑子里建立个概念吧,方式很多,spring源码中大量运用各种方式。
总结
-
bean的定义由前期xml配置逐步演化成注解配置,本质是一样的,都是通过反射机制加载类名后创建对象,对象就是spring管控的bean
-
@Import注解可以指定加载某一个类作为spring管控的bean,如果被加载的类中还具有@Bean相关的定义,会被一同加载
-
spring开放出了若干种可编程控制的bean的初始化方式,通过分支语句由固定的加载bean转成了可以选择bean是否加载或者选择加载哪一种bean
更多推荐
所有评论(0)