Spring 的历史网上有很多的介绍,下面是 Spring 发展历程的一个简介。
在 Spring 1.x 时代,使用 Spring 开发满眼都是 xml 配置的 Bean,随着项目的扩大,我们需要把 xml 配置文件放到不同的配置文件里,那时候需要频繁地在开发的类和配置文件之间切换。
在 Spring 2.x 时代,随着 JDK 1.5 带来的注解支持,Spring 提供了声明 Bean 的注解(如:@Component、@Service),大大减少了配置量。这时 Spring 圈子里存在着一种争论:注解配置和 xml 配置究竟哪个更好?我们最终的选择是应用的基本配置(如:数据库配置)用 xml,业务配置用注解。
从 Spring 3.x 到现在,Spring 提供了 Java 配置的能力,使用 Java 配置可以让你更理解你配置的 Bean。我们目前刚好处于这个时代,Spring 4.x 和 Spring Boot 都推荐使用 Java 配置。
Spring 框架是一个轻量级的企业级开发的一站式解决方案。所谓解决方案就是可以基于 Spring 解决 JavaEE 开发的所有问题。Spring 框架主要提供了IoC
容器、AOP、数据访问、Web 开发、消息、测试等相关技术的支持。
Spring 使用简单的 POJO(Plain Old Java Object
,即无任何限制的普通Java对象)来进行企业级开发。每一个被 Spring 管理的 Java 对象都被称之为 Bean;而 Spring 提供了一个 IoC 容器用来初始化对象,解决对象间的依赖管理和对象的使用。
Spring 是模块化的,这意味着你可以只使用你需要的Spring的模块。如下图所示:
图中的每个最小单元,Spring 都至少有一个对应的 jar 包。
Spring 发展到现在已经不仅仅是 Spring 框架本身的内容,Spring 目前提供了大量的基于 Spring 的项目,可以用来更深入地降低我们的开发难度,提高开发效率。 目前 Spring 的生态里主要有以下项目,我们可以根据自己项目的需要来选择使用相应的项目。
这里我们使用目前 Java 主流的项目构建工具Maven来搭建项目。
Apache Maven 是一个基于项目对象模型(Project Object Model,POM)的软件项目管理工具。Maven 可用来管理项目的依赖、编译、打包、文档等信息。使用 Maven 来管理项目时,项目依赖的 jar 包将不再包含在项目内,而是集中放置在用户目录下的 .m2 文件夹下。关于 Maven 的详细安装介绍可参考[这里][4]。
在创建项目之前,须确保你的计算机上已经安装好有 Java 和 Maven 环境。然后,打开终端通过以下简单的命令就可以在你的当前目录下创建一个 Jave web 的项目结构:
mvn archetype:generate -DgroupId=com.blinkfox -DartifactId=springdemo -DpackageName=com.blinkfox.springdemo -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
其中-DgroupId=com.blinkfox
是组织名,-DartifactId=springdemo
是该组织下的项目名称,-DarchetypeArtifactId=maven-archetype-webapp
代表创建一个简单的 webapp 项目。
创建项目的时候,Maven会自动下载一些需要用到的 jar 包和 Maven 插件。如果顺利创建成功的话,就会在你的当前目录下看到名为 springdemo 的项目,其中包含src
的文件夹和pom.xml
文件。且在你的终端会看到如下输出:
接下来需要通过修改 pom.xml 来添加 Spring 的依赖,添加编译插件,且将编译级别设置为1.7,pom.xml文件的修改如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.blinkfox</groupId>
<artifactId>springdemo</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springdemo Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>springdemo</finalName>
<!-- 指定maven的默认操作为 -->
<defaultGoal>compile</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
Spring 框架本身有四大原则:
Spring 的所有功能设计和实现都是基于此四大原则。
我们经常说的控制反转(Inversion of Control,IoC)和依赖注入(dependency injection,DI)在 Spring 环境下是等同的概念,控制反转是通过依赖注入实现的。所谓依赖注入指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖。
依赖注入的主要目的是为了解耦,体现了一种“组合”的理念。如果你希望你的类具备某项功能的时候,是继承自一个具有此功能的父类好呢?还是组合另外一个具有这个功能的类好呢?答案是不言而喻的,继承一个父类,之类将与父类耦合,组合另外一个类则使耦合度大大降低。
Spring IoC 容器(ApplicationContext)负责创建 Bean,并通过容器将功能类 Bean 注入到你需要的 Bean 中。Spring 提供使用 xml、注解、Java 配置、groovy 配置实现 Bean 的创建和注入。
无论是 xml 配置、注解配置还是 Java 配置,都被称为配置元数据,所谓元数据即描述数据的数据。元数据本身不具备任何可执行的能力,只能通过外界代码来对这些元数据行解析后进行一些有意义操作。Spring 容器解析这些配置元数据进行 Bean 初始化、配置和管理依赖。
声明 Bean 的注解:
@Component
: 组件,没有明确角色@Controller
: 在展现层(MVC -> Spring MVC)使用@Service
: 在业务逻辑层(service层)使用@Repository
: 在数据访问层(dao层)使用注入 Bean 的注解,一般情况下通用:
@Autowired
: Spring 提供的注解@Inject
: JSR-330 提供的注解@Resource
: JSR-250 提供的注解@Autowired
、@Inject
、@Resource
可注解在 set 方法上或者属性上,推荐注解在属性上,优点是代码更少、层次更清晰。
(1)编写功能类的 Bean。
package com.blinkfox.service.impl;
import org.springframework.stereotype.Service;
/**
* Created by blinkfox on 2016/10/27.
*/
@Service
public class FunctionService {
public String sayHello(String word) {
return "Hello " + word + "!";
}
}
代码解释:
(2)使用功能类的 Bean。
package com.blinkfox.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by blinkfox on 2016/10/27.
*/
@Service
public class UseFunctionService {
@Autowired
private FunctionService functionService;
public String sayHello(String word) {
return functionService.sayHello(word);
}
}
代码解释:
(3)配置类。
package com.blinkfox.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Created by blinkfox on 2016/10/27.
*/
@Configuration
@ComponentScan("com.blinkfox.service.impl")
public class DiConfig {
}
代码解释:
(4)运行。
package com.blinkfox.maintest;
import com.blinkfox.config.DiConfig;
import com.blinkfox.service.impl.UseFunctionService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Created by blinkfox on 2016/10/27.
*/
public class FunctionMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DiConfig.class);
UseFunctionService useFunctionService = context.getBean(UseFunctionService.class);
System.out.println(useFunctionService.sayHello("Spring"));
context.close();
}
}
代码解释:
Java 配置是 Spring4.x 推荐的配置方式,可以完全替代 xml 配置;Java 配置也是 Spring Boot 推荐的配置方式。
Java 配置是通过 @Configuration 和 @Bean 来实现的。
何时使用 Java 配置或者注解配置呢?我们主要的原则是:全局配置使用 Java 配置(如数据库相关配置、MVC相关配置),业务 Bean 的配置使用注解配置(@Service、@Component、@Repository、@Controller)。
(1)编写功能类的 Bean
package com.blinkfox.service.impl;
/**
* Created by blinkfox on 2016/10/27.
*/
// 1
public class JavaConfigService {
public String sayHello(String word) {
return "Hello " + word + "!";
}
}
代码解释:
(2)使用功能类的 Bean
package com.blinkfox.service.impl;
/**
* Created by blinkfox on 2016/10/27.
*/
// 1
public class UseJavaConfigService {
// 2
private JavaConfigService javaConfigService;
public void setJavaConfigService(JavaConfigService javaConfigService) {
this.javaConfigService = javaConfigService;
}
public String sayHello(String word) {
return javaConfigService.sayHello(word);
}
}
代码解释:
(3)Java 配置类
package com.blinkfox.config;
import com.blinkfox.service.impl.JavaConfigService;
import com.blinkfox.service.impl.UseJavaConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by blinkfox on 2016/10/27.
*/
@Configuration // 1
public class JavaConfig {
@Bean // 2
public JavaConfigService javaConfigService() {
return new JavaConfigService();
}
@Bean
public UseJavaConfigService useJavaConfigService() {
UseJavaConfigService useJavaConfigService = new UseJavaConfigService();
useJavaConfigService.setJavaConfigService(javaConfigService()); // 3
return useJavaConfigService;
}
}
代码解释:
(4)运行
package com.blinkfox.maintest;
import com.blinkfox.config.JavaConfig;
import com.blinkfox.service.impl.UseJavaConfigService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Created by blinkfox on 2016/10/27.
*/
public class JavaConfigMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(JavaConfig.class);
UseJavaConfigService useJavaConfigService = context.getBean(UseJavaConfigService.class);
System.out.println(useJavaConfigService.sayHello("Spring Java Config"));
context.close();
}
}
AOP:面向切面编程,是面向对象编程(OOP)的补充。
Spring 的 AOP 的存在目的是为了解耦。AOP 可以让一组类共享相同的行为。在 OOP 中只能通过继承和实现接口来共享相同的行为,从而使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,AOP 弥补了 OOP 的不足。
Spring 支持 AspectJ 的注解式切面编程。
Spring本身在事务处理(@Transcational)和数据缓存(@Cacheable)等都使用注解拦截。下面示例将演示基于注解和方法规则的拦截方式,演示一种模拟记录操作的日志系统的实现。
(1)添加 Spring aop 支持及 AspectJ 依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
(2)编写拦截规则的注解。
package com.blinkfox.annotation;
import java.lang.annotation.*;
/**
* Created by blinkfox on 2016/10/29.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAction {
String name() default "这是默认的操作名称";
}
代码解释: 注解本身是没有功能的,就和 xml 一样。注解和 xml 都是一种元数据,元数据即解释数据的数据,这就是所谓的配置。注解的功能来自用这个注解的地方。
(3)编写使用注解的被拦截类。
package com.blinkfox.service.impl;
import com.blinkfox.annotation.LogAction;
import org.springframework.stereotype.Service;
/**
* Created by blinkfox on 2016/10/29.
*/
@Service
public class DemoAnnotationService {
@LogAction(name = "注解式拦截的 add 操作")
public void add() {
}
}
(4)编写使用方法规则被拦截规类。
package com.blinkfox.service.impl;
import org.springframework.stereotype.Service;
/**
* Created by blinkfox on 2016/10/29.
*/
@Service
public class DemoMethodService {
public void add() {
}
}
(5)编写切面。
package com.blinkfox.aop;
import com.blinkfox.annotation.LogAction;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Created by blinkfox on 2016/10/29.
*/
@Aspect // 1
@Component // 2
public class LogAspect {
@Pointcut("@annotation(com.blinkfox.annotation.LogAction)") // 3
public void annotationPointCut() {
}
@After("annotationPointCut()") // 4
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAction logAction = method.getAnnotation(LogAction.class);
System.out.println("---注解式拦截:" + logAction.name()); // 5
}
@After("execution(* com.blinkfox.service.impl.DemoMethodService.*(..))") // 6
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("---方法规则式拦截:" + method.getName());
}
}
代码解释:
(6)配置类。
package com.blinkfox.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* Created by blinkfox on 2016/10/29.
*/
@Configuration
@ComponentScan("com.blinkfox")
@EnableAspectJAutoProxy
public class AopConfig {
}
代码解释:
(6)运行。
package com.blinkfox.maintest;
import com.blinkfox.config.AopConfig;
import com.blinkfox.service.impl.DemoAnnotationService;
import com.blinkfox.service.impl.DemoMethodService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Created by blinkfox on 2016/10/29.
*/
public class AopMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AopConfig.class);
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
DemoMethodService demoMethodService = context.getBean(DemoMethodService.class);
demoAnnotationService.add();
demoMethodService.add();
context.close();
}
}