上面的图是Spring的主要模块示意图,IoC属于Spring的Core模块。
使用Spring中各模块的功能时,首先要在xml中引入模块对应的命令空间。
1.IoC基本概念
IoC控制翻转,指的是对象的创建及对象生命周期的控制不再由程序本身完成,而是由IoC容器来完成,可以是Spring,也可是EJB。
由程序区创建对象,例如如下的代码:
PersonDao dao = new PersonDaoBean();
这样dao对象是依赖于代码本身的。IoC是指对象的创建通过Spring中创建,可以基于构造方法或set方法等,将dao的创建交给Spring,代码不再需要去new这样一个对象。
DI依赖注入,就是在运行期,由外部对象注入到组件中,常常和IoC是一起存在的。
2.Spring中的bean
Spring是通过bean来管理对象及实现Ioc的。
下面的是一个Java Bean的实例:
public class SpringBeanTest { private Action action; private String beanName; public String getBeanName() { return beanName; } public void setBeanName(String name) { this.beanName = name; } public Action getAction() { return action; } public void setAction(Action action) { this.action = action; } private void start(){ System.out.println("get resource for database"); } private void destroy(){ System.out.println("release resource for database"); } public void actionTest(){ System.out.println(action.execute("Kare Smith")); } }
为了管理这个bean,我们需要在一个xml文件中配置这个bean,以便Spring可以知道这个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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <bean id = "beanTest" class = "qs.SpringBeanTest" scope="singleton" init-method = "start" destroy-method = "destroy"> <!-- 指定属性参数 --> <property name = "beanName"> <value>hello</value> </property> <!-- 注入其他的bean --> <property name = "action"> <ref bean ="upper"/> </property> </bean> <bean id = "upper" class = "qs.UpperAction" scope="singleton"/> </beans>
上面的JavaBean中还用到了一个类UpperAction,其代码如下:
public class UpperAction implements Action {
public String execute(String str) {
//return (getMessage() + "," + str).toUpperCase() + " in upperAction";
return str.toUpperCase() + " in upperAction";
}
}
下面是对xml中bean的配置参数的解释:
bean的id属性在整个容器中时唯一的,这个是要注意的。name属性和id属性的不同点是,name属性中可以包含特殊字符,正常情况下还是应该使用id属性。
bean的作用域;singleton,prototype,还有对应于web的request,session,global session(application)。默认的是singleton,这是需要注意的,处理数据时bean使用默认的singleton是会出问题的。singleton的作用范围是Spring容器,在Spring容器之外是不能保证的。
singleton时Spring容器初始化时会初始bean,检查错误。prototype时getBean时才会去初始化对象。
lazy-init ="true",大体积且不一定会用到的bean适用,lazy-init尽量不使用。
bean的init-method方法用于打开,准备好程序所需资源。destroy-method用于资源的释放,需要调用AbstractApplicationContext的close方法。init-method和destroy-method都可以被Spring容器动态地调用。
下面是对这个bean的测试,看看Spring容器是否能够很好地管理这个bean。
public class SpringBeanTestTest2 { private ApplicationContext ctx; private SpringBeanTest beanTest; @Before public void setUp() throws Exception { ctx = new ClassPathXmlApplicationContext("bean.xml"); } @After public void tearDown() throws Exception { } @Test public void test() { beanTest = (SpringBeanTest) ctx.getBean("beanTest"); beanTest.actionTest(); } }
FileSystemXmlApplicationContext需要指定文件路径,适用性不强。ClassPathXmlApplicationContext会在类路径包括jar包中寻找xml文件,XmlWebApplicationContext用于读取web应用的xml文件并装载。
更为完善的测试代码应该如下:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/bean.xml") public class SpringBeanTestTest { @Autowired @Qualifier("beanTest") private SpringBeanTest beanTest; @Test public void test() { beanTest.actionTest(); } }
3.Spring中的注入
Spring中的注入分为基本类型的注入,如int,String等和对象的注入。基本类型的注入用的是value属性,对象的注入用的是ref属性。
Spring中的注入可以有三种方式:构造器注入,set方法注入,注解方式注入。
再看一个java bean的例子:
public class LowerAction { private String message; private List<Integer> testlist; private String [][] arrays; private Map<Integer, String> maps; private Action action; public Action getAction() { return action; } public void setAction(Action action) { this.action = action; } public Map<Integer, String> getMaps() { return maps; } public void setMaps(Map<Integer, String> maps) { this.maps = maps; } public String[][] getArrays() { return arrays; } public void setArrays(String[][] arrays) { this.arrays = arrays; } public List<Integer> getTestlist() { return testlist; } public void setTestlist(List<Integer> testlist) { this.testlist = testlist; } public String getMessage() { return message; } //java bean要求有一个无参的构造方法 public LowerAction(){ } public LowerAction(String arg){ System.out.println(arg); } public void setMessage(String message) { this.message = message; } @Override public String execute(String str) { return (getMessage() + "," + str).toLowerCase() + ", list is" + getTestlist() + ",arrays:" + arrays[1][1] + ",maps:" + maps.get(new Integer(1)) + "actions:" + action.execute("muu"); } }
在xml中完成注入的例子如下:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <bean id = "theAction" class = "qs.LowerAction" scope="singleton" > <!-- 指定属性参数 --> <property name = "message"> <value>hello</value> </property> <!-- 注入其他的bean --> <property name = "action"> <ref bean ="upper"/> </property> <!-- 指定构造器参数 --> <constructor-arg index = "0" value ="constructor test"/> <!-- 注入集合 --> <property name = "testlist"> <list value-type = "java.lang.Integer"> <value>1</value> <value>2</value> <value>3</value> </list> </property> <!-- 注入数组 --> <property name = "arrays"> <array> <array><value>1</value><value>2</value></array> <array><value>3</value><value>4</value></array> </array> </property> <!-- 注入字典 --> <property name = "maps"> <map key-type = "java.lang.Integer" value-type = "java.lang.String"> <entry key ="1" value ="2"/> </map> </property> </bean> </beans>
4.自动装配及自动检测
从上面可以看到,通过xml对bean的属性及引用进行装配,如果bean的数量很多,xml文件将会很大,虽然可以将xml文件分成多份,但依然难以维护。自动装配及自动检测就是为了减少xml中的配置信息。
自动装配autowiring,减少property属性和constructor-arg属性。
自动检测autodiscovery,让Spring识别哪些类需要被配置为bean,减少了bean属性。
4.1基于XML的自动装配
自动装配分为XML和注解2中方式。基于注解的支持以下注解:@Autowired,@Inject,@Resource。
先看基于XML的自动装配,可以有效地减少xml中的property属性和constructor-arg属性。
id为kenny的bean使用了byName的自动装配方式,com.spriginaction.springidol.Instrumentalist类中的:
private Instrument instrument;
instrument在使用时会被自动装配为Saxophone。
如果Instrument接口存在多个子类,则byType的自动装配将会失败。
4.2基于注解的自动装配
可以看出基于XML的自动装配还不是很简便,基于注解的自动装配会极大地简化配置工作, 下面是使用注解自动装配的介绍:
基于注解的支持以下注解:@Autowired,@Inject,@Resource。
@Autowired(required = "false"),不是必须注入的
@Autowired
@Autowired @Qualifier("guitar")指定注入bean id为guitar的bean。接口被多个类实现,注入接口时就需要。
@Value注入String和其他基本类型的属性。
@Autowired默认是按类型装配,@Resource默认是按名称进行装配,名称找不到时再按类型装配。
@Resource在JDK1.6中已支持,是通用的,@Autowired是Spring自己的注解。
可以标注在变量上(可以不需要set方法)或者set方法上。
4.3 自动检测bean
自动检测bean
@Component,通用组件 @Component("add") 显示的指明bean的id
@Controller Spring MVC 的Controller,struts的action
@Repository 数据仓库,DAO组件
@Service 业务层,定义为服务
@Component @Scope("prototype")
@PostConstruct用于标识bean的init方法
@PreDestroy用于destroy方法
自动扫描一般制定一个包,去扫描这个包及子包中的bean。也可以加上过滤器,使得扫描条件更为精确。
include-filter将和component-scan的条件取交集,自动扫描Instrument的子类。
include-filter可以和exclude-filter配合使用。