Spring简介、IOC控制反转

Spring简介

Spring用于减轻项目模块之间的管理,类与类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
Spring核心技术是IOC,AOP。能实现模块之间的,类之间的解耦合。
优点:
1、轻量
2、针对接口编程,IOC解耦合
3、AOP面向切面编程
4、方便集成其他各种框架

Spring体系结构

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。

Spring DAO, Spring Web,SpringMVC,Spring AOP,Spring上下文,Spring ORM,核心容器。所以,Spring是一系列轻量级Java EE框架的集合。

IOC控制反转

Inversion of control:控制反转,是一个概念,一种思想。
是指把对象的创建、赋值、管理工作都交给代码以外的容器实现,控制反转就是对对象控制权的转移,由程序代码本身反转到了程序外部容器。
控制:创建对象,对象属性的赋值,对象之间关系的管理。
反转:把原来开发人员管理,创建对象的权力交给代码之外的容器。由容器代替开发人员管理对象。
正转:由开发人员在代码中,使用new 构造方法 创建对象,开发人员主动管理对象。
容器:一个服务器软件,一个框架(Spring)。
IOC的好处:减少对代码的改动,也能实现不同的功能。实现解耦合。
IOC的体现:Servlet ,Tomcat作为容器创建Servlet对象。
IOC的技术实现:
DI(Dependency Injection) 依赖注入,只需要在程序中提供对象的名称,至于对象是如何在容器中创建,赋值,查找都是由容器内部实现的。
Spring使用DI实现了IOC的功能,Spring底层创建对象使用的是反射机制。
Spring是一个容器。

使用Spring

1、使用Maven创建一个普通的java项目

2、引入Spring依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>

插件可加可不加,指明jdk版本(前面已经指定)

1
2
3
4
5
6
7
8
9
10
11
12
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>ss
</plugin>
</plugins>
</build>

3、创建Spring配置文件

bean.xml

1
2
3
4
5
6
7
8
9
<?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标签声明一个对象-->
<bean id="someService" class="com.zyz.service.impl.SomeServiceImpl"/>
</beans>

4、使用spring创建对象,调用对象方法

1
2
3
4
5
6
7
8
9
10
11
12
// 使用Spring 容器创建对象
@Test
public void test2(){
// 1、指明spring配置文件名称
String config = "beans.xml";
// 2、创建spring容器对象 ApplicationContext
// 从类路径加载配置文件(创建配置文件中的所有对象)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
// 3、从容器中获取对象
SomeService someService = (SomeService) applicationContext.getBean("someService");// 通过id
someService.doSome();
}

Bean定义的方式

基于xml文件

1
2
3
4
5
6
7
8
9
10
11
<!--普通类型  set注入-->
<bean id="myPerson" class="com.zyz.bean.Person">
<property name="id" value="10001"/>
<property name="name" value="Tony"/>
<!-- 引用类型 set注入-->
<property name="school" ref="mySchool"/>
</bean>
<bean id="mySchool" class="com.zyz.bean.School">
<property name="id" value="1001"/>
<property name="schoolName" value="长江大学"></property>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--  构造注入
constructor-arg 形参属性
name 构造方法形参名
index 表示构造方法的参数位置 从左到右0,1,2 ...
普通类型 value
引用类型 ref
-->
<bean id="myPersonConstructor" class="com.zyz.bean.Person">
<constructor-arg name="id" value="10002"/>
<constructor-arg name="name" value="zyz"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
<bean id="mySchool" class="com.zyz.bean.School">
<property name="id" value="1001"/>
<property name="schoolName" value="长江大学"></property>
</bean>
1
2
3
4
5
6
7
8
9
<bean id="myPersonConstructor2" class="com.zyz.bean.Person">
<constructor-arg index="0" value="10003"/>
<constructor-arg index="1" value="wd"/>
<constructor-arg index="2" ref="mySchool2"/>
</bean>
<bean id="mySchool2" class="com.zyz.bean.School">
<property name="id" value="1002"/>
<property name="schoolName" value="清华大学"/>
</bean>

引用类型自动注入:

  • byName:java类中引用类型属性名 与spring配置文件中的bean标签中的id值相同

    1
    2
    3
    4
    5
    6
    7
    8
    <bean id="myPersonConstructor2" class="com.zyz.bean.Person" autowire="byName">
    <constructor-arg index="0" value="10003"/>
    <constructor-arg index="1" value="wd"/>
    </bean>
    <bean id="school" class="com.zyz.bean.School">
    <property name="id" value="1002"/>
    <property name="schoolName" value="清华大学"/>
    </bean>
  • byType:使用byType 方式自动注入,要求:配置文件中被调用者bean 的class 属性指定的类,要与代码中调用者bean 类的某引用类型属性类型同源。即要么相同,要么有is-a 关系(子类,或是实现类)。但这样的同源的被调用bean 只能有一个。

    1
    2
    3
    4
    5
    6
    7
    8
    <bean id="person" class="com.zyz.bean.Person" autowire="byType">
    <property name="id" value="10004"/>
    <property name="name" value="wd788"/>
    </bean>
    <bean id="school" class="com.zyz.bean.School">
    <property name="id" value="1001"/>
    <property name="schoolName" value="北京大学"/>
    </bean>

基于注解

1
2
// value相当于bean标签中的id值
@Component("myStudent")

在配置文件中声明组件扫描器

1
2
<!--   声明组件扫描器  组件就是java对象-->
<context:component-scan base-package="com.zyz.component"/>

另外,Spring 还提供了3 个创建对象的注解:
@Repository 用于对DAO 实现类进行注解
@Service 用于对Service 实现类进行注解
@Controller 用于对Controller 实现类进行注解
这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。

@Autowired注入

  • byType自动注入(默认)

    1
    2
    @Autowired
    private School school;
  • 属性赋值:

    1
    2
    3
    4
    5
    6
    7
    @Component
    public class School{
    @Value("1001")
    private Integer id;
    @Value("北京大学")
    private String schoolName;
    }
  • byName自动注入(当一个接口有多个实现类时,需要使用此方式注入)

    1
    2
    3
    @Autowired
    @Qualifier("mySchool")
    private School school;
  • 属性赋值:

    1
    2
    3
    4
    5
    6
    7
    @Component("mySchool")
    public class School{
    @Value("1001")
    private Integer id;
    @Value("北京大学")
    private String schoolName;
    }

JDK注解@Resource自动注入

  • byName(默认)。如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略,如果找不到相同名称的,就会去找相同类型的,再找不到才报错。

    1
    2
    @Resource
    private School school;

bean 作用域

Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。

1、singleton:单例,默认作用域。

2、prototype:原型,每次创建一个新对象。

3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。

4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。

5、global-session:全局会话,所有会话共享一个实例。

对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。

如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
注: Spring容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于Bean本身的特性。

使用ThreadLocal

使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。

bean的生命周期

实例化 -> 属性赋值 -> 初始化 -> 销毁

  1. 通过构造器或工厂方法创建bean实例

  2. 为bean的属性设置值和对其他bean的引用

  3. 将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法

  4. 调用bean的初始化方法

  5. 将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法

  6. 使用bean

  7. 当容器关闭时调用bean的销毁方法

Spring 中使用的设计模式

  • 工厂设计模式 : Spring使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
  • ……