共计 8500 个字符,预计需要花费 22 分钟才能阅读完成。
1. Spring 简介
一句话概括 Spring,Spring 是一个开源的、轻量的 管制反转 (IOC) 和面向切面编程 (AOP) 的框架。
1.1 Spring、SpringMVC、Spring Boot 和 Spring Cloud 的区别
SpringMVC 是在 Spring 根底之上联合了 MVC 三层架构的框架,应用 DispatchServlet 和视图解析器做到了更不便的视图跳转和控制器调度解决,次要负责解决 WEB 利用开发。SpringMVC 是 Spring 的一个子模块。
SpringBoot 则应用了 约定大于配置 的理念,简化了 Spring 配置,专一于微服务的开发。
SpringCloud 专一于全局微服务的整合、治理。
2. 管制反转—IOC
2.1 基本概念
IOC:管制反转,是一种设计思维。即 将对象创立的控制权转移给了 Spring 容器。
假如咱们创立了类 A,类 A 中有个成员变量类 B。
- 传统开发:咱们须要手动创立类 B 的对象,将他赋给类 A 的对象,这叫被动管制对象的创立;
- IOC:Spring 创立好 B 对象,而后存储到一个容器外面,当 A 对象须要应用 B 对象时,Spring 就从寄存对象的那个容器外面取出 A 要应用的那个 B 对象。
应用传统形式,A 对 B 产生了依赖,也就是 A 和 B 之间存在一种耦合关系(假如 B 的实现有了改变,咱们可能还须要改变 A 的源码),而通过 IOC 的思维,这种依赖关系就被容器取代了,所有对象的创立依赖于容器,在启动的时候,Spring 会读取配置文件,并将创立的对象先注入容器,之后咱们想要应用间接从容器中取就能够。
DI:依赖注入,能够了解为是 Spring 实现 IOC 的具体形式。
2.2 Spring 创建对象形式
class UserService {
private UserDao dao;
public void setUserDao(UserDao dao) {this.userDao = dao;}
}
class UserDao {
private String name;
public void setName(String name) {this.name = name;}
}
2.2.1 构造函数
<bean id="dao" class="com.dong.dao.UserDao">
<!-- 应用 name 定位构造函数 -->
<constructor-arg name="name" value="dongjh"></constructor-arg>
</bean>
<bean id="service" class="com.dong.service.UserService">
<!-- 应用 type 定位构造函数 -->
<constructor-arg type="com.dong.dao.UserDao" ref="dao"></constructor-arg>
</bean>
注:
- 如果某类有无参构造方法,则默认调用无参构造方法;
- 如果某类只有一个构造方法,则默认调用该构造方法。
2.2.2 property(调用 set 办法创立)
<bean id="dao" class="com.dong.service.UserDao">
<!-- 根本类型应用 value 注入属性 -->
<property name="name" value="dongjh"></property>
</bean>
<bean id="service" class="com.dong.service.UserService">
<!-- 援用类型应用 ref 属性 -->
<property name="userDao" ref="dao"></property>
</bean>
注:
- 应用属性注入时,容器会 默认调用无参构造方法创立实例,所以须要保障要创立的 bean 有无参的构造方法;
- 应用属性注入等价于调用了
setXXX
办法,XXX 是 property 标签中的 name,假如下面的代码的setName
办法改成了setName1
,则第一个 bean 的 property 需这样写:
<property name="name1" value="dongjh"></property>
2.3 Bean 的主动拆卸(autowiring)
主动拆卸即 Spring 在上下文中主动寻找,主动给 bean 拆卸属性。
bean 的主动拆卸次要有三种策略:
byname
:把与某 bean(A)属性 具备雷同名字 的其余 bean(B)主动拆卸到该 bean(A)的属性中;byType
:把与某 bean(A)属性 具备雷同类型 的其余 bean(B)主动拆卸到该 bean(A)的属性中;constructor
:把与某 bean(A)的结构器入参 具备雷同类型 的其余 bean(B)主动拆卸到该 bean(A)的属性中;
2.3.1 byName
xml 配置形式
<!-- 因为该 bean 的 id 为 userDao,所以会间接主动拆卸上面 service 的 userDao 属性中 -->
<bean id="userDao" class="com.dong.service.UserDao">
<property name="name" value="dongjh"></property>
</bean>
<!-- 该类有 setUserDao 办法,且设置了属性主动拆卸 byName-->
<bean id="service" class="com.dong.service.UserService" autowired="byName">
</bean>
2.3.2 byType
xml 配置形式
<!-- 该 bean 的类型为 com.dong.service.UserDao,所以会间接主动拆卸上面 service 的 userDao 属性中 -->
<bean id="userDao111" class="com.dong.service.UserDao">
<property name="name" value="dongjh"></property>
</bean>
<!-- 该类一个 com.dong.service.UserDao 类型的属性,且设置了属性主动拆卸 byType-->
<bean id="service" class="com.dong.service.UserService" autowired="byType">
</bean>
2.4 Spring 注解配置
JDK1.5 开始反对注解,而 Spring 是 2.5 开始反对。
应用 Spring 注解配置须要先导入束缚: context 名称空间和注解扫描。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注:只会寻找在同一个利用上下文中定义的注解,比方将这个申明搁置到 DispatcherServlet 的配置上下文中,它就只会查看 controllers 的上下文,而不会查看 services 的上下文,详情见 https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-annotation-config-->
<context:annotation-config/>
<!-- 指定注解扫描返回 -->
<context:component-scan base-package="com.dong.dao"></context:component-scan>
</beans>
@Autowired
之后在须要主动拆卸的 属性或 set 办法 上应用 @AutoWired 注解,就能够实现主动拆卸。
// 具体的可润饰范畴
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
注:@Autowired 会先应用 byType,再按 byName 的程序主动拆卸。
@Autowired 有一个罕用属性:required,默认值为 true,这个属性示意润饰的成员变量是否肯定须要拆卸一个 bean:
- 当 required 设置为 true 时,示意必须有一个能够主动拆卸到该成员变量的 bean,否则报错;
- 当 required 设置为 false 时,则示意该成员变量能够为 null;
@Qualifier
当有多个 bean 满足主动拆卸条件时,能够应用这个指定拆卸的 bean,个别配合 @Autowired 应用。
@Qualifier 有个属性 value,给这个 value 设置想要拆卸的 bean 的 id 即可。
@Component
润饰类,示意将该类的 bean 对象注入到 Spring 容器中。
@Component 有个属性 value,示意注入时设置的 id。
如:
// 等价于 <bean id="user" class="User">
@Component(value="user")
public class User {}
@Component 有一些衍生注解,如 Web 开发会依照 MVC 架构分层,依据不同的性能层,衍生出了一些注解:
- @Repository:针对 dao 层
- @Service:针对 service 层
- @Controller:针对 controller 层
这四个注解性能统一,都示意将某类的 bean 对象注入到 Spring 容器中。
@Value
给属性注入值,次要用于给成员变量注入值,简略的能够 注入根本类型的值,简单时能够注入一些操作系统属性或其余 bean 的属性。
@Scope
用于设置 bean 的作用域,先介绍最常见的:
- singleton:全局惟一,单例;
- prototype:每次注入创立一个新的对象。
注解和配置的区别
xml 性能更加弱小,方便管理保护。注解实用于简略的属性注入。
2.5 JavaConfig 配置
Spring 3.0
之后开始反对了一种更优的配置形式:基于 Java 类的配置形式。通过减少 @Configuration 注解表明这是一个配置类,其底层为 @Component(即该类的 bean 对象也会被 Spring 容器接管)。
@Configuration 联合 @Bean 注解能够实现 XML 配置实现注入的性能,成果与
<beans>
<bean>...</bean>
<bean>...</bean>
</beans>
相似,在应用 @Bean 注解时,示意将该办法返回的对象加载进 Spring 容器,在应用 @Bean 润饰办法时须要留神:
- 办法带返回值,且返回类型为想要被治理的 bean 的类型;
- 办法名即为默认的 bean name,可应用 @Bean 的 name 属性自定义;
- 办法可带参数,则 此时传入的参数也必须是被 Spring 容器治理的 bean。
以创立 UserService 和 UserDao 为例:
@Configuration
public class UserConfiguration {
@Bean
public UserService userService() {return new UserService();
}
@Bean
public UserDao userDao() {return new UserDao();
}
}
当 Spring 扫描该类时,会主动将 UserConfiguration、UserService、UserDao 的 bean 注入到 Spring 容器中进行托管。
3. 面向切面编程—AOP
3.1 基本概念
在软件开发中,咱们须要编写许多的业务代码,但还有很多代码是业务无关的,如
- 日志性能
- 缓存清理
- 资源回收
- 权限查看
- …
为了实现业务代码和非业务代码的拆散,Spring 利用代理模式抽出了非业务代码,造成了 AOP 思维。
AOP 全称 Aspect Oriented Programming,意为面向切面编程,假如业务代码自身是一条纵向的逻辑流,咱们在其中找几个点(切点,Pointcut),插入非业务代码,就像是一个个切面(Aspect),这就是一个简略的 AOP 例子。
切面和切点很容易被混同,严格来说,切点是切面的一部分信息,切面次要蕴含两局部信息:
- 切入点;
- 切入后要执行的动作(Advice)。
而在具体的实现中,Spring 为切面还加了一个信息:在切点的哪个阶段执行?由此衍生出有:
- 前置告诉(Before):在指标办法被调用之前调用告诉性能;
- 后置告诉(After):在指标办法被调用之后调用告诉性能;
- 返回告诉(After-returning):在指标办法胜利执行之后调用告诉;
- 异样告诉(After-throwing):在指标办法抛出异样后调用告诉;
- 盘绕告诉(Around):告诉包裹了被告诉的办法,在被告诉的办法调用之前和调用之后执行自定义的行为。
对于 AOP 在 Spring 的实现有三种形式:
- 实现原生的 Spring API 接口
- 自定义切面类
- 注解
3.2 实现原生 Spring API 接口
Spring 提供了诸如 MethodBeforeAdvice、AfterAdvice、AfterReturningAdvice 等原生接口,咱们能够通过实现这些接口构建切面,如
@Component
public class Log implements MethodBeforeAdvice {
/**
* @param method 要执行的指标对象办法
* @param objects 参数
* @param o 指标对象
* @throws Throwable
*/
public void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println(o.getClass().getName() + "-" + method.getName() + "将要执行");
}
}
该 Log 类实现了 MethodBeforeAdvice,成为了一个在办法执行前插入的切面,其次,咱们须要在 XML 中进行配置,设置切点并建设其和切面的连贯:
<aop:config>
<!-- 切入点:execution 表达式,execution(要执行的办法!)-->
<aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
execution 示意在办法执行时触发;* 示意办法返回值能够是任意类型,com.dong.service.UserServiceImpl.* 应用全限定类名和办法名指定要增加前置告诉的办法,应用 * 示意对 com.dong.service.UserServiceImpl 类下的所有办法都执行切入;(..)示意办法的参数列表,应用 (..) 示意办法的入参能够是任意类型。
须要留神,Log 必须是一个被 Spring 容器治理的 bean 对象,否则 Spring 将无奈找到该切面的 bean,因而应用 @Component 注解润饰(也能够应用 XML 配置等形式注入)。
3.3 自定义切面类
自定义切面不须要实现 Spring 的原生接口,首先须要定义一个切面类,并将加载给 Spring 容器进行托管。
@Component(value="diy")
public class DiyPointCut {public void before() {System.out.println("办法执行前");
}
public void after() {System.out.println("办法执行后");
}
}
与 3.2 类似,该形式须要通过 XML 配置设置切点以及其和切面的连贯。
<aop:config>
<!-- 自定义切面,ref:要援用的 bean-->
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/>
<!-- 指定 advice 和切点的关系 -->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
3.4 注解
3.4.1 启用注解性能
首先,应用注解有两种形式:
-
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" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 启用注解 --> <context:annotation-config/>、<!-- 注解包扫描 --> <context:component-scan base-package="com.dong.dao"></context:component-scan> <context:component-scan base-package="com.dong.log"></context:component-scan> <!-- 启用 aop 注解 --> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
-
应用
@EnableAspectJAutoProxy
注解须要被切的类@Component(value = "userService") @EnableAspectJAutoProxy public class UserServiceImpl implements UserService {}
其次,应用注解时,须要留神两局部:
- 申明切面:申明某类是切面以及切入后理论要执行的动作(Advice);
- 定位切点:须要通过 execution 表达式定位须要切入的办法。
上面介绍一下注解的应用形式。
3.4.2 注解应用
只有对一个类应用 @Aspect 注解润饰,就表明这是一个切面。能够对其中的办法应用
- @Before
- @After
- @AfterReturning
- @Around
- @AfterThrowing
注解指定切入机会,切入点通过 execution 表达式指定,举例如下:
@Aspect
@Component
public class CustomPointCut {@Before("execution(* com.dong.service.UserServiceImpl.*(..))")
public void before() {System.out.println("aop: 办法产生前");
}
}
4. 参考资料
- Spring 最新 5 残缺教程
- Spring 入门(十):Spring AOP 应用解说
- Spring AOP – 注解形式应用介绍