最近重温了一下Spring,还是写点东西出来,帮助自己记忆和加强对Spring的理解,也希望能够帮助大家学习Spring。
首先要提一个Spring框架的核心是思想即IOC(Inversion of Control:控制反转),也称为DI(依赖注入:Dependency Injection),那么IOC究竟是什么呢?
IOC(DI):其实Spring的这个核心概念并没有那么复杂,我们日常开发,通常要两个及以上的对象协作完成某一个业务逻辑,在一个对象需要使用另一个对象协作完成业务逻辑时,传统的方式是用:Object xxx = new Object();这样的方式来获取合作对象,我们就会发现这种方式耦合度很高,为什么说这种方式耦合度高呢,因为这种方式造成代码重用性降低,维护性降低,扩展性降低,加大了项目后期的维护成本,也不便于扩展,举个例子来说明一下这几个特性:
条件:调用UserService的Add方法
传统方式:
UserService userService = new UserService();
User user = new User();
userService.add(user);
Spring DI:
@Autowired
private UserService userService;
public void addUser(){
User user = new User();
userService.add(user);
}
在传统方式上,我们需要调用UserService的add方法,都需要new UserService(),也就是说,new UserService()这个方法我们要重复写很多次,Spring通过依赖注入的方式,相当就是UserService就是一个User管理人员(@Autowired private UserService userService),我们要添加User了,直接把User管理人员找来,把用户交给他,User管理人员就帮你添加这个用户。
从上面我们可以看出,Spring IOC思想就是:由Spring来实现对象的创建、销毁,来管理对象与对象之间的依赖、协作关系,对象只需要完成业务逻辑本身就行了,从这层来说,我们就说管理对象的创建、销毁以及管理对象与对象之间的依赖,协作关系的责任被反转了,由我们程序员负责的东西现在交给Spring来管理了,这就叫控制反转或依赖注入(IOC、DI)。
那么,IOC(DI)有什么好处呢?
1.降低耦合性
2.配置灵活,可直接在XML或使用注解修改,需要什么就配置什么
3.将自己需要new的对象交给容器统一管理、创建、可自动装配
接下来,我们就模拟一下Spring,写一个IOC的例子,便于大家理解!
要实现的业务:实现用户的添加操作!
步骤:
既然JAVA是一门面向对象的语言,我们首先要有“对象”,首先,我们创建一个User对象,实体类统一放入model(或entity)包中!
package com.castiel.model;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
接下来,我们创建一个UserService类,用来编写“添加用户”这个业务逻辑的代码!
package com.castiel.service;
import com.castiel.dao.UserDAO;
import com.castiel.model.User;
public class UserService {
//调用统一的DB接口
private UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
//统一业务方法
public void add(User u){
this.userDAO.save(u);
}
}
业务方法执行完毕,我们就需要把这个User存入数据库,那么,我们就需要编写一个UserDAO的统一接口!以后我们有其它业务需要存储用户到数据库就可以直接引用这个接口,调用save()方法就能完成用户存储了!
package com.castiel.dao;
import com.castiel.model.User;
public interface UserDAO {
public void save(User u);
}
接下来就要和数据库打交道了,那么我们编写一个UserDAO的实现类,在这个类里面我们实现UserDAO的save()方法!
package com.castiel.dao.impl;
import com.castiel.dao.UserDAO;
import com.castiel.model.User;
public class UserDAOImpl implements UserDAO {
@Override
public void save(User u) {
// TODO Auto-generated method stub
System.out.println("开始存储数据!");
System.out.println("存储User...");
//这里编写持久化到数据库的代码,如JDBC,或使用hibernate,ibatis等框架
System.out.println("用户持久化到数据库完成!");
}
}
ok,上面我们已经把添加用户的整个业务编写完毕了,下面,我们来模拟一下Spring,大致看看Spring内部是怎样注入的!
首先,来一个我们熟悉的东西,XML!在项目中我们命名为beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDAO" class="com.castiel.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.castiel.service.UserService">
<property name="userDAO" bean="userDAO" />
</bean>
</beans>
我们要实现的就是,不需要在UserService来new UserDao(),使用某些技术,把UserDao注入到UserService这个类里面,从而使业务能够正常服务!
首先我们编写一个BeanFactory接口,和Spring中命名一样,这就是我们的“冰工厂”了,主要用于对象获取依赖的实体对象!
package com.castiel.spring;
//Bean工厂
public interface BeanFactory {
public Object getBean(String name);
}
接下来我们编写一个 ClassPathXmlApplicationContext类,实现BeanFactory的getBean(String name)的方法及依赖注入功能!
package com.castiel.spring;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
public class ClassPathXmlApplicationContext implements BeanFactory {
//模拟Spring容器,键为xml中配置的bean的id,值为bean的class
private Map<String, Object> beans = new HashMap<String, Object>();
public ClassPathXmlApplicationContext() throws Exception{
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml"));
Element root = doc.getRootElement();
List list = root.getChildren("bean");
for(int i=0;i<list.size();i++){
Element element = (Element)list.get(i);
String id = element.getAttributeValue("id");
String clazz = element.getAttributeValue("class");
System.out.println(id+":"+clazz);
Object o = Class.forName(clazz).newInstance();
beans.put(id, o);
//注入start
for(Element propertyElement:(List<Element>)element.getChildren("property")){
String name = propertyElement.getAttributeValue("name");
String bean = propertyElement.getAttributeValue("bean");
Object beanObject = beans.get(bean);
String methodName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
System.out.println("mothodName = "+methodName);
Method m = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject);
}
//注入end
}
}
@Override
public Object getBean(String name) {
// TODO Auto-generated method stub
return beans.get(name);
}
}
最后,我们编写一个Test类,来检验检验我们上面的注入功能是否成功!这里我给出传统方式和自动注入方式,测试类的建立方式我在这里也简单说说!
package com.castiel.service;
import org.junit.Test;
import com.castiel.dao.UserDAO;
import com.castiel.model.User;
import com.castiel.service.UserService;
import com.castiel.spring.BeanFactory;
import com.castiel.spring.ClassPathXmlApplicationContext;
public class UserServiceTest {
/**测试类生成方式**/
/**
* 1.选中src下com.castiel.service包中的UserService
* 2.右键new jUnit Test Case
* 3.点击NEXT
* 4.选择需要测试的方法
* 5.点击Finish
* 6.把生成出来的*Test.java文件拖到test下com.castiel.service包中
* @throws Exception
*/
@Test
public void testAdd() throws Exception {
/*
* 方法1,传统方式,使用此种方式,在UserService的add方法中,
* userDAO为空,程序报错
*/
/*UserService service = new UserService();
User u = new User();
service.add(u);*/
//方法1结束!
/**
* 自动装配,将userDAO注入到userService,注入方式:setter注入和构造器注入
*/
BeanFactory factory = new ClassPathXmlApplicationContext();
UserService userService = (UserService)factory.getBean("userService");
User user = new User();
userService.add(user);
}
}
我们执行一下测试类, 看看是否将UserDAO成功注入UserService中,执行成功会打印UserDAOImpl类save();方法中的文字!