1.IoC简介 (转载)
(原文:http://jinnianshilongnian.iteye.com/blog/1413846) via:@jinnianshilongnian
1.1、IoC是什么
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
1.2、IoC能做什么
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
1.3、IoC和DI
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
2、代码实现(原创)
2.1准备工作
2.1.1jar包与项目目录结构
spring原项目下
spring\ 1.aopalliance-1.0.jar 2.aopalliance-alpha1.jar 3.aspectjrt.jar aspectjweaver.jar 4.commons-logging.jar
Spring\spring-framework-3.0.0.RELEASE\dist里的所有jar
2.1.2vo包,dao包,service包代码
public calss student{//voprivate String name;private Integer age;/** getter and setter */
}
public interface IStudentDao {//daopublic void add(Student student);
}
public interface IStudentDaoByMybatis {public void add(Student student);
}
public class StudentDaoImpl implements IStudentDao{//dao.impl@Overridepublic void add(Student student) {System.out.println(student.getName()+"成功通过JDBC添加");}
}
public class StudentDaoByMybatisImpl implements IStudentDaoByMybatis{//dao.impl@Overridepublic void add(Student student) {System.out.println(student.getName()+"成功通过Mybatis添加");}
}
public interface IStudentService {//service包public void add(Student student);
}
2.1.3 spring.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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- xmlns:aop="http://www.springframework.org/schema/aop" 声明要用aop的标签xsi:schemaLocation= aop的标签地址"http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd" -->
</beans>
2.2注入IoC(通过spring.xml)
2.2.1重点标签与属性解释
(一)bean的生命周期,两个常用属性:lazy-init和scope
1.懒加载属性lazy-init,每次启动IoC时,通常需要启动的大的数据库启动很耗时(比如数据库连接池),用再加载,通过这种方式提高速度,true是开启,false是关闭,default是默认,beans的全局没有设置时default默认是false关闭
2.scope,设置模式,singleton是单例,prototype是多例,每次用到IoC容器里的bean时,单例用的是一个,多例可用多个,singleton是单例的,整个项目共用一个对象,应用很多(锤子);prototype是多例的,项目会用多个对象,多用于Controller注入(钉子)
3.init-method,bean初始化时调用的方法,参数里写bean对应的类里的方法名,多用于初始化字典值
4.destroy-method,bean销毁时用的方法 ,参数里写bean对应的类里的方法名,多用于数据库连接的关流(现在开始可以自动关了).注意:这个bean的scope用prototype的时候(也就是多例时),不调用该bean的destroy-method
(二)依赖注入, autowire自动装载可省略以下<property>,通过反射的方式,获取这个bean对应的类里的各种信息
1.byName通过set方法的名称注入(装载)(原理是反射),根据ServiceImpl里得到的set方法的名称
2.byType通过实现的接口类型注入(装载), 比如上面这个org.jsoft.dao.IStudentDao
3.no是不自动注入(装载)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><!-- default-autowire-candidates=""如果写在beans标签里的属性为默认的全局aotowire --><!-- default-lazy-init="" 如果写在beans标签里的属性为默认的全局aotowire -->
<!-- 名称必须规范,后缀的Dao必须和该类后缀一样(包括大小写) --><bean id="studentDao" class="org.jsoft.dao.impl.StudentDaoImpl"></bean><bean id="studentDaoByMybatis" lazy-init="true" class="org.jsoft.dao.impl.StudentDaoByMybatisImpl"></bean><bean id="studentService" class="org.jsoft.service.impl.StudentServiceImpl" scope="singleton" init-method="a" destroy-method="b" autowire="byType"><!-- 包含了另一个bean“studentDAO” <property name="studentDao"><ref bean="studentDao"/></property> --></bean><!-- more bean definitions go here --></beans>
2.2.2 java代码
StudentServiceImpl里的代码
public class StudentServiceImpl implements IStudentService{private IStudentDao studentDao;private IStudentDaoByMybatis studentDaoByMybatis;// public IStudentDao getStudentDaoByMybatis() {//对比ByName用法时用
// return studentDao;
// }
// public void setStudentDaoByMybatis(IStudentDao studentDaoByMybatis) {
// this.studentDao = studentDaoByMybatis;
// }//需要写getset方法public IStudentDaoByMybatis getStudentDaoByMybatis() {return studentDaoByMybatis;}public void setStudentDaoByMybatis(IStudentDaoByMybatis studentDaoByMybatis) {this.studentDaoByMybatis = studentDaoByMybatis;}public IStudentDao getStudentDao() {return studentDao;}public void setStudentDao(IStudentDao studentDao) {this.studentDao = studentDao;}// @Override
// public void add(Student student) {
// studentDao.add(student);
// //System.out.println(student.getName());
//
// }@Overridepublic void add(Student student) {studentDaoByMybatis.add(student);//System.out.println(student.getName());}
// @Override
// public void add(Student student) {
// studentDaoByMybatis.add(student);
// //System.out.println(student.getName());
//
// }public void a(){//bean初始化时调用System.out.println("StudentServiceImpl.a(),初始化");}public void b(){//bean销毁时调用System.out.println("StudentServiceImpl.b(),销毁");}
}
//测试用的Main方法
//ApplicationContext 是接口,下面的ClassPathXmlApplicationContext也可以用,后者可以用.close()方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring/spring.xml"});
Student s = new Student();
s.setName("Joy");
IStudentService studentService = context.getBean("studentService",IStudentService.class);
IStudentService studentService2 = context.getBean("studentService",IStudentService.class);//scope的属性
//singleton是单例,只用一个,studentService和studentService2被赋予的是一个bean对象,内存地址一样,所以会返回true;
//而prototype是多例,会给studentService赋予来自一个bean的不同的对象
System.out.println(studentService == studentService2);//对比对象在内存里的地址,一样就返回true,studentService.add(s);
context.close();
2.3注入IoC(通过注解)
2.3.1spring.xml的配置
注意:
1.beans的xmlns:context="http://www.springframework.org/schema/context"这句话一定要有,声明要用注解
2.beans的xsi:schemaLocation属性里一定要有"http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd "注解的地址
3.下面的context标签,写上,注解才会生效
<?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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><!-- --><context:annotation-config/><!-- 扫描组件 --><context:component-scan base-package="org.jsoft"/></beans>
2.3.2注解
1@Component("这个bean的id")都可以用,写在声明类名的上边
2.@Service("这个bean的id")用在serviceimpl里声明类名的上边
3@Repository("这个bean的id")dao持久层里声明类名的上边
4.@Autowired 写在serviceImpl里的声明过的dao的set方法上 @Qualifier("用到的dao层的bean的id")写在这个方法声明参量的括号里参量类型的前边 *不常用*
5.@Controller 用在C层里
6.@Resource("用到的bean的id"),声明的Service或者Dao上面,里面写对应的bean的id
2.3.3Demo(没有@Controller)
1.dao层,只需要写在实现类里,
@Repository("studentDaoImpl")//注入bean
public class StudentDaoImpl implements IStudentDao{//实现了IStudentDao@Overridepublic void add(Student student) {System.out.println(student.getName()+"成功通过JDBC添加");}
}
@Repository("studentDaoByHibernateImpl")
public class StudentDaoByHibernateImpl implements IStudentDao{//也实现了IStudentDao@Overridepublic void add(Student student) {System.out.println(student.getName()+"成功通过Hibernate添加");}
}
@Repository("studentDaoByMybatisImpl")
public class StudentDaoByMybatisImpl implements IStudentDaoByMybatis{//实现了另外一个@Overridepublic void addByMybatis(Student student) {System.out.println(student.getName()+"成功通过Mybatis添加");}
}
2.service层,同样地只需要写在实现类里
@Service(value="studentService")
public class StudentServiceImpl implements IStudentService{//@Resource(name="studentDaoImpl")private IStudentDao studentDao;private IStudentDao studentDaoByHibernate;@Resource(name="studentDaoByMybatisImpl")private IStudentDaoByMybatis studentDaoMyBatis;//@Resource会通过暴力反射找到的bean名字,没有用getset方法
// public IStudentDao getStudentDao() {
// return studentDao;
// }
// public void setStudentDao(IStudentDao studentDao) {
// this.studentDao = studentDao;
// }// @Override
// public void add(Student student) {
// studentDao.add(student);
//
// }@Overridepublic void add(Student student) {studentDaoByHibernate.add(student);}@Overridepublic void addByMybatis(Student student) {studentDaoMyBatis.addByMybatis(student);}public IStudentDao getStudentDao() {return studentDao;}//也可以自动装载,无需写@Resourcepublic void setStudentDao(IStudentDao studentDao) {this.studentDao = studentDao;}public IStudentDao getStudentDaoByHibernate() {return studentDaoByHibernate;}@Autowiredpublic void setStudentDaoByHibernate(@Qualifier("studentDaoByHibernateImpl")IStudentDao studentDaoByHibernate) {this.studentDaoByHibernate = studentDaoByHibernate;}public IStudentDaoByMybatis getStudentDaoMyBatis() {return studentDaoMyBatis;}public void setStudentDaoMyBatis(IStudentDaoByMybatis studentDaoMyBatis) {this.studentDaoMyBatis = studentDaoMyBatis;}
3.main方法的测试与结果
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring/spring.xml"});
Student s = new Student();
s.setName("Joy");
IStudentService studentService = context.getBean("studentService",IStudentService.class);
studentService.add(s);
context.close();