您现在的位置是:首页 >技术教程 >Spring-ioc网站首页技术教程
Spring-ioc
文章目录
一、IOC容器
IOC:Inversion of Control,翻译过来是反转控制。
①获取资源的传统方式
必须清楚了解资源创建整个过程中的全部细节且熟练掌握。
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
②反转控制方式获取资源
不必关心资源创建过程的所有细节。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式
③DI
DI:Dependency Injection,翻译过来是依赖注入。
DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器
的资源注入。相对于IOC而言,这种表述更直接。
所以结论是:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现。
二、基于XML管理bean
步骤为
①创建Maven Module
②引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
③创建类
④创建Spring的配置文件
⑤在Spring的配置文件中配置bean
<!--
配置HelloWorld所对应的bean,即将对象交给Spring的IOC容器管理
通过bean标签配置IOC容器所管理的bean
属性:
id:设置bean的唯一标识
class:设置bean所对应类型的全类名
-->
<bean id="helloworld" class="com.atguigu.spring.bean.HelloWorld"></bean>
⑥创建测试类测试
三、获取bean
3.1 方式一 根据id获取
由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。
HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld");
3.2 方式二 根据类型获取
HelloWorld bean = ac.getBean(HelloWorld.class);
3.3 方式三 根据id和类型获取
HelloWorld bean = ac.getBean("helloworld", HelloWorld.class);
3.4 注意事项
一般情况下一个类型我们只会配置一个bean 所以方式二是使用的最多的方式
四、依赖注入
4.1 setter注入
<bean id="studentOne" class="com.atguigu.spring.bean.Student">
<!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关)-->
<!-- value属性:指定属性值 -->
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
4.2 构造器注入
<bean id="studentTwo" class="com.atguigu.spring.bean.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="33"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>
这两个注入方式一个是通过set方法给属性赋值,一个是通过构造方法给属性赋值,所以当使用setter注入时需要保证有set方法,
使用构造器注入则要保证有 有参构造方法
五 为类类型属性赋值
方式一:引用外部已声明的bean
首先先声明一个bean
<bean id="clazzOne" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="1111"></property>
<property name="clazzName" value="财源滚滚班"></property>
</bean>
这样就给一个bean赋好了值
然后
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="clazz" ref="clazzOne"></property>
</bean>
ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值
这里就是引用了id为clazzOne的bean给clazz类赋了值
方式二:内部bean
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="clazz">
<bean id="clazzInner" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="2222"></property>
<property name="clazzName" value="远大前程班"></property>
</bean>
</property>
</bean>
在一个bean中再声明一个bean就是内部bean
内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性
方式三:级联属性赋值
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="clazz" ref="clazzOne"></property>
<property name="clazz.clazzId" value="3333"></property>
<property name="clazz.clazzName" value="最强王者班"></property>
</bean>
一定先引用某个bean为属性赋值,才可以使用级联方式更新属性
我们最常用的还是第一种方法
六 为数组类型属性赋值
为数组类型赋值就不能直接在 property里使用value了 需要通过array标签来赋值
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</bean>
七 为集合类型属性赋值
①为List集合类型属性赋值
和数组类似也是不能直接在 property里使用value 需要用list标签
<bean id="clazzTwo" class="com.atguigu.spring.bean.Clazz">
<property name="students">
<list>
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</list>
</property>
</bean>
②为Map集合类型属性赋值
和list类似 只不过标签变成了map 而map标签中又有entry标签 通过entry标签中的key 和value来赋值 赋值可以自己输入值,同时也可以使用ref来引用外部bean来赋值
<bean id="studentFive" class="com.atguigu.spring.pojo.Student">
<property name="teacherMap">
<map>
<entry key="10086" value-ref="teacherOne"></entry>
<entry key="10010" value-ref="teacherTwo"></entry>
</map>
</property>
</bean>
这里就是使用了外部的bean来赋值
③引用集合类型的bean
配置一个集合类型的bean,需要使用util的约束
这里List和Map集合都是差不多的
<util:list id="studentList">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>
<util:map id="teacher">
<entry key="10086" value-ref="teacherOne"></entry>
<entry key="10010" value-ref="teacherTwo"></entry>
</util:map>
设置好后直接引用就可以了
<bean id="clazzOne" class="com.atguigu.spring.pojo.Clazz">
<property name="students" ref="studentList"></property>
</bean>
八 bean的生命周期
具体的生命周期过程
1.实例化
bean的创建是默认调用无参构造,创建对象。
在无参构造中加入代码
public User() {
System.out.println("生命周期1:实例化");
}
2.依赖注入
依赖注入则是通过setter注入或者构造器注入 这里举例setter注入 而setter注入是通过set方法 于是在set方法中加入代码
public void setId(Integer id) {
System.out.println("生命周期2:依赖注入");
this.id = id;
}
3.后置处理器的postProcessBeforeInitialization
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口, 且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容 器中所有bean都会执行
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之前执行
System.out.println("后置处理器-postProcessBeforeInitialization");
return bean;
}
}
4.初始化
注意其中的initMethod()和destroyMethod(),可以通过配置bean指定为初始化和销毁的方法
<bean id="user" class="com.atguigu.spring.pojo.User" init-method="initMethod">
public void initMethod()
{
System.out.println("生命周期3:初始化");
}
5.后置处理器的postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之后执行
System.out.println("后置处理器-postProcessAfterInitialization");
return bean;
}
6.IOC容器关闭时销毁
注意其中的initMethod()和destroyMethod(),可以通过配置bean指定为初始化和销毁的方法
<bean id="user" class="com.atguigu.spring.pojo.User" destroy-method="destroyMethod">
public void destroyMethod()
{
System.out.println("生命周期4:销毁");
}
注意
若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行
若bean的作用域为多例时,生命周期的前三个步骤会在获取bean时执行
测试
public void test()
{
//ConfigurableApplicationContext是ApplicationContext子接口提供了刷新和关闭容器的方法
ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("spring-lifeCycle.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
ioc.close();
}
结果为
九 基于xml的自动装配
自动装配:
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值
场景模拟
创建类UserController
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
创建接口UserService
public interface UserService {
void saveUser();
}
创建类UserServiceImpl实现接口UserService
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
创建接口UserDao
public interface UserDao {
void saveUser();
}
创建类UserDaoImpl实现接口UserDao
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
配置bean
创建类UserServiceImpl实现接口UserService
使用bean标签的autowire属性设置自动装配效果
自动装配方式:byType
byType:根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值
若在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配,即值为默认值null
若在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException
<bean id="userController"
class="com.atguigu.autowire.xml.controller.UserController" autowire="byType">
</bean>
<bean id="userService"
class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byType">
</bean>
<bean id="userDao" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl"></bean>
自动装配方式:byName
byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值
<bean id="userController"
class="com.atguigu.autowire.xml.controller.UserController" autowire="byName">
</bean>
<bean id="userService"
class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byName">
</bean>
<bean id="userServiceImpl"
class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byName">
</bean>
<bean id="userDao" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoImpl" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl">
</bean>
例如UserController有一个类userService; byname方式则会寻找同名的bean并赋值
十、基于注解管理bean
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测
到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行
@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
情况一:最基本的扫描方式
<context:component-scan base-package="com.atguigu.spring">
</context:component-scan>
情况二:指定要排除的组件
context:exclude-filter标签:
type:设置排除或包含的依据
type=“annotation”,根据注解排除,expression中设置要排除的注解的全类名
type=“assignable”,根据类型排除,expression中设置要排除的类型的全类名
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="assignable" expression="com.atguigu.controller.UserController"/>
</context:component-scan>
情况三:仅扫描指定组件
context:include-filter标签:指定在原有扫描规则的基础上追加的规则
use-default-filters属性:取值false表示关闭默认扫描规则
此时必须设置use-default-filters=“false”,因为默认规则即扫描指定包下所有类
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable" expression="com.atguigu.controller.UserController"/>-->
</context:component-scan>
基于注解的自动装配
@Autowired注解
在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法。以后我们在项
目中的正式用法就是这样。
例如
public class UserController {
@Autowired
private UserService userService;
public void chi()
{
System.out.println("猛吃");
}
public void saveUser(){
userService.saveUser();
}
}