1、Spring之IOC理论
1.1 IOC的原型
我们程序员不再去管理对象的创建了,更多的去关注业务的实现 ,耦合性大大降低。
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现 ioc
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
测试:
public class UserGetTest {
@Test
public void getUser(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());
userService.getUser();
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
}
执行结果:
从bean中获取到的用户数据为User{name='hresh'}
从MySQL数据库中获取到的用户数据为User{name='acorn'}
1.2 IOC的本质
IOC(Inverse of Control:控制反转)是一种设计思想,就是原本需要在程序中手动创建对象的控制权,交给框架来管理。而IOC容器就是Spring实现IOC的载体,IOC容器实际上就是一个Map,Map中存放的是各种创建的对象。
将对象之间的相互依赖关系交给IOC容器管理,并由IOC容器完成相关的注入,将极大简化应用的开发,把程序从复杂的依赖关系中解救出来。IOC容器就像一个工厂,当我们需要创建一个对象的时候,只需要编写相关的注解/配置文件即可,完全不需要考虑如何去创建这个对象。
Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建对象保存到IOC容器中,当程序需要使用时再从IOC容器中取出所需要的对象。
采用XML配置Bean的时候,Bean的定义信息和实现是分开的。
而使用注解的方式可以将定义和实现集中到一起(在类上添加相关的注解)
1.3实战分析
1.3.1 使用配置文件实现
1、定义一个User类
@NoArgsConstructor
@AllArgsConstructor
@Data
@Accessors(chain = true)
public class User {
private String name;
}
2、配置文件配置
<?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-->
<bean id="user" class="com.linmain.demo2.bean.User">
<property name="name" value="hresh" />
</bean>
</beans>
3、测试类
public class demo2 {
@Test
public void getBeanByXml() {
// 解析demo2.xml ,得到相关的user文件信息
ApplicationContext context = new ClassPathXmlApplicationContext("demo2.xml");
// getBean 参数即为bean标签中的id
User user = (User) context.getBean("user");
System.out.println(user);
}
}
运行结果:
User(name=hresh)
思考
- User 对象是谁创建的?【user 对象是由 Spring 创建的】
- User 对象的属性是怎么设置的?【user 对象的属性是由 Spring 容器设置的】
- 这个过程就叫做控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用 Spring 后,对象是由 Spring 来创建的。
反转:程序本身不创建对象,而变成被动地接收对象。 - 依赖注入:利用 set 方法来进行注入的。
- IOC是一种编程思想,由主动的编程变成被动的接收 。
按照上述的方式我们对之前提到的业务场景进行修改。首先新增 一个 Spring 配置文件 demo3.xml
1.3.2 使用配置文件实现之前的demo的控制反转
1、类
@NoArgsConstructor
@AllArgsConstructor
@Data
@Accessors(chain = true)
public class User {
private String name;
}
public interface UserDao {
public void getUser();
}
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
User test = new User("test");
System.out.println("从bean中获取到的用户数据为:" + test);
}
}
public class UserDaoMysqlImpl implements UserDao {
@Override
public void getUser() {
User mysql = new User("mysql");
System.out.println("从mysql数据库中获取到的用户数据为:"+mysql);
}
}
public interface UserService {
public void getUser();
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoMysqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
2、配置文件配置
<?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="mysqlImpl" class="com.linmain.demo1.dao.impl.UserDaoMysqlImpl" />
<bean id="oracleImpl" class="com.linmain.demo1.dao.impl.UserDaoImpl" />
<bean id="serviceImpl" class="com.linmain.demo1.service.impl.UserServiceImpl">
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写 即setUserDao方法-->
<!--引用另外一个bean , 不是用value 而是用 ref-->
<property name="userDao" ref="mysqlImpl" />
</bean>
</beans>
3、测试类
public class demo3 {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("demo3.xml");
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("serviceImpl");
serviceImpl.getUser();
}
}
运行结果:
从mysql数据库中获取到的用户数据为:User(name=mysql)
1.4 IOC创建对象
1.4.1 无参构造器创建
1、类
@Data
@Accessors(chain = true)
public class User {
private String name;
public User() {
System.out.println("无参构造方法");
}
public User(String name) {
System.out.println("有参构造方法");
this.name = name;
}
}
2、配置文件
<?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">
<!-- 进行无参构造器的ioc-->
<bean id="user" class="com.linmain.demo4.bean.User">
<property name="name" value="hresh" />
</bean>
</beans>
3、测试
public class demo4 {
@Test
public void test() {
// 解析xml配置文件,生成管理相应的bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("demo4.xml");
// 在执行getBean的时候,user已经创建好了,属性是通过set方法注入的
User user = (User) context.getBean("user");
System.out.println(user);
}
}
运行结果:
无参构造方法
User(name=hresh)
1.4.2 有参构造器创建
1、类
@Data
@Accessors(chain = true)
public class User {
private String name;
public User() {
System.out.println("无参构造方法");
}
public User(String name) {
System.out.println("有参构造方法");
this.name = name;
}
}
2、配置文件
<?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="user" class="com.linmain.demo4.bean.User">
<constructor-arg name="name" value="youcan"/>
</bean>
</beans>
3、测试
public class demo5 {
@Test
public void test() {
// 解析xml配置文件,生成管理相应的bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("demo5.xml");
// 在执行getBean的时候,user已经创建好了,属性是通过set方法注入的
User user = (User) context.getBean("user");
System.out.println(user);
}
}
运行结果:
有参构造方法
User(name=youcan)
1.5 IOC涉及到的组件概括
在上面的测试中我们使用到的是ApplicationContext(应用上下文),具体实现是使用ClassPathXmlApplicationContext。我们在此分析一下ClassPathXmlApplicationContext的关系图(实线实现,虚线继承)
此图涉及了IOC容器在spirng中绝大部分的核心类,下面将简单讲解下主要的六大类型
1、Resource
Resource主要是实现了对资源的定义,它的实现类代表了对特定资源的特定访问策略。
如 ClasspathResource 、 URLResource ,FileSystemResource 等
2、ResourceLoader
有了资源之后,就需要有资源加载模块,Spring使用ResourceLoader 来实现对资源的加载。
3、BeanFactory
资源加载完毕之后就需要使用BeanFactory 进行加载解析了。它是一个Bean容器,它内部维护着一个BeanDefinition map,并可根据BeanDefinition 的描述进行bean的创建和管理。
BeanFacoty 有三个直接子类 ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory,DefaultListableBeanFactory 为最终默认实现,它实现了所有接口。
4、BeanDefiniBeanFation
BeanDefinition 用来描述Spring容器中的Bean对象。
5、BeanDefinitionReader
而BeanDefinitionReader 就是起到读取配置文件将其转化成BeanDefinition 保存起来的作用了。
6、ApplicationContext
ApplicationContext是个Spring容器,也叫做应用上下文,它继承了BeanFactory ,同时也是BeanFactory 的Plus版,结构决定了它和BeanFactory 的功能不同,主要区别有:
- 继承 MessageSource ,提供国际化的标准访问策略;
- 继承 ApplicationEventPublisher,提供强大的事件机制;
- 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源;
- 对 Web 应用的支持。
1.6 Spring中XML配置
1.6.1 别名alias
alias:为bean设置别名,可以设置多个别名
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>
1.6.2 Bean的配置
<!--bean就是java对象,由Spring创建和管理-->
<!--
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.msdn.bean.Hello">
<property name="name" value="Spring"/>
</bean>
1.6.3 import
当有多个关于 bean 定义的文件,最后可以集中在一个文件中。
<import resource="{path}/beans.xml"/>