目录
1.IOC容器
- ioc不是什么技术,而是一种设计思想,就是将原本在程序手中创建对象的权力,交给spring框架来管理。
- 以往的思路:若使用某个对象,需要自己负责对象的创建;
- 反转的思路:若使用某个对象,需要从spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权转给了spring框架;
- 好莱坞法则:Don’t call me,I’ll call you
下面以家政服务为例:
过年了,家里想要打扫卫生,想要请一个家政服务帮你打扫卫生,由以下两种做法:
(1)自己主动打电话,询问查找,自己谈价钱;
(2)直接打电话给家政公司,提出要求即可。
那会有人问了,那家政公司从哪里来啊!
1.自行构建
我们可以使用配置文件,或者注解的方式定义下咱们自己容器里面存放的东西。
2.使用别人的
一定会有很多有钱人,成立自己的各类公司,他们的这些服务都可以继承在咱们的容器里,为我们提供很多强大的功能,比如spring自带的很多template模板类。
1.1代码示例
1.新建maven工程
pom文件添加
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-study</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency> <!-- 引入web直接会将所有的依赖项全部依赖过来 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source> <!-- 源代码使用的JDK版本 -->
<target>1.8</target> <!-- 需要生成的目标class文件的编译版本 -->
<encoding>UTF-8</encoding><!-- 字符集编码 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
2.新建user实体类
3.新建beans.xml配置文件
4.新建测试类
1.2详细说说其中的几个对象
1.2.1ApplicationContext
ApplicationContext是spring继BeanFactory之外的另一个核心接口或容器,允许容器通过应用程序上下文环境创
建、获取、管理bean。为应用程序提供配置的中央接口。在应用程序运行时这是只读的。
一个ApplicationContext提供:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { }
访问应用程序组件的Bean工厂方法。从org.springframework.beans.factory.ListableBeanFactory继承
。以通用方式加载文件资源的能力。继承自ResourcePatternResolver 接口
。- 向注册侦听器发布事件的能力。继承自ApplicationEventPublisher接口。
- 解析消息的能力,支持国际化。继承自MessageSource接口
1.2.2ConfigurableApplicationContext
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { }
ClassPathXmlApplicationContext是ConfigurableApplicationContext的一个实现,
该类提供了很多记载资源的方式,该类可以加在类路劲下的xml资源文件,当然如果想加在其他地方的资源,可以使用FileSystemXmlApplicationContext这个类
。
/**
* Create a new ClassPathXmlApplicationContext,
* loading the definitions * from the given XML file and automatically refreshing the context.
* @param configLocation resource location * @throws BeansException if context creation failed
*/
public ClassPathXmlApplicationContext (String configLocation) throws BeansException {
this (new String[]{configLocation}, true, null);
}
2.DI依赖注入
对象是有属性的,属性是需要赋值的。通常的方式是:(new对象,set赋值,get取值)
Student student = new Student ();
student.setName("张三");
student.setAge(23);
Teacher teacher = new Teacher ();
student.setTeacher(teacher);
归根结底,都需要我们自己去赋值。
而控制反转(IOC)也叫依赖注入(DI)的核心思想是,构建对象(包括初始化和赋值)都不需要人为操作,而是将
这个权利交付给容器来进行。
3.IOC好处
- 第一、USB设备作为电脑主机的外部设备,在插入主机之前,与电脑主机没有任何的关系,只有被我们连接在
一起之后,两者才发生联系,具有相关性。所以,无论两者中的任何一方出现什么的问题,都不会影响另一
方的运行。这种特性体现在软件工程中,就是可维护性比较好,非常便于进行单元测试,便于调试程序和诊
断故障。代码中的每一个Class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是 组件之间低耦合或者无耦合带来的好处。 - 第二、USB设备和电脑主机的之间无关性,还带来了另外一个好处,生产USB设备的厂商和生产电脑主机的厂
商完全可以是互不相干的人,各干各事,他们之间唯一需要遵守的就是USB接口标准。这种特性体现在软件
开发过程中,好处可是太大了。每个开发团队的成员都只需要关心实现自身的业务逻辑,完全不用去关心其
它的人工作进展,因为你的任务跟别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人
的组件,再也不用扯不清责任了。所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一
个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。 - 第三、同一个USB外部设备可以插接到任何支持USB的设备,可以插接到电脑主机,也可以插接到DV机,
USB外部设备可以被反复利用。在软件工程中,这种特性就是可复用性好,我们可以把具有普遍性的常用组件
独立出来,反复利用到项目中的其它部分,或者是其它项目,当然这也是面向对象的基本特征。显然,IOC不
仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现,都可以插接到支持此标准的模块 中。 - 第四、同USB外部设备一样,模块具有热插拔特性。IOC生成对象的方式转为外置方式,也就是把对象生成放
在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完 全具有热插拨的特性。
4.依赖注入的方式
4.1构造器注入
1.新建Dog实体类
2.新建dogBean.xml
3.测试类
4.2set方法注入
注意:
(1)要求被注入的属性 , 必须有set方法
。
(2)set方法的方法名由set + 属性首字母大写
。
(3)如果属性是boolean类型 , 没有set方法 , 是 is + 属性首字母大写
。
1.新建Address.java(采用lombok的@Data注解,自动生成get和set方法)
2.新建user2.java
3.新建user2Bean.xml(演示多种类型参数注入)
完整的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.xsd">
<bean id="address" class="com.wshy.example.entity.Address">
<property name="addressInfo" value="珠琳旺角大厦"/>
</bean>
<bean id="user" class="com.wshy.example.entity.User2">
<!-- 基本属性注入 -->
<property name="name" value="wshy"/>
<!-- 引用类型注入 -->
<property name="address" ref="address"/>
<!-- 数组注入 -->
<property name="hobbies">
<array>
<value>写程序</value>
<value>吃</value>
<value>喝</value>
<value>漂亮姑娘</value>
<value>赢钱</value>
</array>
</property> <!-- list注入 -->
<property name="duties">
<list>
<value>IT老师</value>
<value>我儿子的爸爸</value>
</list>
</property> <!-- set注入 -->
<property name="carts">
<set>
<value>纸尿裤</value>
<value>玩具</value>
</set>
</property> <!-- map注入 -->
<property name="familyTies">
<map>
<entry key="father" value="张某某"/>
<entry key="mather" value="钟某某"/>
</map>
</property> <!-- property注入 -->
<property name="workExperience">
<props>
<prop key="first">电厂职工</prop>
<prop key="second">java开发工程师</prop>
<prop key="third">java讲师</prop>
</props>
</property> <!-- null注入 -->
<property name="daughter">
<null/>
</property>
</bean>
</beans>
4.测试类
5.Bean的作用域
Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,作用域包括singleton(单例模式,默认)、
prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)。
5.1Singleton
当一个bean的作用域为Singleton
那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则
只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管
你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用
域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="userServiceImpl" class=""com.xinzhi.service.UserServiceImpl">
1.默认为单例
2.创建两个示例对象,判断是否相等,两个引用类型使用“==”,比较的是引用指向的地址。
返回true表示为同一个实例对象,即单例。
5.2Prototype
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的
getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例
化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经
验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将
bean定义成prototype,可以这样配置:
<bean id="account" class="com.xinzhi.entity.User" scope="prototype"/>
1.scop设置为prototype
2.结果为false,s说明两个对象指向不同的地址,为多例。
5.3Request
当一个bean的作用域为Request
表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个
bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="loginAction" class="com.xinzhi.entity.User" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且
该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其
他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,
request作用域的bean实例将被销毁。
5.4Session
(4)当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅
在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="userPreferences" class="com.xinzhi.entity.User" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences
bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放
心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定
于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被
废弃掉。
6.自动注入
spring作为长期以来java最火的框架,其IOC做的十分的健全,以上情况都是我们手动装配,但是我们也说了
spring及其灵活的帮助我们完成了解耦工作,如果所以的类都是自己手动完成注入,他的解耦能力就不会体现的那
么强烈了,于是spring还为我们提供了自动装配的能力
只要我们的Beans满足bean之间的依赖,且这些bean都存在于容器,且唯一,那么我么就可以按照约定进行bean
的自动装配。同时还能大量的减少配置文件的数量。
6.1按名称自动装配(byName)
过程:
1.从javabean的set方法获取名字,如setAddress,就去spring容器中找名字为address的bean。
2. 如果有,就取出注入;如果没有,就不注入。
1.修改bean.xml,增加一个属性 autowire=“byName”,user2中有一个属性为address,使用byName时,可以自动注入address的bean,需要将手动注入的property删除,若不删除,则自动注入不会生效,以手动注入为先。
2.测试,address仍然被注入成功
注意:若地址的bean为address2,而user2中的属性为address,此时只会寻找bean的name为address的bean,address2就不会被注入了
。测试如下:
6.2按类型自动装配(byType)
注意:按照类型注入,容器内的bean必须是单例
1.在bean.xml中增加autowire="byType“
2.测试结果
注意:如果xml中配置类型有两个,会发生报错,idea会直接报错,如下:
原因:按照类型注入,容器内的bean必须是单例
。
7.基于注解开发
7.1自动装配
步骤:
1.在bean.xml配置文件需要加头文件
2.在javaBean类中的对应属性上增加@Autowired注解
3.测试结果
注意:@autowired注解默认按类型装配,所以当出现多个bean时,xml会不知道选择哪个bean,此时可以在@autowired注解上配合@Qualifier注解,指定使用哪个bean注入。例如:
注意:@Qualifier不能单独使用,它和@Autowired配合可以根据名称进行自动装配
。
@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配,写了啥名字就找哪个;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
7.2分层开发
@Controller,在controller中标注注解
@Service,在service层使用
@Repository,在Dao层使用
1.service层:
@Service
public class UserServiceImpl implements IUserService {
//自动注入该类型的dao的实现
@Autowired private IUserDao userDao;
public void register(User user) {
System.out.println(user.getName()+"注册成功!");
userDao.saveUser(user);
}
}
2.Dao层
@Repository
public class UserOracleDao implements IUserDao {
public int saveUser (User user) {
System.out.println ("我是将数据保存至oracle");
return 1;
}
}
7.3组件开发
1.xml配置文件只留下扫包,其他全部注释
2.address.java的bean加入@Component和@Value注解
3.User.java的bean上添加@Component和@Value注解,其中address为@Resource注解
4.结果,注入成功”贾汪区“和”wshy“
7.4配置文件
@Bean
@Configuration
xml类配置同组件开发时的配置,只留扫包,其他删除或注释掉
1.新建Cat类
2.新建mouse类
3.新建cat_config类,由于@Bean中mouse已经存在,在cat的@Bean注解时,会自动获取容器中已经存在的bean
4.新建测试类