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{//vo
private String name;
private Integer age;
/** getter and setter */
}
public interface IStudentDao {//dao
public void add(Student student);
}
public interface IStudentDaoByMybatis {
public void add(Student student);
}
public class StudentDaoImpl implements IStudentDao{//dao.impl
@Override
public void add(Student student) {
System.out.println(student.getName()+"成功通过JDBC添加");
}
}
public class StudentDaoByMybatisImpl implements IStudentDaoByMybatis{//dao.impl
@Override
public 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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://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/beans
http://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());
//
// }
@Override
public 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.xsd
http://www.springframework.org/schema/context
http://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
@Override
public void add(Student student) {
System.out.println(student.getName()+"成功通过JDBC添加");
}
}
@Repository("studentDaoByHibernateImpl")
public class StudentDaoByHibernateImpl implements IStudentDao{//也实现了IStudentDao
@Override
public void add(Student student) {
System.out.println(student.getName()+"成功通过Hibernate添加");
}
}
@Repository("studentDaoByMybatisImpl")
public class StudentDaoByMybatisImpl implements IStudentDaoByMybatis{//实现了另外一个
@Override
public 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);
//
// }
@Override
public void add(Student student) {
studentDaoByHibernate.add(student);
}
@Override
public void addByMybatis(Student student) {
studentDaoMyBatis.addByMybatis(student);
}
public IStudentDao getStudentDao() {
return studentDao;
}
//也可以自动装载,无需写@Resource
public void setStudentDao(IStudentDao studentDao) {
this.studentDao = studentDao;
}
public IStudentDao getStudentDaoByHibernate() {
return studentDaoByHibernate;
}
@Autowired
public 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();