Spring总结 1.装配bean
本随笔内容要点如下:
- 依赖注入
- Spring装配bean的方式
- 条件化装配
一、依赖注入
我理解的依赖注入是这样的:所谓的依赖,就是对象所依赖的其他对象。Spring提供了一个bean容器,它负责创建应用程序中的bean并通过依赖注入来协调这些对象之间的关系,实现自动创建与管理所创建的对象。我们只需要通过配置来命令Spring提供的容器来完成bean的装配。
二、Spring装配bean的方式
在Spring中,装配bean有三种方式:
- 通过xml进行配置
- 通过Java进行配置
- 通过注解来自动扫描并配置
先使用xml进行举例:
1 package cn.powerfully.domain; 2 3 public class ABean { 4 private String str; 5 private double num; 6 7 public ABean() { 8 } 9 10 public ABean(String str, double num) { 11 this.str = str; 12 this.num = num; 13 } 14 15 public String getStr() { 16 return str; 17 } 18 19 public void setStr(String str) { 20 this.str = str; 21 } 22 23 public double getNum() { 24 return num; 25 } 26 27 public void setNum(double num) { 28 this.num = num; 29 } 30 }
在xml中配置如下
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 8 <bean id="aBean" class="cn.powerfully.domain.ABean"> 9 <constructor-arg name="str" value="bean"/> 10 <constructor-arg name="num" value="3.1415926"/> 11 </bean> 12 13 </beans>
使用bean标签来表示装载一个bean到Spring容器,默认该bean为单例,即容器中只有一个该类对象。对于字段的注入,上面使用了构造器注入,Spring还支持setter注入,通过<property>标签进行注入,具体的可以参考帮助文档。为了简化,spring提供了c命名空间和p命名空间,用来简化构造器注入和setter注入,上面的例子可以改编成如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- <bean id="aBean" class="cn.powerfully.domain.ABean" p:str="p namespace" p:num="3.14" /> --> <bean id="aBean" class="cn.powerfully.domain.ABean" c:str="c namespace" c:num="3.14" /> </beans>
其他基本类型和上面的例子一样,直接编写即可。对于注入对象与集合,将有所不同:
1 package cn.powerfully.domain; 2 3 import java.util.List; 4 import java.util.Map; 5 import java.util.Properties; 6 7 public class BBean { 8 9 private ABean aBean; 10 private List<String> list; 11 private Map<String, Integer> map; 12 private Properties props; 13 14 public ABean getaBean() { 15 return aBean; 16 } 17 18 public void setaBean(ABean aBean) { 19 this.aBean = aBean; 20 } 21 22 public List<String> getList() { 23 return list; 24 } 25 26 public void setList(List<String> list) { 27 this.list = list; 28 } 29 30 public Map<String, Integer> getMap() { 31 return map; 32 } 33 34 public void setMap(Map<String, Integer> map) { 35 this.map = map; 36 } 37 38 public Properties getProps() { 39 return props; 40 } 41 42 public void setProps(Properties props) { 43 this.props = props; 44 } 45 46 }
实例化该类时,需要注入对象、List、Map、Properties,xml配置如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:c="http://www.springframework.org/schema/c" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd"> 8 9 <!-- <bean id="aBean" class="cn.powerfully.domain.ABean" p:str="p namespace" p:num="3.14" /> --> 10 <bean id="aBean" class="cn.powerfully.domain.ABean" c:str="c namespace" c:num="3.14" /> 11 12 <bean id="bBean" class="cn.powerfully.domain.BBean" p:aBean-ref="aBean"> 13 <!-- <property name="aBean" ref="aBean" /> --> 14 <property name="list"> 15 <list> 16 <value>item1</value> 17 <value>item2</value> 18 </list> 19 </property> 20 <property name="map"> 21 <map> 22 <entry key="key1" value="1" /> 23 <entry key="key2" value="2" /> 24 </map> 25 </property> 26 <property name="props"> 27 <props> 28 <prop key="key1">1</prop> 29 <prop key="key2">2</prop> 30 </props> 31 </property> 32 </bean> 33 34 </beans>
如果想注入对象,则需要使用ref属性而不是value属性,ref属性值为bean对象的id。特别注意的是,如果使用p或c命名空间,其格式为c:propertyName-ref。常见的集合注入如上配置。关于具体的配置参见帮助文档,毕竟本随笔只是我的知识点简要回忆。
接着是Java配置,个人觉得Java配置会比xml配置来得更爽,它具有很强的灵活性。还是使用上面的例子,配置如下:
1 package cn.powerfully.config; 2 3 import java.util.Arrays; 4 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 8 import cn.powerfully.domain.ABean; 9 import cn.powerfully.domain.BBean; 10 11 @Configuration 12 public class BeanConfig { 13 14 @Bean 15 public ABean aBean() { 16 return new ABean("java configura", 3.1415); 17 } 18 19 @Bean 20 public BBean bBean(ABean aBean) { 21 BBean bean = new BBean(); 22 bean.setaBean(aBean); 23 bean.setList(Arrays.asList("item1", "item2")); 24 return bean; 25 } 26 27 }
通过注解@Configuration来表明该类是配置类,通过@Bean来表示该方法返回结果将放入容器,而方法返回结果即实例对象需要自己创建,其中在容器中bean的id为方法名。对于bBean,需要注入aBean对象,只需要在方法中填入参数,接着在代码里面直接引用即可。
最后,回忆下注解。一般用到的注解为@Component与@Autowired。@Component标记在类上,表示该类需要被实例化并由Spring容器管理。@Autowired表示需要在Spring容器中查找符合的bean来设置到当前属性里,该注解可以用在字段、构造器、setter上。同时,还要开启自动扫描才能实现。
三、条件化装配
有时,我们需要动态地来装配bean。当满足一定条件时我们才需要装配这个bean。Spring4之后,注解@Conditional可以实现这个功能。
1 @Conditional(UserCondition.class) 2 public class User {} 3 4 public class UserCondition implements Condition { 5 @Override 6 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 7 return false; 8 } 9 }
当UserCondition中matches方法返回真时,才会创建User对象。关于该接口的具体使用请参考另一篇博文(还没写~.~)。
其中@Profile是其中的一个特例,它也是使用@Conditonal来实现的。配置完该注解后,我们一开始可以通过设置profile类型来管理需要创建哪一系列bean。例如,在Web开发中,我们所选择的数据库可能根据运行时期的不同而不同。例如在开发环境,我们可能使用c3p0、dbcp等作为数据源。而在生产环境中,我们可能使用JNDI数据源。这是我们就可以使用profile了。我们先为不同的配置设置不同的profile,然后再使用时设置profile就行了。具体的还是参考另一篇博文。>_<
简单例子如下:
定义交通工具接口
1 package cn.powerfully.domain; 2 3 public interface Vehicle {}
定义车与飞机类并实现Vehicle接口:
1 package cn.powerfully.domain; 2 3 import org.springframework.context.annotation.Profile; 4 import org.springframework.stereotype.Component; 5 6 @Component 7 @Profile("car") 8 public class Car implements Vehicle { 9 @Override 10 public String toString() { 11 return "Car"; 12 } 13 }
1 package cn.powerfully.domain; 2 3 import org.springframework.context.annotation.Profile; 4 import org.springframework.stereotype.Component; 5 6 @Component 7 @Profile("plane") 8 public class Plane implements Vehicle { 9 @Override 10 public String toString() { 11 return "Plane"; 12 } 13 }
创建配置类
1 package cn.powerfully.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 6 @Configuration 7 @ComponentScan(basePackages = "cn.powerfully.domain") 8 public class VehicleConfig {}
JUnit测试
1 package cn.powerfully.test; 2 3 import org.junit.Test; 4 import org.junit.runner.RunWith; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.test.context.ActiveProfiles; 7 import org.springframework.test.context.ContextConfiguration; 8 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 10 import cn.powerfully.config.VehicleConfig; 11 import cn.powerfully.domain.Vehicle; 12 13 @RunWith(SpringJUnit4ClassRunner.class) 14 @ContextConfiguration(classes=VehicleConfig.class) 15 @ActiveProfiles("car") 16 public class Demo { 17 @Autowired 18 private Vehicle vehicle; 19 20 @Test 21 public void test(){ 22 System.out.println(vehicle); 23 } 24 }
当选用了car时,输出Car,选择plane时,输出了Plane...当然也可以使用xml配置:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd"> 7 8 <beans profile="car"> 9 <bean id="vehicle" class="cn.powerfully.domain.Car" /> 10 </beans> 11 12 <beans profile="plane"> 13 <bean id="vehicle" class="cn.powerfully.domain.Plane" /> 14 </beans> 15 16 17 </beans>
注意profile属性是位与beans标签上的。