前言
在Spring的第二篇中主要讲解了Spring Core模块的使用IOC容器创建对象的问题,Spring Core模块主要是解决对象的创建和对象之间的依赖关系,因此本博文主要讲解如何使用IOC容器来解决对象之间的依赖关系!
回顾以前对象依赖
我们来看一下我们以前关于对象依赖,是怎么的历程
直接new对象
- 在最开始,我们是直接new对象给serice的userDao属性赋值…
class UserService{ UserDao userDao = new UserDao();}
写DaoFactory,用字符串来维护依赖关系
后来,我们发现service层紧紧耦合了dao层。我们就写了DaoFactory,在service层只要通过字符串就能够创建对应的dao层的对象了。
DaoFactory
public class DaoFactory { private static final DaoFactory factory = new DaoFactory(); private DaoFactory(){} public static DaoFactory getInstance(){ return factory; } publicT createDao(String className,Class clazz){ try{ T t = (T) Class.forName(className).newInstance(); return t; }catch (Exception e) { throw new RuntimeException(e); } }}
- serivce
private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class); private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class); private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class); private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);
DaoFactory读取配置文件
再后来,我们发现要修改Dao的实现类,还是得修改service层的源代码呀..于是我们就在DaoFactory中读取关于daoImpl的配置文件,根据配置文件来创建对象,这样一来,创建的是哪个daoImpl对service层就是透明的
DaoFactory
public class DaoFactory { private UserDao userdao = null; private DaoFactory(){ try{ InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties"); Properties prop = new Properties(); prop.load(in); String daoClassName = prop.getProperty("userdao"); userdao = (UserDao)Class.forName(daoClassName).newInstance(); }catch (Exception e) { throw new RuntimeException(e); } } private static final DaoFactory instance = new DaoFactory(); public static DaoFactory getInstance(){ return instance; } public UserDao createUserDao(){ return userdao; }}
- service
UserDao dao = DaoFactory.getInstance().createUserDao();
Spring依赖注入
通过上面的历程,我们可以清晰地发现:对象之间的依赖关系,其实就是给对象上的属性赋值!因为对象上有其他对象的变量,因此存在了依赖…
Spring提供了好几种的方式来给属性赋值
- 1) 通过构造函数
- 2) 通过set方法给属性注入值
- 3) p名称空间
- 4)自动装配(了解)
- 5) 注解
搭建测试环境
- UserService中使用userDao变量来维护与Dao层之间的依赖关系
UserAction中使用userService变量来维护与Service层之间的依赖关系
userDao
public class UserDao { public void save() { System.out.println("DB:保存用户"); }}
- userService
public class UserService { private UserDao userDao; public void save() { userDao.save(); }}
- userAnction
public class UserAction { private UserService userService; public String execute() { userService.save(); return null; }}
构造函数给属性赋值
其实我们在讲解创建带参数的构造函数的时候已经讲过了…我们还是来回顾一下呗..
我们测试service和dao的依赖关系就好了….在serice中加入一个构造函数,参数就是userDao
public UserService(UserDao userDao) { this.userDao = userDao; //看看有没有拿到userDao System.out.println(userDao); }
applicationContext.xml配置文件
- 测试:可以成功获取到userDao对象
// 创建容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //得到service对象 UserService userService = (UserService) ac.getBean("userService");
通过set方法给属性注入值
我们这里也是测试service和dao层的依赖关系就好了…在service层通过set方法来把userDao注入到UserService中
- 为UserService添加set方法
public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; //看看有没有拿到userDao System.out.println(userDao); } public void save() { userDao.save(); }}
applicationContext.xml配置文件:通过property节点来给属性赋值
- 引用类型使用ref属性
- 基本类型使用value属性
- 测试:
内部Bean
我们刚才是先创建userDao对象,再由userService对userDao对象进行引用…我们还有另一种思维:先创建userService,发现userService需要userDao的属性,再创建userDao…我们来看看这种思维方式是怎么配置的:
applicationContext.xml配置文件:property节点内置bean节点
- 测试
我们发现这种思维方式和服务器访问的执行顺序是一样的,但是如果userDao要多次被其他service使用的话,就要多次配置了…
p 名称空间注入属性值
p名称控件这种方式其实就是set方法的一种优化,优化了配置而已…p名称空间这个内容需要在Spring3版本以上才能使用…我们来看看:
applicationContext.xml配置文件:使用p名称空间
- 测试
自动装配
Spring还提供了自动装配的功能,能够非常简化我们的配置
自动装载默认是不打开的,自动装配常用的可分为两种:
- 根据名字来装配
- 根据类型类装配
XML配置根据名字
applicationContext.xml配置文件:使用自动装配,根据名字
- 测试
XML配置根据类型
applicationContext.xml配置文件:使用自动装配,根据类型
值得注意的是:如果使用了根据类型来自动装配,那么在IOC容器中只能有一个这样的类型,否则就会报错!
- 测试:
我们这只是测试两个对象之间的依赖关系,如果我们有很多对象,我们也可以使用默认自动分配
使用注解来实现自动装配
@Autowired注解来实现自动装配:
- 可以在构造器上修饰
- 也可以在setter方法上修饰
- 来自java的@Inject的和@AutoWired有相同的功能
如果没有匹配到bean,又为了避免异常的出现,我们可以使用required属性上设置为false。【谨慎对待】
- 测试代码
@Componentpublic class UserService { private UserDao userDao ; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; }}
顺利拿到userDao的引用
注解
自从jdk5有了注解这个新特性,我们可以看到Struts2框架、Hibernate框架都支持使用注解来配置信息…
通过注解来配置信息就是为了简化IOC容器的配置,注解可以把对象添加到IOC容器中、处理对象依赖关系,我们来看看怎么用吧:
使用注解步骤:
- 1)先引入context名称空间
- xmlns:context=”http://www.springframework.org/schema/context”
- 2)开启注解扫描器
<context:component-scan base-package=""></context:component-scan>
- 也可以通过自定义扫描类以@CompoentScan修饰来扫描IOC容器的bean对象
//表明该类是配置类@Configuration//启动扫描器,扫描bb包下的 //也可以指定多个基础包 //也可以指定类型@ComponentScan("bb")public class AnnotationScan { }
在使用@ComponentScan()这个注解的时候,在测试类上需要@ContextConfiguration这个注解来加载配置类…
- @ContextConfiguration这个注解又在Spring的test包下..
创建对象以及处理对象依赖关系,相关的注解:
- @ComponentScan扫描器
- @Configuration表明该类是配置类
- @Component 指定把一个对象加入IOC容器—>@Name也可以实现相同的效果【一般少用】
- @Repository 作用同@Component; 在持久层使用
- @Service 作用同@Component; 在业务逻辑层使用
- @Controller 作用同@Component; 在控制层使用
- @Resource 依赖关系
- 如果@Resource不指定值,那么就根据类型来找,相同的类型在IOC容器中不能有两个
- 如果@Resource指定了值,那么就根据名字来找
测试代码
- UserDao
package aa;import org.springframework.stereotype.Repository;/** * Created by ozc on 2017/5/10. *///把对象添加到容器中,首字母会小写@Repositorypublic class UserDao { public void save() { System.out.println("DB:保存用户"); }}
- userService
package aa;import org.springframework.stereotype.Service;import javax.annotation.Resource;//把UserService对象添加到IOC容器中,首字母会小写@Servicepublic class UserService { //如果@Resource不指定值,那么就根据类型来找--->UserDao....当然了,IOC容器不能有两个UserDao类型的对象 //@Resource //如果指定了值,那么Spring就在IOC容器找有没有id为userDao的对象。 @Resource(name = "userDao") private UserDao userDao; public void save() { userDao.save(); }}
- userAction
package aa;import org.springframework.stereotype.Controller;import javax.annotation.Resource;/** * Created by ozc on 2017/5/10. *///把对象添加到IOC容器中,首字母会小写@Controllerpublic class UserAction { @Resource(name = "userService") private UserService userService; public String execute() { userService.save(); return null; }}
- 测试
package aa;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * Created by ozc on 2017/5/10. */public class App { public static void main(String[] args) { // 创建容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("aa/applicationContext.xml"); UserAction userAction = (UserAction) ac.getBean("userAction"); userAction.execute(); }}
注解和XML配置是可以混合使用的…