[Spring]IOC控制反转和DI依赖注入
从之前算起到现在接触Spring也已经有几天了,进度也不是很快,就只弄懂了控制反转和依赖注入那么一点东西.然后敲了两个demo
主要是因为之前没有学过,然后网上资源很多但是都不是面向我们初学者的,大多都是直接上一堆配置,一堆代码纯粹告诉我如何用,而我敲完之后却不知道原理,在这里我算是淌过几次坑了
我找过W3School,上面的基础挺详细,各种概念给你讲清楚,但是它的教程有点老,Spring教程好像最后更新是16年,而且,有些地方对小白不是很友好,比如他没有提供需要导入的jar包,这一点上How2j做的不错
但是个人感觉How2j上的是简洁版的Spring,站长省略掉了其中的一些东西,之后我干脆找了外面java培训的视频看,感觉挺不错,某宝上有教程卖,十多块就可以买到(这里真的不是打广告)
Spring框架的介绍就不多说了,
Spring的下载地址:http://repo.spring.io/release/org/springframework/spring/
Spring的依赖包下载:http://s3.amazonaws.com/dist.springframework.org/release/SPR/spring-framework-3.0.2.RELEASE-dependencies.zip(自从有些版本以后Spring就没有提供这个了)
IOC:
IOC称为控制反转,控制反转的意思就是将创建对象的权利转交,我们以前创建对象的方式是new Object();而这样容易造成对象之间高耦合,那么什么是耦合呢,我的理解是他们之间的关系过于紧密
这样如果我们需要加入新的功能的时候就要改动源代码,不仅不利于测试而且也会让程序不便扩展,那么在Spring采用什么方式生成对象,这里就要涉及到DI,即依赖注入了
那么问题来了
依赖是谁依赖谁,当然是程序依赖于IOC容器(ApplicationContext或者BeanFactory)
为什么要依赖,因为程序不再通过new Object()生成对象,必须通过容器所提供的资源来创建对象
注入的是什么,是容器中的资源(包括对象,常量等)
那么IOC这种方式是怎样来生成对象的呢
首先,在程序中,有一个applicationContext.xml的文件,当然其他名字也没问题,我们得到对象的方式就是通过解析这个XMl文件,然后再反射来得到对象的
首先看一个applicationContext.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" xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="UserServiceImpl" class="com.example.demo1.Implements.UserServiceImpl" init-method="init" destroy-method="destroy" scope="prototype"> <constructor-arg name="service" ref="service"></constructor-arg> </bean> <bean id="service" class="com.example.demo1.Implements.UserServiceImpl"></bean> </beans>
这里的bean标签通俗的来说,如果把ApplicationContext比作一个工厂的话,bean就是这个工厂的产品,然后工厂根据他里面的id和class(即要生成的对象对应的实现类)来生成对象
解析xml文件,得到他的class的名字,通过反射找到该类,再生成对象,通过这个过程,我们把对象的控制权完全交给了spring,而不是之前的new Object()
通过解析XML得到对象的代码:
ApplicationContext instance = new ClassPathXmlApplicationContext("applicationContext.xml"); UserServiceImpl serviceImpl = (UserServiceImpl) instance.getBean("UserServiceImpl"); serviceImpl.save();//save为对象的一个方法
ClassPathXmlApplicationContext表示是在工程路径下面搜索该XML文件,如果要在电脑磁盘中寻找.需要用FileSystemApplicationContext
bean标签的相关属性
拿上面的XML为例子
id表示对象的唯一标识,也就是说这个不同的bean的id是不同的,他的命名规范和java变量的命名规范是一样的,不能出现特殊字符,比如?><这些,同时也不能纯数字
name的话,可以用特殊字符和纯数字但是不能出现空格,但是就不同的bean可以用同一个,就像人的身份证和名字一样,身份证只有一个,但名字可以是相同的,但是这仅建立在指定ID的的情况下
如果没有指定ID,name会被当做id,同样还是会报错
对比这两张截图
init-method属性是bean的初始方法,在创建好bean后调用该方法。
destory-method属性是bean的销毁方法,在销毁bean之前调用该方法,一般在该方法中释放资源
scope属性表示bean的作用范围,scope有4个值:
singleton:表示整个IOC容器共享一个Bean,也就是说每次说每次通过getBean获取的bean都是同一个。
prototype:每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
request:每次HTTP请求将会生成各自的bean实例
session:每次会话请求对应一个bean实例
后面两个一般用于web项目中
autowire表示bean的自动装配,autowire的值有:
no : 默认值,不进行自动装配
byName : 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配
byType : 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生,也可以通过设置dependency-check="objects" 让Spring抛出异常。
constructor:与byType方式类似,不同之处在于它应用于构造器参数。如果容器中没有找到与构造器参数类型一致的bean, 那么抛出异常
autodetect : 通过bean类的内省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式,否则采用 constructor。
default:由上级标签的default-autowire属性确定。
注入
1.setter的方法
2.构造的方法
测试类
package com.example.demo1.Implements; import com.example.demo1.Interface.UserService; public class UserServiceImpl implements UserService { private UserServiceImpl service; public void init(){ System.out.println("初始化"); } public void destroy(){ System.out.println("销毁"); } public UserServiceImpl(UserServiceImpl service){ this.service = service; } public void setService(UserServiceImpl service) { this.service = service; } public UserServiceImpl(){ } @Override public void save() { System.out.println("保存信息"+this.hashCode()); } }
<property name="service" ref="test"></property>对应setService()
<constructor-arg name="service" ref="service"></constructor-arg>对应构造函数
P名称空间注入
先在xml文件里添加一行
xmlns:p="http://www.springframework.org/schema/p"
对于非对象属性,直接采用P:属性名.即可以注入,例如我一个类中有name和password属性
那么在xml文件里便可以通过如下方式注入
<bean id="Client" class="com.example.demo1.Implements.UserClientImpl" p:name="lidong" p:password="123"> </bean>
SpEL表达式注入
SpEL全称SpringExpressionLanguage,SpEL 使用 #{...} 作为定界符 , 所有在大括号中的字符都将被认为是 SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利。
通过 SpEL 可以实现:
- 通过 bean 的 id 对 bean 进行引用。
- 调用方式以及引用对象中的属性。
- 计算表达式的值
- 正则表达式的匹配。
看代码吧还是
<bean id="UserClient" class="com.example.demo1.Implements.UserClientImpl"> <property name="name" value="#{555*123}"></property> <property name="password" value="#{123}"></property> </bean>
这里我在name标签处使用了一个乘法
然后打印出,可以看出这里面进行了运算