一般来说,业务逻辑是分层的
假设我们现在有服务层和持久层。
持久层IDao和实现类DaoImpl:
public interface IDao {
public void save(String data);
}
public class DaoImpl implements IDao {
public void save(String data) {
System.out.println("save to database:" + data);
}
}
服务层接口IService和实现类ServiceImpl
public interface IService {
public void save(String data);
}
public class ServiceImpl implements IService {
private IDao iDao;
public void setiDao(IDao iDao) {
this.iDao = iDao;
}
public void save(String data) {
System.out.println("sovle data");
iDao.save(data);
}
}
可以看到,其ServiceImpl类中存在对持久层IDao的引用。也就是说ServiceImpl中存在IDao这个对象。Service通过调用持久层的方法来保存数据的意思。
所以,这么一种关系通过配置文件应该怎么表示呢?我们想要表达我们有两个接口,而接口的实现类也分别给出了,并且接口之间存在引用的关系。
首先是我们有两个接口IService和IDao,他们的实现类分别是ServiceImpl和DaoImpl。我们要表达这么一个关系。
在配置文件中,我们就需要这么做:
<bean id="iService" class="Spring.test1.ServiceImpl"></bean>
<bean id="iDao" class="Spring.test1.DaoImpl"></bean>
在Spring容器中,所有对象都称为bean对象。所以两个接口分别对应两个bean标签。
可以看到,id为iService的bean的实现类是Spring.test1.ServiceImpl这个类。id为iDao的bean的实现类是Spring.test1.DaoImpl这个类。
这和我们一开始的期望是一样的。(当然,如果不使用面向接口的方式,单独的类的实现也可以是这么一个方式)
在我看到,这是这么一个意思:可以通过bean的id名,返回一个class对应的包的对象给我(而这个对象是否是新创建的取决于我们的配置,默认是单例)。
而bean之间是存在引用关系的,这怎么表达呢?
<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">
<bean id = "iService" class = "Spring.test1.ServiceImpl">
<property name = "iDao" ref = "iDao"></property>
</bean>
<bean id = "iDao" class = "Spring.test1.DaoImpl"></bean>
</beans>
这是其中一种方式,通过ref属性可以找到对应的bean名称并调用对应的方法去初始化引用的对象。
上面的配置是通过设值注入的方式初始化的。
也就是说,如果使用property标签,则引用对象的那个类,在这里我们是ServiceImpl类,必须实现引用对象的set方法,property标签会自动调用类中的set方法为引用对象进行初始化。
另一种方式是构造注入,只需要将property标签替换成如下语句:
<constructor-arg name = "iDao" ref = "iDao"></constructor-arg>
并且ServiceImpl对象必须实现构造函数的初始化,也就是说SercieImpl需要增加如下的构造函数:
public ServiceImpl(IDao iDao) {
this.iDao = iDao;
}
这是两种依赖注入的方式。因为ServiceImpl依赖于iDao实现它的功能,所以需要注入iDao的值。
bean标签还有很多其他的属性。
<beans>标签(Spring配置文件的根元素,包含一个或多个bean元素):
1.default-autowire属性:默认的bean自动装配模式。
1) no:不使用自动装配。bean的引用必须通过ref元素定义。这是默认的属性
2) byName:通过属性名字进行自动装配。
3) byType:如果BeanFactory中正好有一个同属性类型一样的bean,就自动装配这个属性。如果有多于一个这样的bean,就抛出一个致命异常,它指出你可能不能对那个bean使用byType的自动装配。如果没有匹配的bean,则什么都不会发生,属性不会被设置。
4) constructor:这个同byType类似,不过是应用于构造函数的参数。如果在BeanFactory中不是恰好有一个bean与构造函数参数相同类型,则一个致命的错误会产生。
<bean>标签:
1.id属性:IoC容器中bean的唯一标识符,不允许重复。
2.class属性:bean的类名。我的理解是让容器知道从哪里去获取对象。
3.scope属性:定一个bean的作用域。有5中取值。
1) singleton。默认的scope,同一个IoC容器中仅存在一个bean实例,是单例。
2) prototype。每次获取bean对象时,都返回一个新的实例,相当于每次都new一个新的对象。
3) request。用于web开发,该作用域仅适用于WebApplicationContext。每次HTTP请求都会创建一个Bean中。
4) session。同样用于web开发,同一个session共享一个bean对象。
5) globalSession。用于分布式系统,跨系统的时候也能共享bean对象。如果不是portlet环境,等同于session。
4.init-method和destroy-method属性:Spring在初始化bean或销毁bean时候,会分别调用这两个属性的值对应的方法。
例如:
<bean id=“foo” class=“...Foo” init-method=“setup” destory-method=“teardown”/>
初始化阶段如果对应的Foo类中有setup方法的话,就会调用setup方法。销毁阶段也一样,调用teardown方法。
顺便提一下Spring的生命周期。Spring的生命周期分为四个阶段:1.定义 2.初始化 3.使用 4.销毁
初始化对应上下文的start方法,销毁对应这上下文的destroy方法。这个在下文的Junit测试中有使用。
现在我们需要的业务逻辑已经写的差不多了。
接下来通过Junit来测试我们的配置是否成功。
这里我通过maven来管理项目。(eclipse j2ee版本好像是自带maven的)
首先创建一个maven项目。
因为需要使用Junit和Spring framework,所以我们在pom配置中加入以下依赖。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.version>4.3.3.RELEASE</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
这里直接上代码吧
package Spring.test1;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
private ClassPathXmlApplicationContext context;
@Before
public void before() {
try {
context = new ClassPathXmlApplicationContext("classpath:spring-ioc.xml");
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After
public void after() {
context.destroy();
}
@Test
public void Test() {
IService iService = (IService) context.getBean("iService");
iService.save("myData");
}
}
这里需要说明一下,我们在获取的bean的时候,需要通过Spring容器的应用上下文来获取,指明是获取哪个上下文的bean。所以我们就需要先获取上下文,就是@Before模块中所示。spring-ioc.xml是我们之前写的配置文件。而classpath是什么呢?
classpath是资源文件目录,在maven中是src/main/resources目录,spring-ioc.xml存放在这个目录下。
因为我们需要spring-framework的上下文,所以我们能看到,在pom配置文件中,有关于context的依赖。
bean容器的初始化,也就是上下文的获取,是有三种方式的。
1.通过本地文件来获取:
例如:
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("文件路径");
2.通过ClassPath来获取:
就如同样例所示。
3.通过web来获取:
这个方法比较不一样,在我们的web应用中,我们在web.xml文件中进行配置。
通过listener或servlet来获取。
下面是listener的例子:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这个时候我们应该理解了上下文的定义了。
如果我们希望在类中获取Ioc容器的bean的信息怎么办呢?
我们可以通过实现ApplicationContextAware这个接口来完成。
例子:
public class AwareTest implements ApplicationContextAware {
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
((IService)arg0.getBean("iService")).save("myData2");
}
}
只需要实现这个接口,就可以拿到我们的上下文了,当然这需要在配置文件中进行配置。
还有许许多多的Aware接口,大家可以自己去查找,例如BeanFactoryAware,ResourceLoaderAware等等。