一. Spring的概述
Spring是Java生态中最为成功的框架,他的核心思想是控制反转(Inverse of Control)和面向切面编程(Aspect Oriented Programming)。Spring的优势:
- Spring可以通过配置(XML或注解)来扩展POJO的功能,通过依赖注入实现系统解耦
- 使用AOP技术消除了系统中事务的try…catch…finally模板代码,通过声明式事务技术使得开发人员可以专注于业务逻辑
- Spring提供了很对模板类来整合其他优秀的开源框架,统一模板简化开发,例如HibernateTemplate,RedisTemplate等
控制反转(IoC) 的核心思想就是将程序中对象创建的主动权由程序本身转为交由第三方创建(IoC容器)! 例如程序中的Controller,Service,Repository以及各种第三方框架的组件(@Component / @Bean)的初始化交由Spring的IoC容器,开发人员只需要通过描述的方式(XML or 注解)将这些组件注册到spring容器中即可。Spring容器在初始化的时候会创建容器中注册的组件,并且通过解析组件的依赖关系(@Autowired)完成依赖对象的注入。所以当程序从容器中获取指定组件的时候,容器确保组件已经完成初始化,并且已经注入所依赖的组件,达到开箱即用。
二. 在Spring IoC 容器中装配Bean
1. 2种依赖注入的方式
Spring中依赖注入主要分为:构造方法注入和属性注入。构造方法注入 需要在Bean中提供有参的构造方法,组件通过参数注入。
public class UserController {
private UserService userService;
public UserService getUserService() {
return userService;
}
// 属性的setter方法
public void setUserService(UserService userService) {
this.userService = userService;
}
// 无参构造器
public UserController(){}
// 通过构造方法的参数注入组件对象
public UserController(UserService userService){
this.userService = userService;
}
}
在applicationContext.xml配置文件中为UserController组件注入UserService对象:
<?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">
<!-- 通过XML完成组件的注册 -->
<bean id="userController" class="com.znker.controller.UserController">
<!-- 通过构造方法完成对象的注入,ref标签通过id引用容器中的Bean -->
<constructor-arg index="0" ref="userService"/>
</bean>
<bean id="userService" class="com.znker.service.UserService" />
</beans>
这是比较原始的注入方法,constructor-arg元素用于定义类构造方法的参数,index指定入参顺序,value引用为属性注入的值。
属性注入 是通过属性对应的setter方法注入。
<?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">
<!-- 通过XML完成组件的注册 -->
<bean id="userController" class="com.znker.controller.UserController">
<!-- 使用setter方法注入,因为使用反射技术,Bean需要提供一个无参构造器
property元素:指定需要注入的属性
value元素:引用容器中Bean对象完成注入,根据Bean名称注入
-->
<property name="userService" ref="userService"/>
</bean>
<bean id="userService" class="com.znker.service.UserService" />
</beans>
2. 装配容器中的Bean
Spring将自己开发的Bean和第三方类库Bean注册到容器中主要有三种方法:
- 在XML文件显式配置(主要是第三方类库,例如DataSource,RedisTempalte…)
- 使用注解,Bean的扫描和注册以及自动装配机制(@Bean / @Component,@Autowired)
- 使用java配置类注册Bean
使用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="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<!-- 向Bean中显式注入简单值 -->
<property name="username" value="root"/>
<property name="password" value="1234"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 为属性注入容器中的Bean对象 -->
<property name="dataSource" ref="dataSource"/>
<!-- 注入简单值 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 扫描Mapper接口,将通过动态代理生成的Bean注册到容器中去 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入简单值,指明扫描的包 -->
<property name="basePackage" value="com.znker.mapper"/>
<!-- 注入简单值,从容器中寻找id为sqlSessionFactory的Bean注入 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 扫描所有带有@Repository注解的代理类,将其作为Bean注册到容器中去 -->
<property name="annotationClass" value="org.springframework.stereotype.Repository"/>
</bean>
</beans>
除了注入简单值和引用对象之外,还能给Bean注入集合和映射对象。
Spring中的那些注解:
除了XML外还可以使用注解装配Bean,并且注解更加流行(springboot推荐全注解)。注解开发提供了自动装配的功能。使用注解开发需要解决两个问题:1. 让spring发现并注册Bean到容器中(注解扫描),2. 完成容器中Bean依赖关系的自动装配(依赖注入)。
注解@Component
告知Spring将这个类扫描生成为Bean注册到容器中,value元素表示该Bean在容器中的id,默认是类首字母的小写。
@Component // Spring扫描到该类的时候会将它注册为容器中的Bean
public class UserService {
@Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public UserDao getUserDao() {
return userDao;
}
}
@Component
注解还有三个派生注解:@Controller
,@Service
,Repository
这三个注解和也可能将pojo标识为容器中的Bean,在分层开发的的结构中,这些注解会使得pojo的作用更加清晰。
除了给pojo加注解之外,还要告知spring去哪里扫描对应的pojo,在使用java配置类开发的时候,可以使用@ComponentScan
注解标识需要扫描的包
package com.znker.dao;
import org.springframework.context.annotation.ComponentScan;
// 该注解告知spring扫描指定包及其子包下所有的类,将带有@Component注解的pojo注册为容器中的Bean
// 如果该注解不指定basePackages元素,默认扫描当前配置类所在的包,basePackages可以指定多个包
@ComponentScan(basePackages = {"com.znker.dao"})
public class DaoConfig {
}
注解@Autowired
实现了容器中Bean之间依赖关系的自动装配。Spring容器在初始化的时候完成容器中的Bean的定义和生成,然后寻找Bean所需的资源,完成依赖注入。@Autowired
注解默认是按容器中Bean的类型完成依赖项查找和注入的(byType)
@Controller
public class UserController {
// 从容器中查找UserService类型的Bean注入
@Autowired
private UserService userService;
}
按类型注入可能会存在Bean不唯一的情况,例如容器有多个UserService接口的实现类,此时spring不知道该注入哪个实现类,需要其它辅助方法来消除歧义。@Primary
注解标注在其中某个实现类上,告知当出现歧义的时候,优先使用该Bean注入,而@Qualifier
标注在需要注入资源属性上,此时按照容器中Bean的id来注入所需资源(默认是实现类的类名的首字母小写),从而消除歧义。
注解@Bean
和@Component
一样都可以标注pojo为容器中的Bean,但是@Bean
有一个特殊的用法,它可以将方法返回的对象作为Bean存放到容器中。
实际开发中,XML和注解是混合使用的,一般项目中自己开发的pojo优先使用注解配置(Controller,Service,Repository…),而第三方组件(DataSource,RedisTemplate…)则用XML配置更加方便。如果是基于java配置的项目中,使用@ImportResource
可以导入XML配置文件,将XML中定义的Bean注册到spring容器中去。
package com.znker.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
// 该注解导入了xml配置文件中定义的Bean,配置项是个数组,可用引入多个xml配置文件,用逗号隔开
@ImportResource({"classpath:spring-dao.xml"})
@ComponentScan("com.znker.*")
public class ApplicationConfig {
}
在分模块开发的时候,项目中可能有多个XML配置文件,此时可以在主配置文件中导入各个模块的配置文件。此时主配置文件中就有所有Bean的注册信息,容器使用主配置文件初始化的时候就能注册工程中的所有Bean
<import resource="classpath:spring-dao.xml"/>
在基于xml配置文件初始化容器的时候,需要指定容器需要扫描的包,将标有注解的pojo注册为容器中的Bean:
<!-- 扫描指定包极其子包下所有类,将标注了@Component、@Bean等注解的pojo注册为容器中的Bean -->
<context:component-scan base-package="com.znker.*"/>
加载属性文件:
开发过程中属性配置经常写到properties属性文件中,这样更改属性的时就无需修改项目源代码了,方便环境切换。注解@PropertySource
用于将属性配置文件引入java配置类中。database.properties属性文件:
jdbc.database.driver=com.mysql.jdbc.Driver
jdbc.database.url=jdbc:mysql://localhost:3306/demo
jdbc.database.username=root
jdbc.database.password=1234
// 加载属性文件
@PropertySource(value = {"classpath:database.properties"})
public class ApplicationConfig {
}
使用xml的方式加载属性文件:
<!-- 加载属性文件,多个属性文件之间使用逗号隔开 -->
<conontext:property-placeholder location="classpath:database.properties"/>
注解@Value
和占位符可以引用属性文件中的配置@Value("${jdbc.database.driver}")
将属性值注入到pojo的属性中。
Bean的作用域:
在默认情况下,spring容器的Bean只有一个实例
- 单例(singleton):默认选项,整个应用只生成一个实例,容器中组件共用一个实例
- 原型(prototype):多例模式,每次注入或者从容器中获取Bean实例的时候spring容器都会创建一个新的实例
- 会话(session):在web应用中,在一个会话过程中只创建一个实例
- 请求(request):在web应用中,在一个请求过程中会创建一个实例