理解IoC
bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。
Spring通过IoC的方式将用户管理bean转变为框架来管理bean
不仅接管Bean的生成。同时接管其整个生命周期
管理的bean存放在IoC Container中,应用程序代码从Ioc Container中获取依赖的bean,注入到应用程序中,这个过程叫 依赖注入(DI)
IoC是设计思想,DI是实现方式
在传统的Java SE程序设计中,对象的创建和它所需要依赖对象的创建和注入都是由我们来完成的,例如
1 | UserServiceImpl userService = new UserServiceImpl();//service层对象的创建 |
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,而是直接从IoC容器中获取
1 | UserServiceImpl service = (UserServiceImpl)context.getBean("userService"); |
IoC配置
xml配置
此种配置方式,将bean的创建和依赖注入方式写入了xml文件。然后将xml配置文件传给spring的特定类。从而根据这些配置创建出bean并且注入相关的依赖。
xml文件格式
1 |
|
在xml文件的beans标签中完成bean的创建和依赖的配置。
bean的创建
1 | <bean id="userDaoImplBean" class="com.shijivk.dao.impl.UserDaoImpl"/> |
对于没有依赖类的bean,使用自结束标签创建,而有依赖类的则需使用一对标签来创建。
id属性表示要创建的bean的名称,id是唯一的,用于标识容器中的一个bean。
bean默认采用单例的方式,也就是对于多次getBean只会在容器中创建一个对象。每次返回的对象相同
可以看到上图中两次的地址相同。将bean标签中的scope属性设置为prototype可以切换为多例。
name属性可以同时创建多个同类型的对象,例如
1 | <bean id = "bean1" name="bean2 bean3" class="com.shijivk.dao.impl.UserDaoImpl" |
就会同时创建三个名字分别为bean1,bean2,bean3的bean
class属性表示所要创建的bean对应的实现类
依赖注入
依赖注入即让spring给类中的基本类型或是一些类的指针赋值,我们通常通过setter或者是构造函数向一个类中传入数据。因此便有了setter注入和构造器注入。
setter注入
主要做两件事:
告知spring要将哪个bean赋值给类里面的哪个对象
提供setter方法
1 | <bean id="userServiceImplBean" class="com.shijivk.service.impl.UserServiceImpl"> |
通过property标签,告诉spring将ref属性指定的Bean赋值给类里面名叫userDao的变量。
1 | <bean id="userServiceImplBean" class="com.shijivk.service.impl.UserServiceImpl"> |
对于基本类型的注入,则使用value属性
构造器注入
1 | <bean id="userServiceImplBean" class="com.shijivk.service.impl.UserServiceImpl"> |
1 | public class UserServiceImpl { |
将ref中的bean,赋值给name对应的形参(注意对应的是形参名而不是类内的变量名)。同时通过调用构造函数
这一构造函数可以为public也可以为private。spring采用反射的方式。即使为private也可正确调用。
1 | <constructor-arg name="userName" value="ZhangSan"/> |
对于基本类型可以采用如上方式。
但这样的方式还是会有一定的耦合。因为name对应着的为类内的形参,二者的名字要保持一致。因此可以采用如下的方式来降低耦合
采用类型来匹配(要求各形参类型不同)
1 | <constructor-arg type="int" value="10"/> |
采用位置来匹配
1 | <constructor-arg index="0" value="10"/> |
自动装配
在满足一定的条件时可以让spring自动的完成依赖的注入
主要有两种方式:
按类型(byType)
1 | <bean id="userDaoImplBean" class="com.shijivk.dao.impl.UserDaoImpl"/> |
当依赖的类只有一个bean的时候。可以让spring自动的完成注入。但如果有多个类型相同的bean时则会报错
按名称(byName)
1 | <bean id="userDaoImplBean" class="com.shijivk.dao.impl.UserDaoImpl"/> |
此时spring会自动寻找UserServiceImpl类中的名为userDaoImplBean的变量。并自动的将这个bean赋值给它。
即id的值应该与类中对应类型的变量的名字相同。
补充:Spring默认查找的不是原变量,而是对应Setter的名字。例如userDaoImplBean,如果我们根据规范来命名setter,应该为setUserDaoImplBean,Spring会截取后半部分,然后判断是否和name中的值相同。如果相同就使用这个setter来注入。
byName方式一般不推荐使用。因为会提高耦合度。
注解配置
使用注解的方式配置bean,并使用配置类来替代xml文件
配置类
我们希望改变使用xml配置的方式,用java配置类来代替xml
首先创建一个配置类。在其上方加@Configuration注解即可将其指定为配置类
1 |
|
bean的创建
使用注解定义bean有两种方式,分别针对不同的使用场景
方案1:针对第三方类(不修改使用类的源代码)
对于第三方类,我们无法修改其源代码,例如在其中添加@Component注解,所以我们直接在配置类中定义bean
1 |
|
在配置类中。我们需要提供一个方法。返回值即为bean所对应的对象。需要在方法上方加上@Bean注解,其中参数可以忽略。若忽略,bean的id即为方法名(首字母小写),若未忽略,则bean的id即为注解的参数。
依赖注入:
对于此种配置方式,只需要在形参中写上需要的类型,Spring就会默认采用byType的方式来为该形参赋值,例如
1 |
|
Spring在查找到UserDao这个类型的bean之后,将其赋值给我们下面userService函数的形参,然后我们再手动的将其注入到bean中。
注意:Spring在此处仅仅是找到对应类型的bean,然后赋值给形参。其并不会完成依赖的注入。剩余对bean的赋值操作需要我们手动来完成。
方案2:在可以修改引用类的源代码时
在需要创建对应bean的类上方加上@Component注解,例如
1 |
|
这样便定义好了一个id为userDao的bean,相当于如下配置
1 | <bean id="userDaoImplBean" class="com.shijivk.dao.impl.UserDaoImpl"/> |
此外@Component还有三个衍生注解
@Controller:用于表现层bean的定义
@Service:用于业务层bean的定义
@Repository:用于数据层bean的定义
这三个衍生注解的效果与@Component相同,只是更便于看出所定义的bean属于哪一个层。
对于此种方案,在定义好了bean之后,我们还需要让Spring来查找到我们到底定义了哪些bean。需要告知Spring扫描的位置。
若采用xml的方式。我们可以在beans标签中填上如下标签(此时不需要配置类,因为配置类是代替xml的)
1 | <context:component-scan base-package="com.shijivk"/> |
这时Spring便会扫描com.shijivk下的所有类。找出有@Component及其衍生注解的类。并创建bean
同时我们还需要一个注解来代替我们上面的标签。让Spring去扫描。因此加上@ComponentScan注解,如下:
1 |
但如果相加多个扫描路径。用数组的格式,例如
1 |
这样,便完成了采用注解定义和创建bean
依赖注入
在纯注解的开发模式下,采用自动装配的方式。在需要被依赖注入的类中加入@Autowired注解,Spring会自动采取byType的匹配方式
1 |
|
而如果同一个类型有多个bean,则可以在@Autowired注解下方加上@Qualifier注解。指定对应的bean
对于基本类型的注入。使用@value注解
1 |
|
这一注解内的内容,还可以移入一个properties文件中,以减少代码中的信息
在java配置类的上方加上@PropertySource注解,属性为配置文件类路径。例如config.properties,多文件要使用数组的形式。
在配置文件中写上对应的值,例如name=ZhangSan
然后将原value注解的内容改为
1 | @Value("${name}") |
使用容器
创建容器
通过xml文件配置的容器创建方式如下
1 | ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
ClassPathXmlApplicationContext内的路径为类路径
FileSystemXmlApplicationContext内的路径为文件系统路径
通过注解配置的容器创建方式如下
1 | ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); |
AnnotationConfigApplicationContext内的为java配置类
获取bean
1 | BookDao bookDao = (BookDao)ctx.getBean("bookDao"); |