这篇文章为大家带来有关Spring容器以及spring ioc原理的介绍。文章不仅介绍Spring容器以及spring ioc原理的知识点,还介绍了Spring的配置和使用,希望大家通过这篇文章能有所收获。
网站建设哪家好,找成都创新互联!专注于网页设计、网站建设、微信开发、小程序设计、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了伊川免费建站欢迎大家使用!
一:spring ioc的原理:
小A刚到公司老大安排了一个活,公司前不久刚开发了一个社交网站,运行不太稳定,经常会出现莫名其妙的bug,需要在必要的地方加上日志,方便找到错误,小A很快就开发好了日志记录类,为了以后的扩展性,还添加了一个接口:
public interfaceILogger {
voiddoLog();
}
public classConsolLoggerimplementsILogger {
@Override
public voiddoLog() {
System.out.println("打印日志到控制台");
}
}
先在添加好友功能这里增加一个日志:
public classFriend {
private ILoggerlogger=new ConsoleLogger ();
public voidaddFriend(){
System.out.println("添加好友!");
logger.doLog();//添加日志
}
}
发现好多地方需要添加的,一个一个加上去,三天后终于全部加好了。
这天老大又找到了小A:小A啊,现在日志是在控制台打印的,你看能不能保存在文件里面啊。小A皱了皱眉,先估算了一下工作量,需要先开发一个保存日志到文件的类,然后逐个修改,工作量有点大哦,而且都是重复性的工作,太没挑战,万一以后还需要再改那岂不是又要浪费几天美好时光,有没有简单点的办法呢?
工厂模式,小A突然灵光一闪,对就是它了,先写一个记录日志到文件的类:
public classFileLoggerimplementsILogger{
public voiddoLog(){
System.out.println("记录日志到文件");
}
}
再写一个工厂类:
public class LoggerFactory {
public static ILogger createLogger(){
return new FileLogger();
}
}
现在可以通过工厂创建日志对象了:
public class Friend {
private FileLogger logger = LoggerFactory.createLogger();
public void addFriend(){
System.out.println("添加好友!");
logger.doLog();//添加日志
}
}
以后再有需求变动的时候只需要修改工厂类就可以了,完美,等等似乎还有一点瑕疵,如果能够实现连工厂类都不需要修改岂不是更完美,有点得寸进尺了哦,人类的智慧是无限的看步骤:
1. 将日志的实现类的全限定名放到配置文件中
2. 通过xml解析根据id从配置文件中读出日志的实现类的全限定名
3. 通过反射动态创建日志实现类的对象
修改以后的工厂类伪代码如下:
public class LoggerFactory {
public static ILogger createLogger(String id){
//解析xml根据id获取要创建的日志类的全限定名
//使用反射动态创建日志实现类对象
//将该对象返回
return;
}
}
如果需要修改日志类,现在只需要修改xml配置文件就可以了,完美。
这就是spring ioc实现的基本原理,当然身为一个伟大的产品,怎么可以如此简单呢.
在Spring中有一个核心概念叫容器,顾名思意,容器是用来装东西的,装的什么东西呢?就是需要管理的对象,装在哪里呢?一个HashMap中, 大致的可以理解为以对象名为键,以对象本身为值的一个HashMap,当然远比这要复杂。
我们用容器来实例化对象,管理对象之间的依赖。
在spring中有两个重要的接口:BeanFactory和ApplicationContext,所谓的容器就是实现了BeanFactory接口或者BeanFactory接口的类的实例,BeanFactory是最顶层最基本的接口,它描述了容器需要实现的最基本的功能,比如对象的注册,获取。
public interfaceBeanFactory {
StringFACTORY_BEAN_PREFIX="&";
/*
*四个不同形式的getBean方法,获取实例
*/
Object getBean(String name)throwsBeansException;
TgetBean(String name, Class requiredType)throwsBeansException;
TgetBean(Class requiredType)throwsBeansException;
Object getBean(String name, Object... args)throwsBeansException;
//是否存在
booleancontainsBean(String name);
//是否为单实例
booleanisSingleton(String name)throwsNoSuchBeanDefinitionException;
//是否为多例
booleanisPrototype(String name)throwsNoSuchBeanDefinitionException;//
//名称、类型是否匹配
booleanisTypeMatch(String name, Class> targetType)
throwsNoSuchBeanDefinitionException;
//获取类型
Class> getType(String name)throwsNoSuchBeanDefinitionException;
//根据实例的名字获取实例的别名
String[] getAliases(String name);
}
ApplicationContext依赖BeanFactory接口,它描述的内容更加广泛,例如资源的获取等等。
当然除了这两个接口还有很多其它接口,这里不重点讨论,附上一张图以作了解。
通常我们使用的最多的容器是实现了ApplicationContext接口的类,ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
ClassPathXmlApplicationContext在类路径下寻找配置文件来实例化容器,默认是读取 src 目录下的配置文件
FileSystemXmlApplicationContext在文件系统路径下寻找配置文件来实例化容器,默认是读取项目名下一级,与src同级的配置文件
这里的配置文件是xml文件,它描述了被管理的对象和对象之间的依赖(beans.xml)。
<beansxmlns="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">
<beanid="userDao"class="cn.xh.dao.UserDaoImpl">bean>
beans>
在这个配置文件中有一行配置:
<beanid="userDao"class="cn.xh.dao.UserDaoImpl">bean>
表示使用spring容器管理的对象UserDaoImpl
加载配置文件创建容器:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
获取容器中的对象
UserDaoImpl userDao = (UserDaoImpl) applicationContext.getBean("userDao");
我们来比较一下beanFactory和applicationContext:
1.BeanFactory接口定义了容器的最基本功能,它可以读取类的配置文档,管理类的加载,实例化,维护类之间的依赖关系。实现它的容器实例化时并不会初始化配置文件中定义的类,初始化动作发生在第一次调用时。 第一次调用创建好对象后就放入缓存中以后使用直接从缓存中获取。
2. applicationContext接口除了提供容器的基本功能外还提供了很多的扩展功能,实现它的容器实例化时就会将配置文件中定义的类初始化。
3.最常用的的容器是实现了applicationContext接口的容器ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
三.spring ioc的注入方式。
spring依赖注入(DI),是spring 控制反转(IOC)的具体实现,在一个类A中需要一个成员变量B,以前是直接给B赋值,现在通过Spring容器在需要的时候将B的值注入到A对象中,简单的说,就是通过spring在适当的时候给A的成员变量B赋值。
Spring的注入有三种方式:构造方法注入,setter方法注入,接口注入,使用最广泛的是setter方法注入,接口注入的方式现在已经很少用了。我们重点介绍前面两种注入方式:
构造方法注入:
就是使用类中的构造函数,给成员变量赋值,注意这里是使用spring框架来为我们赋值,上代码,创建一个Person类,通过构造方法为其成员变量赋值。
public classPerson {
private intpid;
privateStringpname;
private intage;
publicPerson(intpid, String pname,intage) {
this.pid= pid;
this.pname= pname;
this.age= age;
}
@Override
publicString toString() {
return"Person{"+
"pid="+pid+
", pname='"+pname+'\''+
", age="+age+
'}';
}
}
spring的配置:
<beanid="person"class="cn.xh.dao.Person">
<constructor-argname="pid"value="1">constructor-arg>
<constructor-argname="pname"value="张三">constructor-arg>
<constructor-argname="age"value="18">constructor-arg>
bean>
通过<constructor-argname="pid"value="1">constructor-arg>给Person的成员变量赋值。
构造方法注入有它的局限性,试想一下如果有20个成员变量,构造方法的参数岂不是要20个,很不优雅,使用setter方法注入可以解决这个问题。
Setter方法注入:
在一个类中我们往往会通过get和set方法来对成员变量进行赋值和取值的操作,可以通过set方法来给成员变量赋值。
public classPerson {
private intpid;
privateStringpname;
private intage;
public intgetPid() {
returnpid;
}
public voidsetPid(intpid) {
this.pid= pid;
}
publicString getPname() {
returnpname;
}
public voidsetPname(String pname) {
this.pname= pname;
}
public intgetAge() {
returnage;
}
public voidsetAge(intage) {
this.age= age;
}
@Override
publicString toString() {
return"Person{"+
"pid="+pid+
", pname='"+pname+'\''+
", age="+age+
'}';
}
}
spring的配置:
<beanid="person"class="cn.xh.dao.Person">
<propertyname="pid"value="1">property>
<propertyname="pname"value="张三">property>
<propertyname="age"value="18">property>
bean>
配置<propertyname="pid"value="1">property>以后可以通过类提供的set方法将值赋给成员变量。
使用setter方法注入有一个需要特别注意的地方,假如我们给Person类加一个有参的构造函数:
public classPerson {
private intpid;
privateStringpname;
private intage;
publicPerson(intpid, String pname,intage) {
this.pid= pid;
this.pname= pname;
this.age= age;
}
}
程序运行的时候会报错,原因就是当我们加上有参构造函数以后就不会默认生成无参构造函数,使用setter方法注入容器启动的时候会调用无参构造函数去实例化对象,所以我们需要手动加上无参构造函数。
看完上述内容,你们对Spring容器以及spring ioc原理有进一步的了解吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读。