1. 概念入门
概念: Spring是2003年兴起的一个轻量级的JAVA开源框架,核心特性是控制反转和面向切面编程:
- 历史发展:
- Spring1.x时代只能使用xml配置bean,随着项目扩大,bean和xml配置的数量不断增多,二者之间切换频繁。
- Spring2.x时代支持使用JDK5带来的注解配置bean,减少了xml配置代码,简化项目。
- Spring3.x时代开始支持使用纯java代码配置bean,SpringBoot推荐此种方案。
- 总结:基本配置如数据源建议使用可读性更高的XML配置,开发相关如Service注入Dao等建议使用更简单的注解配置。
- 控制反转IOC:Inversion of Control,就是将实例化过程和实例生命周期管理都交给spring容器,以解决手动new实例时,侵入性和耦合度双高的问题。
- 依赖注入DI:depend injection,在IOC产生实例的同时注入属性。
- 面向切面AOP:Aspect Oriented Programming,就是通过预编译的方式在运行期使用动态代理实现的一种技术,主要作用于控制事务的传播,如一套下单事务中:
orderDao.insert(order);:添加订单后,事务应该向下传播。detailDao.insert(detail);:添加明细后,事务不该向下传播。logDao.insert(log);:无论添加成功该是失败,都应该记录日志。
- 官方下载:Spring官方下载地址,建议下载
4.3.14.RELEASE-dist版本:- Framework5.x 支持 JDK8+
- Framework4.x 支持 JDK6+
- Framework3.x 支持 JDK5+
spring产品图解
2. 基础配通
流程:
- 引入最简依赖:
- spring-core:核心包,被很多其他spring模块引用。
- spring-beans:管理java类的包。
- spring-context:Spring上下文包。
- spring-context-support:Spring上下文包拓展包。
- spring-expression:SpEL表达式语言包。
- 在classpath下开发主配文件:
app-start.xml:- 实例化spring容器(鱼缸)的时候需要加载该主配文件。
- 头信息在
7.2.1 Configuration metadata
- 开发实体类:
Student.java:- 埋一个
name属性并配置set()/get()。
- 埋一个
- 在主配文件中管理实体类:使用
<bean>标签来关联实体类(鱼):<bean>中使用id属性设置唯一标识,可以随意命名。<bean>中使用class属性关联Student实体类的类全名。
- 测试:
new ClassPathXmlApplicationContext():指定主配文件位置以造鱼缸。getBean():通过id的方式捞出对应Student实例类的那条鱼。setName()/getName():使用这条鱼。close():砸鱼缸以释放资源。
若主配文件不在
classpath下,则替换使用FileSystemXmlApplicationContext容器,加载路径从项目出发。
- res:
pom.xml
<properties>
<spring>4.3.14.RELEASE</spring>
</properties>
<dependencies>
<!--spring-core-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring}</version>
</dependency>
<!--spring-beans-->
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring}</version>
</dependency>
<!--spring-context-->
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-context</artifactId>
<version>${spring}</version>
</dependency>
<!--spring-context-support-->
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring}</version>
</dependency>
<!--spring-expression-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<optional>true</optional>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
- res:
classpath:spring/start/app-classpath.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="student" class="com.yap.start.pojo.Student"/>
</beans>
- res:
app-file-system.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="studentE" class="com.yap.start.pojo.Student"/>
</beans>
- src:
c.y.start.pojo.Student
/**
* @author yap
*/
@Data
public class Student implements Serializable {
private String name;
}
- tst:
c.y.start.StudentTest.classPathApp()
/**
* @author yap
*/
public class StudentTest {
private ClassPathXmlApplicationContext app;
@Test
public void classPathApp() {
// "classpath:" Can be omitted
app = new ClassPathXmlApplicationContext("classpath:spring/start/app-classpath.xml");
Student student = (Student) app.getBean("student");
student.setName("赵四");
System.out.println(student.getName());
}
@After
public void after(){
app.close();
}
}
- tst:
c.y.start.StudentTest.fileSystemApp()
@Test
public void fileSystemApp() {
FileSystemXmlApplicationContext app;
app = new FileSystemXmlApplicationContext("app-file-system.xml");
Student studentE = (Student) app.getBean("studentE");
studentE.setName("studentE");
System.out.println(studentE.getName());
}
3. 容器的多配置
概念: 同时读取多个配置来实例化spring容器有两种方式:
- 使用
ClassPathXmlApplicationContext(String... configLocations)构造。 - 在一个主配文件中使用
<import resource="">引入另一个主配文件。
源码: /spring4/
- res:
classpath:spring/start/multiple-a.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="studentA" class="com.yap.start.pojo.Student"/>
</beans>
- res:
classpath:spring/start/multiple-b.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="studentB" class="com.yap.start.pojo.Student"/>
</beans>
- res:
classpath:spring/start/multiple-c.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">
<import resource="classpath:spring/start/multiple-d.xml"/>
<bean id="studentC" class="com.yap.start.pojo.Student"/>
</beans>
- res:
classpath:spring/start/multiple-d.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="studentD" class="com.yap.start.pojo.Student"/>
</beans>
- src:
c.y.start.pojo.Student
/**
* @author yap
*/
@Data
public class Student implements Serializable {
private String name;
}
- tst:
c.y.start.StudentTest.loadMultipleXmlByArray()
@Test
public void loadMultipleXmlByArray() {
app = new ClassPathXmlApplicationContext("spring/start/multiple-a.xml", "spring/start/multiple-b.xml");
Student studentA = (Student) app.getBean("studentA");
Student studentB = (Student) app.getBean("studentB");
studentA.setName("studentA");
System.out.println(studentA.getName());
studentB.setName("studentB");
System.out.println(studentB.getName());
}
- tst:
c.y.start.StudentTest.loadMultipleXmlByImport()
@Test
public void loadMultipleXmlByImport() {
app = new ClassPathXmlApplicationContext("spring/start/multiple-c.xml");
Student studentC = (Student) app.getBean("studentC");
Student studentD = (Student) app.getBean("studentD");
studentC.setName("studentC");
System.out.println(studentC.getName());
studentD.setName("studentD");
System.out.println(studentD.getName());
}
4. bean的获取方式
概念: 通过 id,name 或 类.class 都可以获取到bean:
id不允许重复,使用id来获取bean是相对比较规范的一种方式。name可以重复但会发生覆盖,若和id同时存在,则name退化成别名。类.class可以省略掉强转这一步骤。
源码: /spring4/
- res:
classpath:spring/start/get-by-id.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="studentF" class="com.yap.start.pojo.Student"/>
</beans>
- res:
classpath:spring/start/get-by-name.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 name="admin" class="com.yap.start.pojo.Student"/>
</beans>
- res:
classpath:spring/start/get-by-class.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 class="com.yap.start.pojo.Student"/>
</beans>
- src:
c.y.start.pojo.Student
/**
* @author yap
*/
@Data
public class Student implements Serializable {
private String name;
}
- tst:
c.y.start.StudentTest.getBeanById()
@Test
public void getBeanById() {
app = new ClassPathXmlApplicationContext("spring/start/get-by-id.xml");
Student student = (Student) app.getBean("studentF");
student.setName("studentF");
System.out.println(student.getName());
app.close();
}
- tst:
c.y.start.StudentTest.getBeanByName()
@Test
public void getBeanByName() {
app = new ClassPathXmlApplicationContext("spring/start/get-by-name.xml");
Student student = (Student) app.getBean("admin");
student.setName("admin");
System.out.println(student.getName());
app.close();
}
- tst:
c.y.start.StudentTest.getBeanByClass()
@Test
public void getBeanByClass() {
app = new ClassPathXmlApplicationContext("spring/start/get-by-class.xml");
Student student = app.getBean(Student.class);
student.setName("student");
System.out.println(student.getName());
app.close();
}
5. bean的创建方式
概念: spring容器支持通过无参构造,静态工厂或实例工厂的方式来创建bean:
- 无参构造:spring容器默认调用类的无参构造器进行实例化过程,工作中常用:
- 只能调用无参构造,无法调用有参构造。
- 静态工厂:在静态工厂方法中手动实例化指定类并返回,然后由spring容器来调用该方法即可,静态方法速度快但会有线程安全问题:
<bean class="静态工厂类全名" factory-method="静态工厂方法名"/>
- 实例工厂:在实例工厂方法中手动实例化指定类并返回,然后由spring容器来调用该方法即可,实例方法速度慢但没有线程安全问题:
<bean id="实例工厂ID" class="实例工厂类全名"/><bean factory-bean="实例工厂ID" factory-method="实例工厂方法名"/>
源码: /spring4/
- res:
classpath:spring/ioc/build-by-constructor.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 class="com.yap.start.pojo.User"/>
</beans>
- res:
classpath:spring/ioc/build-by-static-factory.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 class="com.yap.ioc.factory.UserFactory" factory-method="getUserByStaticMethod"/>
</beans>
- res:
classpath:spring/ioc/build-by-dynamic-factory.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="userFactory" class="com.yap.ioc.factory.UserFactory"/>
<bean factory-bean="userFactory" factory-method="getUserByDynamicMethod"/>
</beans>
- src:
c.y.ioc.pojo.User
/**
* @author yap
*/
public class User {
public User() {
System.out.println("User()...");
}
public User(String name) {
System.out.println("User(String name)... " + name);
}
public void init(){
System.out.println("init()...");
}
public void destroy(){
System.out.println("destroy()...");
}
}
- src:
c.y.ioc.factory.UserFactory
/**
* @author yap
*/
public class UserFactory {
public UserFactory() {
System.out.println("UserFactory()...");
}
public static User getUserByStaticMethod() {
return new User("static");
}
public User getUserByDynamicMethod() {
return new User("dynamic");
}
}
- tst:
c.y.ioc.UserTest.buildByConstructor()
/**
* @author yap
*/
public class UserTest {
private ClassPathXmlApplicationContext app;
@Test
public void buildByConstructor() {
app = new ClassPathXmlApplicationContext("spring/ioc/build-by-constructor.xml");
app.getBean(User.class);
}
@After
public void after() {
app.close();
}
}
- tst:
c.y.ioc.UserTest.buildByStaticFactory()
@Test
public void buildByStaticFactory() {
app = new ClassPathXmlApplicationContext("spring/ioc/build-by-static-factory.xml");
app.getBean(User.class);
}
- tst:
c.y.ioc.UserTest.buildByDynamicFactory()
@Test
public void buildByDynamicFactory() {
app = new ClassPathXmlApplicationContext("spring/ioc/build-by-dynamic-factory.xml");
app.getBean(User.class);
}
6. bean的加载机制
概念: <bean> 的 lazy-init 属性可以调整该bean的加载机制:
false:非延迟加载,默认值,表示该bean在spring容器初始化时被加载和实例化。true:延迟加载,也叫懒加载,表示该bean在spring容器调用getBean()时被加载和实例化。
源码: /spring4/
- res:
classpath:spring/ioc/lazy.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 lazy-init="false" class="com.yap.start.pojo.User"/>
</beans>
- src:
c.y.ioc.pojo.User
/**
* @author yap
*/
public class User {
public User() {
System.out.println("User()...");
}
public User(String name) {
System.out.println("User(String name)... " + name);
}
public void init(){
System.out.println("init()...");
}
public void destroy(){
System.out.println("destroy()...");
}
}
- tst:
c.y.ioc.UserTest.lazy()
@Test
public void lazy() {
app = new ClassPathXmlApplicationContext("spring/ioc/lazy.xml");
app.getBean(User.class);
}
7. bean的作用范围
概念: <bean> 的 scope 属性可以调整该bean的作用范围:
singleton:表示该bean使用单例模式,即每次从容器中获取到的都是同一个bean,默认值。prototype:表示该bean使用原型模式,即每次从容器中获取到的都是新new出来的一个bean,该模式必须搭配延迟加载。
源码: /spring4/
- res:
classpath:spring/ioc/scope.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 class="com.yap.start.pojo.User" scope="prototype" lazy-init="false"/>
</beans>
- src:
c.y.ioc.pojo.User
/**
* @author yap
*/
public class User {
public User() {
System.out.println("User()...");
}
public User(String name) {
System.out.println("User(String name)... " + name);
}
public void init(){
System.out.println("init()...");
}
public void destroy(){
System.out.println("destroy()...");
}
}
- tst:
c.y.ioc.UserTest.scope()
@Test
public void scope() {
app = new ClassPathXmlApplicationContext("spring/ioc/scope.xml");
User userA = app.getBean(User.class);
User userB = app.getBean(User.class);
System.out.println(userA.hashCode());
System.out.println(userB.hashCode());
}
8. bean的生命周期
概念: <bean> 的 init-method 属性和 destroy-method 属性可以分别指定该bean的初始化方法和销毁方法。
源码: /spring4/
- res:
classpath:spring/ioc/life-cycle.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 class="com.yap.start.pojo.User" init-method="init" destroy-method="destroy"/>
</beans>
- src:
c.y.ioc.pojo.User
/**
* @author yap
*/
public class User {
public User() {
System.out.println("User()...");
}
public User(String name) {
System.out.println("User(String name)... " + name);
}
public void init(){
System.out.println("init()...");
}
public void destroy(){
System.out.println("destroy()...");
}
}
- tst:
c.y.ioc.UserTest.lifeCycle()
@Test
public void lifeCycle() {
app = new ClassPathXmlApplicationContext("spring/ioc/life-cycle.xml");
app.getBean(User.class);
}