默认状况下,Spring中的bean都是以单例的形式存在的,不管注入多少次,每次注入的都是同一个实例。java
考虑到某些bean多是可变的,Spring定义了不一样的做用域,能够基于这些做用域建立不一样的bean,mysql
单例是默认的做用域,若是选择@Scope
注解选择其余做用域,这能够和@Component
和@Bean
一块儿使用。git
@Configuration public class Cap3MainConfig { //给容器中注册一个bean, 类型为返回值的类型, 默认是单实例 /* * prototype:多实例: IOC容器启动的时候,IOC容器启动并不会去调用方法建立对象, 而是每次获取的时候才会调用方法建立对象 * singleton:单实例(默认):IOC容器启动的时候会调用方法建立对象并放到IOC容器中,之后每次获取的就是直接从容器中拿(大Map.get)的同一个bean * request: 主要针对web应用, 递交一次请求建立一个实例 * session:同一个session建立一个实例 */ @Scope("prototype") @Bean public Person person(){ return new Person("vincent",20); } }
顾名思义,懒加载推迟加载Bean。默认状况下,在IOC容器初始化时,会将各个Bean注册到容器中;若是在定义Bean时,使用@Lazy
声明,则该Bean只有在第一次使用时,才会被注册到IOC容器中。github
下面的例子中,person实例将会在第一次被获取的时候才会初始化。web
@Configuration public class Cap4MainConfig { //给容器中注册一个bean, 类型为返回值的类型, 默认是单实例 /* * 懒加载: 主要针对单实例bean:默认在容器启动的时候建立对象 * 懒加载: 容器启动时候不建立对象, 仅当第一次使用(获取)bean的时候才建立被初始化 */ @Lazy @Bean public Person person(){ System.out.println("给容器中添加person......."); return new Person("vincent",20); } }
可使用以下测试程序进行测试:面试
public class Cap4Test { @Test public void test01(){ AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap4MainConfig.class); String[] names = app.getBeanDefinitionNames(); // 此时能够获取到person的name,可是person依然未实例化 for(String name:names){ System.out.println(name); } System.out.println("IOC容器建立完成........"); // 实例化person app.getBean("person");//执行获取的时候才建立并初始化bean } }
Spring4引入了@Conditional
注解,用于条件化注册Bean。若是给定的条件,计算结果为true,就会建立这个bean,不然的话,bean会被忽略。spring
下面的例子中,将IOC容器注册bean时, 当操做系统为WINDOWS时,注册Lison实例; 当操做系统为LINUX时, 注册James实例,此时要用得@Conditional注解进行定制化条件选择注册bean;sql
@Configuration public class Cap5MainConfig { @Bean("person") public Person person(){ System.out.println("给容器中添加person......."); return new Person("person",20); } @Conditional(WinCondition.class) @Bean("lison") public Person lison(){ System.out.println("给容器中添加win......."); return new Person("win",58); } @Conditional(LinCondition.class) @Bean("james")//bean在容器中的ID为james, IOC容器MAP, map.put("id",value) public Person james(){ System.out.println("给容器中添加mac......."); return new Person("mac",20); } }
注意到,咱们须要本身实现对应的WinCondition.class
和LinCondition.class
类,以其中的一个为例,以下能够看到,须要实现本身的match函数,后端
public class LinCondition implements Condition{ /* *ConditionContext: 判断条件可使用的上下文(环境) *AnnotatedTypeMetadata: 注解的信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // TODO 是否为WINDOW系统 //能获取到IOC容器正在使用的beanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //获取当前环境变量(包括咱们操做系统是WIN仍是LINUX??) Environment environment = context.getEnvironment(); String os_name = environment.getProperty("os.name"); if(os_name.contains("Mac")){ return true; } return false; } }
这节中,首先总结一下Spring中常见的注入Bean的方法。设计模式
前两种方法在上一章已经介绍了,如今主要介绍剩下两类。
下面的配置类中,直接将Dog和Cat import到配置中,自己配置类中也定义了person的实例bean以及自定义的factoryBean。
@Configuration @Import(value = { Dog.class,Cat.class, JamesImportSelector.class, JamesImportBeanDefinitionRegistrar.class }) public class Cap6MainConfig { //容器启动时初始化person的bean实例 @Bean("person") public Person persond(){ System.out.println("aaaaaaaaaaaa"); return new Person("james",20); } @Bean public JamesFactoryBean jamesFactoryBean(){ return new JamesFactoryBean(); } }
在JamesImportSelector.class
实现中,只须要返回全部须要import的class类名便可。
public class JamesImportSelector implements ImportSelector{ @Override public String[] selectImports(AnnotationMetadata importingClassMetadata){ //返回全类名的bean return new String[]{"com.enjoy.cap6.bean.Fish","com.enjoy.cap6.bean.Tiger"}; } }
在JamesImportBeanDefinitionRegistrar.class
中,根据须要能够手动注入须要的bean实例,
public class JamesImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /* *AnnotationMetadata:当前类的注解信息 *BeanDefinitionRegistry:BeanDefinition注册类 * 把全部须要添加到容器中的bean加入; * @Scope */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean bean1 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Dog"); boolean bean2 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Cat"); //若是Dog和Cat同时存在于咱们IOC容器中,那么建立Pig类, 加入到容器 //对于咱们要注册的bean, 给bean进行封装, if(bean1 && bean2){ RootBeanDefinition beanDefinition = new RootBeanDefinition(Pig.class); registry.registerBeanDefinition("pig", beanDefinition); } } }
注意到上面,也能够经过FactoryBean的方法来将所须要的bean注入到IOC容器中,在这其中,须要手动实现其中的getObject等方法。
public class JamesFactoryBean implements FactoryBean<Monkey>{ @Override public Monkey getObject() throws Exception { // TODO Auto-generated method stub return new Monkey(); } @Override public Class<?> getObjectType() { // TODO Auto-generated method stub return Monkey.class; } @Override public boolean isSingleton() { return true; } }
下面是实际的测试程序,须要注意的是,直接使用getBean(bean name)是取出FactoryBean里面封装的Monkey实例,若是须要拿到FactoryBean自己,须要加上&
符号。
public class Cap6Test { @Test public void test01(){ AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap6MainConfig.class); System.out.println("IOC容器建立完成........"); Object bean1 = app.getBean("jamesFactoryBean"); Object bean2 = app.getBean("jamesFactoryBean");//取Monkey bean System.out.println("bean的类型="+bean1.getClass()); System.out.println(bean1 == bean2); Object bean3 = app.getBean("&jamesFactoryBean");//取factoryBean System.out.println("bean的类型="+bean3.getClass()); // 打印输出全部bean String[] beanDefinitionNames = app.getBeanDefinitionNames(); for(String name:beanDefinitionNames){ System.out.println(name); } } }
Spring中出现了BeanFactory和FactoryBean,下面对二者的区别进行解释:
BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,全部的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
FactoryBean是一个Bean,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式相似。
1. BeanFactory
BeanFactory,以Factory结尾,表示它是一个工厂类(接口),它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及创建这些对象间的依赖。
BeanFactory只是个接口,并非IOC容器的具体实现,可是Spring容器给出了不少种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是经常使用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个彻底可配置的系统或应用。
ApplicationContext包含BeanFactory的全部功能,一般建议比BeanFactory优先 。ApplicationContext以一种更向面向框架的方式工做以及对上下文进行分层和实现继承,ApplicationContext包还提供了如下的功能:
- MessageSource, 提供国际化的消息访问
- 资源访问,如URL和文件
- 事件传播
- 载入多个(有继承关系)上下文 ,使得每个上下文都专一于一个特定的层次,好比应用的web层;
BeanFactory提供的方法及其简单,仅提供了六种方法供客户调用:
2. FactoryBean
通常状况下,Spring经过反射机制利用
Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户能够经过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来讲占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改成FactoryBean
以Bean结尾,表示它是一个Bean,不一样于普通Bean的是:它是实现了FactoryBean
本节介绍Spring在运行时的两种常见注入方式,@Value和@Autowired。
该注解的做用是将咱们配置文件的属性读出来,有@Value(“${}”)
和@Value(“#{}”)
两种方式。
1. @Value(“${}”):注入的是外部配置文件对应的property
在application.propertites配置属性以下:
在程序中动态读取server.port属性,
@w=300
这样server.port=8000就注入到了对应的参数中。
2. @Value(“#{}”):经常使用的方式是#{obj.property ? :default_value}
,注意与上一种方式不一样的是,这种方式中的obj须要是一个对象。也能够在其中填写SpEL表达式
。
Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”,相似于Struts2x中使用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等,而且能与Spring功能完美整合,如能用来配置Bean定义。
下面的例子中,首先定义UserBean并从property文件中读取属性,属性值为mysql。
@w=400
接着在另外一个Controller类中注入UserBean的属性。
@w=300
Spring中常利用@Autowired
完成依赖注入(DI), 对IOC容器中的各个组件的依赖关系赋值。
下面的例子中,是常见的DAO、Service、Controller模型,采用Autowired能够方便的在Service层和Controller层中注入对应的Bean实例。
@Autowired实现原理就是:默认优先按类型去容器中找对应的组件,至关于anno.getBean(TestDao.class)去容器获取id为testDao的bean, 并注入到TestService的bean中;
可是当容器中有多个testDao时,使用默认的@Autowired就会发生异常,IOC容器此时没法肯定哪一个bean做为依赖注入的对象,Spring引入了Qualifier和Primary来解决这个问题。
假定有两个testDao,其bean id分别为testDao1和testDao2,此时可使用@Autowired和@Qualifier
结合来指定注入哪个bean,下面的例子中,指定bean id为testDao,注意还能够加上required=false
当容器中找不到这个bean时,也不会报错,此时该对象注入失败为null。
若是不使用@Qualifier
,可使用@Primary
来指定默认的首选bean。此时经过getBean和autowired获取到的都是@Primary
指定的bean。
当@Qualifier
和@Primary
共存时,@Qualifier
会按照bean id来获取指定的bean,不会受到@Primary
的影响。此时使用getBean获取到的就是@Primary
标识的bean。
扩展:
@Resource和Autowired同样能够装配bean
@Resource缺点: 不能支持@Primary功能,不能支持@Autowired(required = false)的功能
@Inject和Autowired同样能够装配bean, 支持@Primary功能, 可用于非spring框架.
@Inject缺点: 但不能支持@Autowired(required = false)
的功能,须要引入第三方包javax.inject
@w=350
Autowired属于spring的, 不能脱离spring, @Resource和@Inject都是JAVA规范
推荐使用@Autowired。
@Component
主要和ComponentScan结合,用于自动检测和配置Bean,Bean和被注解的类是一一对应的关系。
@Bean
用于显式声明一个单独的Bean,而不是让Spring自动完成该过程,经过该注解能够将类的定义和Bean的声明解耦。特别是使用第三方的库时,只能经过@Bean来将某些类注入到容器中。
本文由『后端精进之路』原创,首发于博客 http://teckee.github.io/ , 转载请注明出处
搜索『后端精进之路』关注公众号,马上获取最新文章和价值2000元的BATJ精品面试课程。