全网最具体的Spring入门教程
为什么用Spring
什么是Spring
Spring 是一款开源的轻量级 Java 开发结构,旨在进步开发人员的开发功率以及体系的可维护性。
Spring的一个最大的意图便是使JAVA EE开发愈加简略。一同,Spring之所以与Struts、Hibernate等单层结构不同,是由于Spring致力于供给一个以一致的、高效的办法结构整个运用,而且能够将单层结构以最佳的组合揉和在一同树立一个连接的体系。能够说Spring是一个供给了更完善开发环境的一个结构,能够为POJO(Plain Ordinary Java Object)目标供给企业级的服务。
Spring的特性和优势
从Spring 结构的特性来看:
- 非侵入式:根据Spring开发的运用中的目标能够不依靠于Spring的API
- 操控回转:Inversion of Control(IOC),指的是将目标的创立权交给 Spring 去创立,是一个轻量级的IOC容器。运用 Spring 之前,目标的创立都是由咱们自己在代码中new创立。而运用 Spring 之后。目标的创立都是给了 Spring 结构,完成松耦合。
- 依靠注入:Dependency Injection(DI),是指依靠的目标不需求手动调用 setXX 办法去设置,而是经过装备赋值。
- 面向切面编程:Aspect Oriented Programming(AOP),把运用事务逻辑和体系服务分隔,经过切面和模板削减样板式代码。
- 容器:Spring 是一个容器,它包括而且办理运用目标的生命周期
- 组件化:Spring 完成了运用简略的组件装备组合成一个杂乱的运用。在 Spring 中能够运用XML和Java注解组合这些目标。
- 声明式事务的支撑:能够从单调繁杂的事务办理代码中摆脱出来,经过声明式办法灵敏地进行事务的办理,可向下扩展到(例如运用一个单一的数据库)本地事务并扩展到大局事务(例如,运用 JTA),进步开发功率和质量。
- 一站式:在 IOC 和 AOP 的根底上能够整合各种企业运用的开源结构和优异的第三方类库(实践上 Spring 本身也供给了表现层的 SpringMVC 和耐久层的 Spring JDBC)
从运用Spring 结构的优势看:
- Spring 能够使开发人员运用 POJOs 开发企业级的运用程序。只运用 POJOs 的优点是你不需求一个运用程序服务器,可是你能够挑选运用一个强健的 servlet 容器,比方 Tomcat 或许一些商业产品。
- Spring 在一个单元形式中是有安排的。即便包和类的数量十分大,你只需忧虑你需求的,而其它的就能够疏忽了。
- Spring 不会让你白费力气做重复作业,它能够整合一些现有的技能,像 ORM 结构、日志结构、JEE、Quartz 和 JDK 计时器,其他视图技能。
- 测验一个用 Spring 编写的运用程序很简略,由于环境相关的代码被移动到这个结构中。此外,经过运用 JavaBean-style POJOs,它在运用依靠注入测验数据时变得更简略。
- Spring 的 web 结构是一个规划杰出的 web MVC 结构,它为比方 Structs 或许其他工程上的或许不怎么受欢迎的 web 结构供给了一个很好的供代替的挑选。MVC 形式导致运用程序的不同方面(输入逻辑,事务逻辑和UI逻辑)别离,一同供给这些元素之间的松懈耦合。模型(Model)封装了运用程序数据,一般它们将由 POJO 类组成。视图(View)担任烘托模型数据,一般来说它生成客户端浏览器能够解说 HTML 输出。操控器(Controller)担任处理用户恳求并构建恰当的模型,并将其传递给视图进行烘托。
- Spring 对 JavaEE 开发中十分难用的一些 API(JDBC、JavaMail、长途调用等),都供给了封装,使这些API运用难度大大下降。
相关材料
Spring的组件
Spring5.x 版别中 Web 模块的 Portlet 组件现已被抛弃掉,一同增加了用于异步呼应式处理的 WebFlux 组件。
从最基层往上介绍
Spring Test
Spring 团队发起测验驱动开发(TDD)。有了操控回转 (IoC)的协助,单元测验和集成测验变得更简略。
Spring 的测验模块对 JUnit(单元测验结构)、TestNG(相似 JUnit)、Mockito(首要用来 Mock 目标)、PowerMock(处理 Mockito 的问题比方无法模仿 final, static, private 办法)等等常用的测验结构支撑的都比较好。而且还额定供给了一些根据 Spring 的测验功用,比方在测验 Web 结构时,模仿 Http 恳求的功用。
包括Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient。
源码对应模块如下:
Core Container
Spring 结构的中心模块,也能够说是根底模块,首要供给 IoC 依靠注入功用的支撑。由 Beans 模块、Core 中心模块、Context 上下文模块和 SpEL 表达式言语模块组成,没有这些中心容器,也不行能有 AOP、Web 等上层的功用。
- spring-core:封装了 Spring 结构的底层部分,包括资源拜访、类型转化及一些常用工具类。
- spring-beans:供给对 bean 的创立、装备和办理等功用的支撑,包括操控回转和依靠注入。
- spring-context:树立在 Core 和 Beans 模块的根底之上,集成 Beans 模块功用并增加资源绑定、数据验证、国际化、Java EE 支撑、容器生命周期、事情传达等。ApplicationContext 接口是上下文模块的焦点。
- spring-expression:供给对表达式言语(Spring Expression Language) SpEL 的支撑,只依靠于 core 模块,不依靠于其他模块,能够独自运用。支撑拜访和修正特点值,办法调用,支撑拜访及修正数组、容器和索引器,命名变量,支撑管用和逻辑运算,支撑从 Spring 容器获取 Bean,它也支撑列表投影、挑选和一般的列表聚合等。
对应源码模块如下:
AOP、Aspects、Instrumentation和Messaging
- spring-aspects:该模块为与 AspectJ 的集成供给支撑,是一个功用强大且老练的面向切面编程(AOP)结构。
- spring-aop:供给了面向切面的编程完成。供给比方日志记载、权限操控、功用计算等通用功用和事务逻辑别离的技能,而且能动态的把这些功用增加到需求的代码中,这样各司其职,下降事务逻辑和通用功用的耦合。
- spring-instrument:供给了为 JVM 增加署理(agent)的功用。 详细来讲,它为 Tomcat 供给了一个织入署理,能够为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的相同。没有了解也没联系,这个模块的运用场景十分有限。
- spring-messaging:是从 Spring4.0 开端新参加的一个模块,首要职责是为 Spring 结构集成一些根底的报文传送运用。
- spring-jcl 模块: Spring 5.x中新增了日志结构集成的模块。
对应源码模块如下:
Data Access/Integration
- spring-jdbc:供给了对数据库拜访的笼统 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需求和 JDBC API 交互,这样就屏蔽了数据库的影响。
- spring-tx:支撑编程和声明式事务办理。
- spring-orm:供给对 Hibernate、JPA、iBatis 和 MyBatis 等 ORM 结构的支撑。而且还能够运用 Spring 事务办理,无需额定操控事务。
- spring-oxm:供给一个笼统层支撑 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。将 Java 目标映射成 XML 数据,或许将XML 数据映射成 Java 目标。
- spring-jms : 指 Java 音讯服务,供给一套 “音讯生产者、音讯顾客”模板用于愈加简略的运用 JMS,JMS 用于用于在两个运用程序之间,或分布式体系中发送音讯,进行异步通讯。自 Spring Framework 4.1 今后,它还供给了对 spring-messaging 模块的承继。
对应源码模块:
Spring Web
- spring-web:供给了根本的 Web 开发集成特性,例如多文件上传功用、运用的 Servlet 监听器的 IOC 容器初始化以及 Web 运用上下文。
- spring-webmvc:供给了一个 Spring MVC Web 结构完成。Spring MVC 结构供给了根据注解的恳求资源注入、更简略的数据绑定、数据验证等及一套十分易用的 JSP 标签,彻底无缝与 Spring 其他技能协作。
- spring-websocket:供给了对 WebSocket 的支撑,WebSocket 能够让客户端和服务端进行双向通讯。
- spring-webflux:供给对 WebFlux 的支撑。WebFlux 是 Spring Framework 5.0 中引进的新的呼应式结构。与 Spring MVC 不同,它不需求 Servlet API,是彻底异步,而且经过Reactor项目完成了Reactive Streams标准。Spring WebFlux 用于创立根据事情循环履行模型的彻底异步且非堵塞的运用程序。
对应源码模块如下:
Spring、SpringMVC、SpringBoot之间的联系
Spring 包括了多个功用模块(上面刚刚提到过),其间最重要的是 Spring-Core(首要供给 IoC 依靠注入功用的支撑) 模块, Spring 中的其他模块(比方 Spring MVC)的功用完成根本都需求依靠于该模块。
Spring MVC 是 Spring 中的一个很重要的模块,首要赋予 Spring 快速构建 MVC 架构的 Web 程序的才干。MVC 是模型(Model)、视图(View)、操控器(Controller)的简写,其间心思维是经过将事务逻辑、数据、显现别离来安排代码。
运用 Spring 进行开发各种装备过于费事比方敞开某些 Spring 特性时,需求用 XML 或 Java 进行显式装备。所以,Spring Boot 诞生了!
Spring 旨在简化 J2EE 企业运用程序开发。Spring Boot 旨在简化 Spring 开发(削减装备文件,开箱即用!)。
Spring Boot 仅仅简化了装备,假如你需求构建 MVC 架构的 Web 程序,你仍是需求运用 Spring MVC 作为 MVC 结构,仅仅说 Spring Boot 帮你简化了 Spring MVC 的许多装备,真实做到开箱即用!
HelloWorld-xml
这儿仅仅表明这是Spring第一个项目,以HelloWorld作为标示。实践需求是获取 用户列表信息,并打印履行日志
事例
事例源码点击这儿
- 引进依靠
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.3.37</spring.version>
<aspectjweaver.version>1.9.6</aspectjweaver.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
</dependencies>
- POJO - User
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- DAO 获取 POJO, UserDaoServiceImpl (mock 数据)
public class UserDaoImpl{
public List<User> findUserList() {
return Collections.singletonList(new User("seven", 18));
}
}
- 事务层 UserServiceImpl(调用DAO层)
public class UserServiceImpl {
private UserDaoImpl userDao;
public void setUserDao(UserDaoImpl userDao) {
this.userDao = userDao;
}
public List<User> findUserList() {
return userDao.findUserList();
}
}
- 阻拦一切service中的办法,并输出记载
@Aspect
public class LogAspect {
@Around("execution(* com.seven.springhelloworldxml.service.*.*(..))")
public Object businessService(ProceedingJoinPoint pjp) throws Throwable {
// get attribute through annotation
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
System.out.println("execute method: " + method.getName());
// continue to process
return pjp.proceed();
}
}
- 增加并增加spring.xml和aspects.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="userDao" class="com.seven.springhelloworldxml.dao.UserDaoImpl">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="userService" class="com.seven.springhelloworldxml.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.seven.springhelloworldxml" />
<aop:aspectj-autoproxy/>
<bean id="logAspect" class="com.seven.springhelloworldxml.aspects.LogAspect">
<!-- configure properties of aspect here as normal -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
- APP中设置xml文件
public class APP {
public static void main(String[] args) {
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "spring.xml");
// retrieve configured instance
UserServiceImpl service = context.getBean("userService", UserServiceImpl.class);
// use configured instance
List<User> userList = service.findUserList();
// print info from beans
userList.forEach(a -> System.out.println(a.getName() + "," + a.getAge()));
}
}
运转成果:
怎么表现的Spring优势
操控回转 - IOC
查询用户(service经过调用dao查询pojo),本质上便是怎么创立User/Dao/Service?
- 假如没有Spring结构,需求自己创立User/Dao/Service等,比方:
UserDaoImpl userDao = new UserDaoImpl();
UserSericeImpl userService = new UserServiceImpl();
userService.setUserDao(userDao);
List<User> userList = userService.findUserList();
- 有了Spring结构,能够将原有Bean的创立作业转给结构, 需求用时从Bean的容器中获取即可,这样便简化了开发作业
Bean的创立和运用别离了。
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "spring.xml");
// retrieve configured instance
UserServiceImpl service = context.getBean("userService", UserServiceImpl.class);
// use configured instance
List<User> userList = service.findUserList();
更进一步,便能了解为何会有如下的常识点了:
- Spring结构办理这些Bean的创立作业,即由用户办理Bean转变为结构办理Bean,这个就叫操控回转 - Inversion of Control (IoC)
- Spring 结构保管创立的Bean放在哪里呢? 这便是IoC Container;
- Spring 结构为了更好让用户装备Bean,必定会引进不同办法来装备Bean? 这便是xml装备,Java装备,注解装备等支撑
- Spring 结构已然接管了Bean的生成,必定需求办理整个Bean的生命周期等;
- 运用程序代码从Ioc Container中获取依靠的Bean,注入到运用程序中,这个进程叫 依靠注入(Dependency Injection,DI) ; 所以说操控回转是经过依靠注入完成的,其实它们是同一个概念的不同视点描绘。浅显来说便是IoC是规划思维,DI是完成办法
- 在依靠注入时,有哪些办法呢?这便是结构器办法,@Autowired, @Resource, @Qualifier... 一同Bean之间存在依靠(或许存在先后顺序问题,以及循环依靠问题等)
面向切面 - AOP
第二个需求:给Service一切办法调用增加日志(调用办法时),本质上是解耦问题;
- 假如没有Spring结构,需求在每个service的办法中都增加记载日志的办法,比方:
public List<User> findUserList() {
System.out.println("execute method findUserList");
return this.userDao.findUserList();
}
- 有了Spring结构,经过@Aspect注解 界说了切面,这个切面中界说了阻拦一切service中的办法,并记载日志; 能够显着看到,结构将日志记载和事务需求的代码解耦了,不再是侵入式的了
/**
* aspect for every methods under service package.
*/
@Around("execution(* com.seven.springhelloworldxml.service.*.*(..))")
public Object businessService(ProceedingJoinPoint pjp) throws Throwable {
// get attribute through annotation
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
System.out.println("execute method: " + method.getName());
// continue to process
return pjp.proceed();
}
更进一步,便能了解为何会有如下的常识点了:
- Spring 结构经过界说切面, 经过阻拦切点完成了不同事务模块的解耦,这个就叫面向切面编程 - Aspect Oriented Programming (AOP)
- 为什么@Aspect注解运用的是aspectj的jar包呢?这就引出了Aspect4J和Spring AOP的历史根由,只要了解了Aspect4J和Spring的根由才干了解有些注解上的兼容规划
- 怎么支撑更多阻拦办法来完成解耦, 以满意更多场景需求呢? 这便是@Around, @Pointcut... 等的规划
- 那么Spring结构又是怎么完成AOP的呢? 这就引进署理技能,分静态署理和动态署理,动态署理又包括JDK署理和CGLIB署理
Spring结构逐渐简化开发
Java 装备办法改造
事例源码点击这儿
在前文的比如中, 经过xml装备办法完成的,这种办法实践上比较费事; 我经过Java装备进行改造:
- User,UserDaoImpl, UserServiceImpl,LogAspect不必改
- 将原经过.xml装备转化为Java装备
@EnableAspectJAutoProxy
@Configuration
public class BeansConfig {
/**
* @return user dao
*/
@Bean("userDao")
public UserDaoImpl userDao() {
return new UserDaoImpl();
}
/**
* @return user service
*/
@Bean("userService")
public UserServiceImpl userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(userDao());
return userService;
}
/**
* @return log aspect
*/
@Bean("logAspect")
public LogAspect logAspect() {
return new LogAspect();
}
}
- 在App中加载BeansConfig的装备
public class APP {
public static void main(String[] args) {
// create and configure beans
ApplicationContext context = new AnnotationConfigApplicationContext(BeansConfig.class);
// retrieve configured instance
UserServiceImpl service = context.getBean("userService", UserServiceImpl.class);
// use configured instance
List<User> userList = service.findUserList();
// print info from beans
userList.forEach(a -> System.out.println(a.getName() + "," + a.getAge()));
}
}
这儿简略提一下完成原理:
-
当运用启动时,Spring结构会运用Java的反射API来查看一切带有
@Configuration
注解的类(这儿不明白的能够看下注解完成的原理)。Spring结构内置了一个注解处理器ConfigurationClassPostProcessor
,它是BeanFactoryPostProcessor
的一个完成。 -
接着
ConfigurationClassPostProcessor
会在容器初始化时被调用,它会查找一切带有@Configuration
注解的类,并解析这些类中界说的@Bean
办法。-
BeanDefinition:关于每个
@Configuration
类,Spring会为其间的每个@Bean
办法生成一个BeanDefinition
目标。这些BeanDefinition
目标会包括创立和装备Bean所需的一切信息。 -
处理嵌套装备:假如一个
@Configuration
类中包括了另一个@Configuration
类的引证,ConfigurationClassPostProcessor
会递归地处理这些嵌套的装备类。
-
-
注册BeanDefinition: 一旦一切的
BeanDefinition
被创立,它们会被注册到Spring容器的BeanFactory
中。这样,Spring容器就能够在需求时创立和注入这些Bean。 -
署理装备类: 为了支撑嵌套装备类和循环依靠等特性,Spring会为每个
@Configuration
类创立一个署理,这个署理会在运转时处理相关的逻辑。
注解装备办法改造
事例源码点击这儿
更进一步,Java 5开端供给注解支撑,Spring 2.5 开端彻底支撑根据注解的装备而且也支撑JSR250 注解。在Spring后续的版别开展倾向于经过注解和Java装备结合运用.
- BeanConfig 不再需求Java装备
@EnableAspectJAutoProxy
@Configuration
public class BeansConfig {
}
- UserDaoImpl 增加了 @Repository注解
@Repository
public class UserDaoImpl{
public List<User> findUserList() {
return Collections.singletonList(new User("seven", 18));
}
}
- UserServiceImpl 增加了@Service 注解,并经过@Autowired注入userDao
@Service
public class UserServiceImpl {
@Autowired
private UserDaoImpl userDao;
public List&