切换默认shell到wsl
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
# POJO
|
||||
## POJO定义
|
||||
POJO(Plain Old Java Object)是一种直接的类型,POJO并不包含对任何框架的引用。
|
||||
> 对于POJO类型,该类属性和方法的定义并没有特定的约束和限制
|
||||
## Java Bean命名约束
|
||||
由于对POJO本身并没有对POJO类属性和方法的定义强制指定命名约束,因而许多框默认支持Java Bean命名约束。
|
||||
> ### Java Bean命名约束
|
||||
> 在Java Bean命名约束中,为POJO类属性和方法的命名指定了如下规则:
|
||||
> 1. 属性的访问权限都被设置为private,属性通过getter和setter向外暴露
|
||||
> 2. 对于方法的命名,getter和setter遵循getXXX/setXXX的命名规范(对于boolean属性的getter,可以使用isXXX形式
|
||||
> 3. Java Bean命名规范要求Java Bean对象需要提供无参构造方法
|
||||
> 4. 实现Serializable接口,能够将对象以二进制的格式进行存储
|
||||
## 其他命名规范
|
||||
由于Java Bean命名规范中有些规则强制对Java Bean的命名进行限制可能会带来弊端,故而如今许多框架在接受Java Bean命名规范之余,仍然支持其他的POJO命名规范
|
||||
> 如在Spring中,通过@Component注解注册Bean对象时,被@Component注解的类并不一定要实现Serializable接口,也不一定要拥有无参构造方法。
|
||||
# POJO
|
||||
## POJO定义
|
||||
POJO(Plain Old Java Object)是一种直接的类型,POJO并不包含对任何框架的引用。
|
||||
> 对于POJO类型,该类属性和方法的定义并没有特定的约束和限制
|
||||
## Java Bean命名约束
|
||||
由于对POJO本身并没有对POJO类属性和方法的定义强制指定命名约束,因而许多框默认支持Java Bean命名约束。
|
||||
> ### Java Bean命名约束
|
||||
> 在Java Bean命名约束中,为POJO类属性和方法的命名指定了如下规则:
|
||||
> 1. 属性的访问权限都被设置为private,属性通过getter和setter向外暴露
|
||||
> 2. 对于方法的命名,getter和setter遵循getXXX/setXXX的命名规范(对于boolean属性的getter,可以使用isXXX形式
|
||||
> 3. Java Bean命名规范要求Java Bean对象需要提供无参构造方法
|
||||
> 4. 实现Serializable接口,能够将对象以二进制的格式进行存储
|
||||
## 其他命名规范
|
||||
由于Java Bean命名规范中有些规则强制对Java Bean的命名进行限制可能会带来弊端,故而如今许多框架在接受Java Bean命名规范之余,仍然支持其他的POJO命名规范
|
||||
> 如在Spring中,通过@Component注解注册Bean对象时,被@Component注解的类并不一定要实现Serializable接口,也不一定要拥有无参构造方法。
|
||||
|
||||
@@ -1,109 +1,109 @@
|
||||
# SpEL(Spring Expression Language)
|
||||
- ## SpEL的用法
|
||||
- SpEL如何将表达式从字符串转化为计算后的值
|
||||
- 在转化过程中,在parseExpression方法执行时可能会抛出ParseException异常,在执行getValue方法时可能会抛出EvaluationException
|
||||
```java
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
Expression exp = parser.parseExpression("'Hello World'");
|
||||
String message = (String) exp.getValue();
|
||||
```
|
||||
- 在SpEL中获取String的字节数组
|
||||
```java
|
||||
ExpressionParser parser=new SpelExpressionParser();
|
||||
Expression exp=parser.parseExpression("'Hello World'.bytes");
|
||||
byte[] bytes=(byte[])exp.getValue();
|
||||
```
|
||||
- 在调用Expression类型的getValue方法时,可以不用进行强制类型转换,而是在getValue方法中传入一个Class参数,返回值将会被自动转换成Class对应的目标类型,当转换失败时会抛出EvaluationException
|
||||
```java
|
||||
ExpressionParser parser=new SpelExpressionParser();
|
||||
Expression exp=parser.parseExpression("'Hello World'.bytes.length");
|
||||
Integer bytes=exp.getValue(Integer.class);
|
||||
```
|
||||
- SpEL可以针对特定的对象,给出一个表达式并且在getValue方法中传入一个对象,那么表达式中的变量将会针对该对象中的特定属性
|
||||
```java
|
||||
// 如下步骤会比较waifu对象的name属性是否为"touma"字符串
|
||||
ExpressionParser parser=new SpelExpressionParser();
|
||||
Expression exp=parser.parseExpression("name=='touma'");
|
||||
Boolean equals=exp.getValue(waifu,Boolean.class);
|
||||
```
|
||||
- 可以为parser设置一个parserconfiguration,用于处理当列表或集合元素的index操作超过集合长度时的默认行为
|
||||
```java
|
||||
class Demo {
|
||||
public List<String> list;
|
||||
}
|
||||
|
||||
// Turn on:
|
||||
// - auto null reference initialization
|
||||
// - auto collection growing
|
||||
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
|
||||
|
||||
ExpressionParser parser = new SpelExpressionParser(config);
|
||||
|
||||
Expression expression = parser.parseExpression("list[3]");
|
||||
|
||||
Demo demo = new Demo();
|
||||
|
||||
Object o = expression.getValue(demo);
|
||||
|
||||
// demo.list will now be a real collection of 4 entries
|
||||
// Each entry is a new empty String
|
||||
```
|
||||
- ## SpEL在bean对象定义时的使用
|
||||
- 在使用@Value注解时,可以结合SpEL表达式进行使用,@Value注解可以运用在域变量、方法、方法和构造器的参数上。@Value会指定默认值
|
||||
- ## SpEL对List、Map的支持
|
||||
- 可以通过{}来直接表示list
|
||||
```java
|
||||
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
|
||||
|
||||
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
|
||||
```
|
||||
- 可以通过{key:value}形式来直接表示map,空map用{:}来进行表示
|
||||
```java
|
||||
// evaluates to a Java map containing the two entries
|
||||
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
|
||||
|
||||
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
|
||||
```
|
||||
- 可以通过new int[]{}的形式为SpEL指定数组
|
||||
```java
|
||||
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
|
||||
|
||||
// Array with initializer
|
||||
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
|
||||
|
||||
// Multi dimensional array
|
||||
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
|
||||
```
|
||||
- ## SpEL支持的特殊操作符
|
||||
- instanceof
|
||||
```java
|
||||
boolean falseValue = parser.parseExpression(
|
||||
"'xyz' instanceof T(Integer)").getValue(Boolean.class);
|
||||
```
|
||||
- 正则表达式
|
||||
```java
|
||||
boolean trueValue = parser.parseExpression(
|
||||
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
|
||||
```
|
||||
- 类型操作符,获取类型的Class对象、调用静态方法
|
||||
```java
|
||||
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
|
||||
|
||||
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
|
||||
|
||||
boolean trueValue = parser.parseExpression(
|
||||
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
|
||||
.getValue(Boolean.class);
|
||||
```
|
||||
- new操作符:
|
||||
- 可以在SpEL表达式中通过new操作符来调用构造器,但是除了位于java.lang包中的类,对其他的类调用构造器时都必须指定类的全类名
|
||||
```java
|
||||
Inventor einstein = p.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
.getValue(Inventor.class);
|
||||
|
||||
// create new Inventor instance within the add() method of List
|
||||
p.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor(
|
||||
'Albert Einstein', 'German'))").getValue(societyContext);
|
||||
# SpEL(Spring Expression Language)
|
||||
- ## SpEL的用法
|
||||
- SpEL如何将表达式从字符串转化为计算后的值
|
||||
- 在转化过程中,在parseExpression方法执行时可能会抛出ParseException异常,在执行getValue方法时可能会抛出EvaluationException
|
||||
```java
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
Expression exp = parser.parseExpression("'Hello World'");
|
||||
String message = (String) exp.getValue();
|
||||
```
|
||||
- 在SpEL中获取String的字节数组
|
||||
```java
|
||||
ExpressionParser parser=new SpelExpressionParser();
|
||||
Expression exp=parser.parseExpression("'Hello World'.bytes");
|
||||
byte[] bytes=(byte[])exp.getValue();
|
||||
```
|
||||
- 在调用Expression类型的getValue方法时,可以不用进行强制类型转换,而是在getValue方法中传入一个Class参数,返回值将会被自动转换成Class对应的目标类型,当转换失败时会抛出EvaluationException
|
||||
```java
|
||||
ExpressionParser parser=new SpelExpressionParser();
|
||||
Expression exp=parser.parseExpression("'Hello World'.bytes.length");
|
||||
Integer bytes=exp.getValue(Integer.class);
|
||||
```
|
||||
- SpEL可以针对特定的对象,给出一个表达式并且在getValue方法中传入一个对象,那么表达式中的变量将会针对该对象中的特定属性
|
||||
```java
|
||||
// 如下步骤会比较waifu对象的name属性是否为"touma"字符串
|
||||
ExpressionParser parser=new SpelExpressionParser();
|
||||
Expression exp=parser.parseExpression("name=='touma'");
|
||||
Boolean equals=exp.getValue(waifu,Boolean.class);
|
||||
```
|
||||
- 可以为parser设置一个parserconfiguration,用于处理当列表或集合元素的index操作超过集合长度时的默认行为
|
||||
```java
|
||||
class Demo {
|
||||
public List<String> list;
|
||||
}
|
||||
|
||||
// Turn on:
|
||||
// - auto null reference initialization
|
||||
// - auto collection growing
|
||||
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
|
||||
|
||||
ExpressionParser parser = new SpelExpressionParser(config);
|
||||
|
||||
Expression expression = parser.parseExpression("list[3]");
|
||||
|
||||
Demo demo = new Demo();
|
||||
|
||||
Object o = expression.getValue(demo);
|
||||
|
||||
// demo.list will now be a real collection of 4 entries
|
||||
// Each entry is a new empty String
|
||||
```
|
||||
- ## SpEL在bean对象定义时的使用
|
||||
- 在使用@Value注解时,可以结合SpEL表达式进行使用,@Value注解可以运用在域变量、方法、方法和构造器的参数上。@Value会指定默认值
|
||||
- ## SpEL对List、Map的支持
|
||||
- 可以通过{}来直接表示list
|
||||
```java
|
||||
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
|
||||
|
||||
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
|
||||
```
|
||||
- 可以通过{key:value}形式来直接表示map,空map用{:}来进行表示
|
||||
```java
|
||||
// evaluates to a Java map containing the two entries
|
||||
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
|
||||
|
||||
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
|
||||
```
|
||||
- 可以通过new int[]{}的形式为SpEL指定数组
|
||||
```java
|
||||
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
|
||||
|
||||
// Array with initializer
|
||||
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
|
||||
|
||||
// Multi dimensional array
|
||||
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
|
||||
```
|
||||
- ## SpEL支持的特殊操作符
|
||||
- instanceof
|
||||
```java
|
||||
boolean falseValue = parser.parseExpression(
|
||||
"'xyz' instanceof T(Integer)").getValue(Boolean.class);
|
||||
```
|
||||
- 正则表达式
|
||||
```java
|
||||
boolean trueValue = parser.parseExpression(
|
||||
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
|
||||
```
|
||||
- 类型操作符,获取类型的Class对象、调用静态方法
|
||||
```java
|
||||
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
|
||||
|
||||
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
|
||||
|
||||
boolean trueValue = parser.parseExpression(
|
||||
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
|
||||
.getValue(Boolean.class);
|
||||
```
|
||||
- new操作符:
|
||||
- 可以在SpEL表达式中通过new操作符来调用构造器,但是除了位于java.lang包中的类,对其他的类调用构造器时都必须指定类的全类名
|
||||
```java
|
||||
Inventor einstein = p.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
.getValue(Inventor.class);
|
||||
|
||||
// create new Inventor instance within the add() method of List
|
||||
p.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor(
|
||||
'Albert Einstein', 'German'))").getValue(societyContext);
|
||||
```
|
||||
@@ -1,177 +1,177 @@
|
||||
# Spring AOP
|
||||
- ## Spring AOP的核心概念
|
||||
- Aspect:切面,一个模块化的考虑
|
||||
- Joint Point:连接点,程序执行时的一个时间点,通常是方法的执行
|
||||
- Advice:当切面在一个切入点执行多做时,执行的动作被称之为Advice,Advice有不同的类型:before、after、around
|
||||
- Pointcut:切入点,advice通常运行在满足pointcut的join point上,pointcut表达式与join point相关联,Spring中默认使用AspectJ切入点表达式
|
||||
- Introduction:在类中声明新的方法、域变量甚至是接口实现
|
||||
- linking:将应用类型或对象和切面链接起来
|
||||
- ## Spring AOP的类型
|
||||
- before:在连接点之前运行,但是无法阻止后续连接点的执行
|
||||
- after returning:在连接点正常返回之后进行
|
||||
- after throwing:在链接点抛出异常正常退出之后进行
|
||||
- after finally:上两种的结合,不管连接点是正常退出还是抛出异常退出,都会在其之后执行
|
||||
- around:around可以自定义连接点之前和之后的执行内容,其也能够选择时候执行连接点的方法
|
||||
- ## Spring AOP的特点
|
||||
- 区别于AspectJ AOP框架,Spring AOP框架是基于代理来实现的
|
||||
- 对于实现了接口的类,Spring AOP通常是通过JDK动态代理来实现的,对于没有实现接口的类,Spring AOP是通过cglib来实现的
|
||||
- 可以强制Spring AOP使用cglib,在如下场景:
|
||||
- 如果想要advise类中方法,而该方法没有在接口中定义
|
||||
- 如果想要将代理对象传递给一个具有特定类型的方法作为参数
|
||||
- ## Spring AOP的AspectJ注解支持
|
||||
- Spring AOP支持AspectJ注解,Spring AOP可以解释和AspectJ 5相同的注解,通过使用AspectJ提供的包来进行切入点解析和匹配
|
||||
- 但是,即使使用了AspectJ注解,AOP在运行时仍然是纯粹的Spring AOP,项目不需要引入AspectJ的编译器和weaver
|
||||
- Spring AOP对AspectJ注解支持的开启
|
||||
- 通过@EnableAspectJAutoProxy注解,会自动的为满足切入点匹配的连接点bean对象创建移动代理对象
|
||||
```java
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy
|
||||
class AspectJConfiguration {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- ## 声明Spring AOP切面
|
||||
- 在容器中,任何bean对象,如其类型具有@AspectJ注解,将会被自动探知到并且用来配置spring aop
|
||||
- 在Spring AOP中,aspect其自身是无法作为其他aspect的目标对象的。被标记为@Aspect的类,不仅标明其为aspect,并且将其从自动代理中排除
|
||||
- 如果为某个bean对象配置了切面,那么在后续创建该bean对象时,实际上是创建该bean对象的代理对象
|
||||
```java
|
||||
@Component // 将该类型声明为bean对象
|
||||
@Aspect // 声明切面
|
||||
public class ProxyAspect {
|
||||
|
||||
}
|
||||
```
|
||||
- ## 声明Spring AOP切入点
|
||||
- 由于Spring AOP仅仅支持方法的连接点,故而可以将切入点看做对bean对象方法的匹配
|
||||
- Join Point expression的种类:
|
||||
- execution:匹配目标方法的执行,可以在括号中接收一个函数签名,包含返回类型、函数名和函数参数类型
|
||||
```java
|
||||
// 被@JointPoint注解标注的方法必须具有void的返回类型
|
||||
@Pointcut("execution(* Point.*(..))")
|
||||
void methodInjected() {
|
||||
|
||||
}
|
||||
```
|
||||
- within:匹配声明在某一特定类中的方法
|
||||
```java
|
||||
@Pointcut("within(Point)")
|
||||
```
|
||||
- this:匹配生成的代理对象为该类型的一个实例
|
||||
- target:匹配目标对象为该类型的一个实例
|
||||
- args:匹配特定参数
|
||||
- @args:传递参数的类型具有指定的注解
|
||||
- @target:运行时该对象的类具有指定的注解
|
||||
- @within:运行时执行的方法,其方法定义在具有指定注解的类中(可以是继承父类的方法,父类指定了注解
|
||||
- @annotation:执行的方法具有指定注解
|
||||
- Spring AOP同样支持将JoinPoint匹配为具有特定name的Spring bean对象
|
||||
```java
|
||||
@Pointcut("bean(nameA) || bean(nameB))")
|
||||
```
|
||||
- ## Spring AOP中的Advice
|
||||
- Advice和Pointcut Expresion相关联,主要可以分为before、after、around等种类
|
||||
- Before:
|
||||
```java
|
||||
@Before("execution(* Point.*(..))")
|
||||
public void doSomething() {
|
||||
|
||||
}
|
||||
```
|
||||
- AfterReturning:
|
||||
```java
|
||||
// AfterReturning支持获取切入点执行后返回的值
|
||||
@AfterReturning(
|
||||
pointcut="execution(* Point.*(..))",
|
||||
returning="retVal")
|
||||
public void doSomething(int retVal) {
|
||||
|
||||
}
|
||||
```
|
||||
- AfterThrowing:
|
||||
```java
|
||||
@AfterThrowing(
|
||||
pointcut="execution(* Point.*())",
|
||||
throwing="ex"
|
||||
)
|
||||
public void doSomething(Throwable ex) {
|
||||
|
||||
}
|
||||
```
|
||||
- After:After不管是切入点正常返回还是抛出异常,都会执行,类似于finally
|
||||
```java
|
||||
@After("execution(* Point.*())")
|
||||
public void doSomething() {
|
||||
|
||||
}
|
||||
```
|
||||
- Around:其方法必须会一个Oject类型的返回值,并且方法的第一个参数类型是ProceedingJoinPoint
|
||||
```java
|
||||
@Around("execution(* Point.*())")
|
||||
public Object doSomething(ProceedingJoinPoint pjp) {
|
||||
return isCacheExisted()?returnFromCache():pjp.proceed();
|
||||
}
|
||||
```
|
||||
- ## Spring AOP中Advice方法对JoinPoint的访问
|
||||
- 任何advice方法,都可以声明声明其第一个参数为JoinPoint类型。@Around标注的adivce方法其第一个参数的类型必须为ProceedingJoinPoint类型,该类型为JoinPoint的子类型
|
||||
- JoinPoint接口具有如下方法:
|
||||
- getArgs:返回方法参数
|
||||
- getThis:返回代理对象
|
||||
- getTarget:返回目标对象
|
||||
- getSignature:返回函数的签名
|
||||
- toString:返回该advice方法的描述信息
|
||||
- ## Advice方法通过参数来获取传递给底层方法的参数
|
||||
- 在pointcut表达式的args中,如果用advice方法中的参数名来代替参数类型,那么该类型的参数值会被传递给该参数
|
||||
```java
|
||||
@Before("execution(* Point.*(..) && args(position,..))")
|
||||
public void adviceMethod(Position position) {
|
||||
|
||||
}
|
||||
```
|
||||
- 或者,可以通过如下方式,先通过一个Pointcut获取参数,在在另一个方法中获取named pointcut已获取的参数
|
||||
```java
|
||||
// 此时,adviceMethodTwo同样能够获取Position参数
|
||||
@Pointcut("execution(* Point.*(..)) && args(position,..)")
|
||||
public void adviceMethodOne(Position position) {
|
||||
|
||||
}
|
||||
|
||||
@Before("adviceMethodOne(position)")
|
||||
public void adviceMethodTwo(Position position) {
|
||||
|
||||
}
|
||||
```
|
||||
- Spring AOP可以通过如下方式来约束泛型的参数
|
||||
```java
|
||||
@Before("execution(* GenericsInterface+.method(*) && args(param))")
|
||||
public void adviceMethod(DesiredType param) {
|
||||
|
||||
}
|
||||
```
|
||||
- ## 通过Spring AOP对参数进行预处理
|
||||
```java
|
||||
@Around("execution(* Point.area(*) && args(width,height))")
|
||||
public double caculateInCM(ProceedingJoinPoint jp,double width,double height) {
|
||||
width*=100;
|
||||
height*=100;
|
||||
return jp.proceed(width,height);
|
||||
}
|
||||
```
|
||||
- ## Spring AOP中多个advice对应到同一个Pointcut
|
||||
- 如果多个advice都具有相同的pointcut,那么多个advice之间的执行顺序是未定义的。可以为Aspect类实现Ordered接口,或者添加@Order标记来定义该advice的执行优先级,那么具有具有较小order值的方法将会优先被执行
|
||||
- ## Spring AOP Introduction
|
||||
- 在Spring AOP中,可以通过Introduction来声明一个对象继承了某接口,并且为被代理的对象提供被继承接口的实现
|
||||
- 可以通过@DeclareParent注解为指定对象添加接口并且指明该接口默认的实现类,完成后可以直接将生成的代理对象复制给接口变量
|
||||
```java
|
||||
@Aspect
|
||||
public class MyAspect {
|
||||
@DeclareParent(value="cc.rikakonatsumi.interfaces.*+",defaultImpl=DefaultImpl.class)
|
||||
private static MyInterface myInterface;
|
||||
|
||||
// 之后,可以直接通过this(ref)在pointcut表达式中获取服务对象,也可以通过getBean方法获取容器中的对象
|
||||
}
|
||||
```
|
||||
- ## @RestControllerAdvice的使用
|
||||
- @RestControllerAdvice是@Componnent注解的一个特例,@RestControllerAdivce注解的组成包含@Component
|
||||
- @RestControllerAdivce组合了@ControllerAdvice和@ResponseBody两个注解
|
||||
- 通常,@RestControllerAdvice用作为spring mvc的所有方法做ExceptionHandler
|
||||
|
||||
|
||||
# Spring AOP
|
||||
- ## Spring AOP的核心概念
|
||||
- Aspect:切面,一个模块化的考虑
|
||||
- Joint Point:连接点,程序执行时的一个时间点,通常是方法的执行
|
||||
- Advice:当切面在一个切入点执行多做时,执行的动作被称之为Advice,Advice有不同的类型:before、after、around
|
||||
- Pointcut:切入点,advice通常运行在满足pointcut的join point上,pointcut表达式与join point相关联,Spring中默认使用AspectJ切入点表达式
|
||||
- Introduction:在类中声明新的方法、域变量甚至是接口实现
|
||||
- linking:将应用类型或对象和切面链接起来
|
||||
- ## Spring AOP的类型
|
||||
- before:在连接点之前运行,但是无法阻止后续连接点的执行
|
||||
- after returning:在连接点正常返回之后进行
|
||||
- after throwing:在链接点抛出异常正常退出之后进行
|
||||
- after finally:上两种的结合,不管连接点是正常退出还是抛出异常退出,都会在其之后执行
|
||||
- around:around可以自定义连接点之前和之后的执行内容,其也能够选择时候执行连接点的方法
|
||||
- ## Spring AOP的特点
|
||||
- 区别于AspectJ AOP框架,Spring AOP框架是基于代理来实现的
|
||||
- 对于实现了接口的类,Spring AOP通常是通过JDK动态代理来实现的,对于没有实现接口的类,Spring AOP是通过cglib来实现的
|
||||
- 可以强制Spring AOP使用cglib,在如下场景:
|
||||
- 如果想要advise类中方法,而该方法没有在接口中定义
|
||||
- 如果想要将代理对象传递给一个具有特定类型的方法作为参数
|
||||
- ## Spring AOP的AspectJ注解支持
|
||||
- Spring AOP支持AspectJ注解,Spring AOP可以解释和AspectJ 5相同的注解,通过使用AspectJ提供的包来进行切入点解析和匹配
|
||||
- 但是,即使使用了AspectJ注解,AOP在运行时仍然是纯粹的Spring AOP,项目不需要引入AspectJ的编译器和weaver
|
||||
- Spring AOP对AspectJ注解支持的开启
|
||||
- 通过@EnableAspectJAutoProxy注解,会自动的为满足切入点匹配的连接点bean对象创建移动代理对象
|
||||
```java
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy
|
||||
class AspectJConfiguration {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- ## 声明Spring AOP切面
|
||||
- 在容器中,任何bean对象,如其类型具有@AspectJ注解,将会被自动探知到并且用来配置spring aop
|
||||
- 在Spring AOP中,aspect其自身是无法作为其他aspect的目标对象的。被标记为@Aspect的类,不仅标明其为aspect,并且将其从自动代理中排除
|
||||
- 如果为某个bean对象配置了切面,那么在后续创建该bean对象时,实际上是创建该bean对象的代理对象
|
||||
```java
|
||||
@Component // 将该类型声明为bean对象
|
||||
@Aspect // 声明切面
|
||||
public class ProxyAspect {
|
||||
|
||||
}
|
||||
```
|
||||
- ## 声明Spring AOP切入点
|
||||
- 由于Spring AOP仅仅支持方法的连接点,故而可以将切入点看做对bean对象方法的匹配
|
||||
- Join Point expression的种类:
|
||||
- execution:匹配目标方法的执行,可以在括号中接收一个函数签名,包含返回类型、函数名和函数参数类型
|
||||
```java
|
||||
// 被@JointPoint注解标注的方法必须具有void的返回类型
|
||||
@Pointcut("execution(* Point.*(..))")
|
||||
void methodInjected() {
|
||||
|
||||
}
|
||||
```
|
||||
- within:匹配声明在某一特定类中的方法
|
||||
```java
|
||||
@Pointcut("within(Point)")
|
||||
```
|
||||
- this:匹配生成的代理对象为该类型的一个实例
|
||||
- target:匹配目标对象为该类型的一个实例
|
||||
- args:匹配特定参数
|
||||
- @args:传递参数的类型具有指定的注解
|
||||
- @target:运行时该对象的类具有指定的注解
|
||||
- @within:运行时执行的方法,其方法定义在具有指定注解的类中(可以是继承父类的方法,父类指定了注解
|
||||
- @annotation:执行的方法具有指定注解
|
||||
- Spring AOP同样支持将JoinPoint匹配为具有特定name的Spring bean对象
|
||||
```java
|
||||
@Pointcut("bean(nameA) || bean(nameB))")
|
||||
```
|
||||
- ## Spring AOP中的Advice
|
||||
- Advice和Pointcut Expresion相关联,主要可以分为before、after、around等种类
|
||||
- Before:
|
||||
```java
|
||||
@Before("execution(* Point.*(..))")
|
||||
public void doSomething() {
|
||||
|
||||
}
|
||||
```
|
||||
- AfterReturning:
|
||||
```java
|
||||
// AfterReturning支持获取切入点执行后返回的值
|
||||
@AfterReturning(
|
||||
pointcut="execution(* Point.*(..))",
|
||||
returning="retVal")
|
||||
public void doSomething(int retVal) {
|
||||
|
||||
}
|
||||
```
|
||||
- AfterThrowing:
|
||||
```java
|
||||
@AfterThrowing(
|
||||
pointcut="execution(* Point.*())",
|
||||
throwing="ex"
|
||||
)
|
||||
public void doSomething(Throwable ex) {
|
||||
|
||||
}
|
||||
```
|
||||
- After:After不管是切入点正常返回还是抛出异常,都会执行,类似于finally
|
||||
```java
|
||||
@After("execution(* Point.*())")
|
||||
public void doSomething() {
|
||||
|
||||
}
|
||||
```
|
||||
- Around:其方法必须会一个Oject类型的返回值,并且方法的第一个参数类型是ProceedingJoinPoint
|
||||
```java
|
||||
@Around("execution(* Point.*())")
|
||||
public Object doSomething(ProceedingJoinPoint pjp) {
|
||||
return isCacheExisted()?returnFromCache():pjp.proceed();
|
||||
}
|
||||
```
|
||||
- ## Spring AOP中Advice方法对JoinPoint的访问
|
||||
- 任何advice方法,都可以声明声明其第一个参数为JoinPoint类型。@Around标注的adivce方法其第一个参数的类型必须为ProceedingJoinPoint类型,该类型为JoinPoint的子类型
|
||||
- JoinPoint接口具有如下方法:
|
||||
- getArgs:返回方法参数
|
||||
- getThis:返回代理对象
|
||||
- getTarget:返回目标对象
|
||||
- getSignature:返回函数的签名
|
||||
- toString:返回该advice方法的描述信息
|
||||
- ## Advice方法通过参数来获取传递给底层方法的参数
|
||||
- 在pointcut表达式的args中,如果用advice方法中的参数名来代替参数类型,那么该类型的参数值会被传递给该参数
|
||||
```java
|
||||
@Before("execution(* Point.*(..) && args(position,..))")
|
||||
public void adviceMethod(Position position) {
|
||||
|
||||
}
|
||||
```
|
||||
- 或者,可以通过如下方式,先通过一个Pointcut获取参数,在在另一个方法中获取named pointcut已获取的参数
|
||||
```java
|
||||
// 此时,adviceMethodTwo同样能够获取Position参数
|
||||
@Pointcut("execution(* Point.*(..)) && args(position,..)")
|
||||
public void adviceMethodOne(Position position) {
|
||||
|
||||
}
|
||||
|
||||
@Before("adviceMethodOne(position)")
|
||||
public void adviceMethodTwo(Position position) {
|
||||
|
||||
}
|
||||
```
|
||||
- Spring AOP可以通过如下方式来约束泛型的参数
|
||||
```java
|
||||
@Before("execution(* GenericsInterface+.method(*) && args(param))")
|
||||
public void adviceMethod(DesiredType param) {
|
||||
|
||||
}
|
||||
```
|
||||
- ## 通过Spring AOP对参数进行预处理
|
||||
```java
|
||||
@Around("execution(* Point.area(*) && args(width,height))")
|
||||
public double caculateInCM(ProceedingJoinPoint jp,double width,double height) {
|
||||
width*=100;
|
||||
height*=100;
|
||||
return jp.proceed(width,height);
|
||||
}
|
||||
```
|
||||
- ## Spring AOP中多个advice对应到同一个Pointcut
|
||||
- 如果多个advice都具有相同的pointcut,那么多个advice之间的执行顺序是未定义的。可以为Aspect类实现Ordered接口,或者添加@Order标记来定义该advice的执行优先级,那么具有具有较小order值的方法将会优先被执行
|
||||
- ## Spring AOP Introduction
|
||||
- 在Spring AOP中,可以通过Introduction来声明一个对象继承了某接口,并且为被代理的对象提供被继承接口的实现
|
||||
- 可以通过@DeclareParent注解为指定对象添加接口并且指明该接口默认的实现类,完成后可以直接将生成的代理对象复制给接口变量
|
||||
```java
|
||||
@Aspect
|
||||
public class MyAspect {
|
||||
@DeclareParent(value="cc.rikakonatsumi.interfaces.*+",defaultImpl=DefaultImpl.class)
|
||||
private static MyInterface myInterface;
|
||||
|
||||
// 之后,可以直接通过this(ref)在pointcut表达式中获取服务对象,也可以通过getBean方法获取容器中的对象
|
||||
}
|
||||
```
|
||||
- ## @RestControllerAdvice的使用
|
||||
- @RestControllerAdvice是@Componnent注解的一个特例,@RestControllerAdivce注解的组成包含@Component
|
||||
- @RestControllerAdivce组合了@ControllerAdvice和@ResponseBody两个注解
|
||||
- 通常,@RestControllerAdvice用作为spring mvc的所有方法做ExceptionHandler
|
||||
|
||||
|
||||
|
||||
@@ -1,183 +1,183 @@
|
||||
# Spring Core IOC
|
||||
- ## IOC容器和bean简介
|
||||
- IOC简介:
|
||||
- IOC(控制反转)也被称之为依赖注入(DI),对象通过构造函数参数、工厂方法参数、或者在构造后通过setter来设置属性来定义依赖。在对象被创建时,IOC容器会将依赖注入到bean对象中,
|
||||
- IOC容器:
|
||||
- IOC容器接口:
|
||||
- BeanFactory:BeanFactory是一个接口,提供了高级配置功能来管理任何类型的对象
|
||||
- ApplicationContext:ApplicationContext是BeanFactory的一个子接口,在BeanFactory的基础上,其添加了一些更为特殊的特性。
|
||||
- IOC容器职责
|
||||
- IOC容器负责来初始化、配置、组装bean对象
|
||||
- ## 基于注解的Spring容器配置
|
||||
- @Required
|
||||
- @Required应用于bean对象属性的setter方法,表示该属性在配置时必须被填充,通过依赖注入或是用xml定义bean时显式指定值
|
||||
- 该注解当前已经被弃用
|
||||
- @Autowired
|
||||
- 通过在构造函数上标明@Autowired来对方法参数进行注入
|
||||
- 当在构造函数上标记@Autowired时,如果当前类中只有一个构造函数,那么@Autowired注解可以被省略;如果当前类有多个构造函数,那么应该在某个构造函数上指明@Autowired注解
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public Shiromiya(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate=jdbcTemplate;
|
||||
}
|
||||
}
|
||||
```
|
||||
- 同样,可以在setter上应用@Component
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public setJdbcTemplate(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate=jdbcTemplate;
|
||||
}
|
||||
}
|
||||
```
|
||||
- 将@Autowired应用于字段
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
}
|
||||
```
|
||||
- 通过@Autowired获取相同类型的所有bean对象
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private String[] waifus;
|
||||
/*
|
||||
* 这里,同样也支持Collections类型
|
||||
* 例如 List<String>
|
||||
*/
|
||||
|
||||
@Autowired
|
||||
public Shiromiya(String[] waifus) {
|
||||
this.waifus=waifus;
|
||||
}
|
||||
}
|
||||
|
||||
// 同样,可以通过Map类型来获取所有相同类型bean对象的name和value
|
||||
// key:对应bean对象的name
|
||||
// value:对应该bean对象的值
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private Map<String,String> waifus;
|
||||
|
||||
@Autowired
|
||||
public void setWaifus(Map<String,String> waifus) {
|
||||
this.waifus=waifus;
|
||||
}
|
||||
}
|
||||
```
|
||||
- 在@Autowired标记在构造函数上时,即使required为true,在参数为多bean类型时,即使没有匹配的bean,该属性会赋值为{}(空集合)而不是抛出异常
|
||||
- @Autowired作用与构造函数的规则
|
||||
- 当required属性为其默认值true时,在bean类型中只有一个构造函数可以用@Autowired标注
|
||||
- 如果bean类型中有多个构造函数标注了@Autowired注解,那么那么他们都必须将required属性设置为false,并且所有标注了@Autowired属性的构造函数都会被视为依赖注入的候选构造函数
|
||||
- 如果有多个候选的构造函数,那么在IOC容器中可以满足的匹配bean最多的构造函数将会被选中
|
||||
- 如果没有候选函数被选中,那么其会采用默认构造函数,如无默认构造函数,则抛出异常
|
||||
- 如果bean有多个构造函数,并且所有构造函数都没有标明@Autowired注解,那么会采用默认构造函数,如果默认构造函数不存在,抛出异常
|
||||
- 如果bean类型只有一个构造函数,那么该构造函数会被用来进行依赖注入,即使该构造函数没有标注@Autowired注解
|
||||
- 除了使用@Autowired的required属性,还可以使用@Nullable注解来标注可为空
|
||||
```java
|
||||
@Component
|
||||
public class Shiromiya {
|
||||
@Autowired
|
||||
@Nullable
|
||||
private int num;
|
||||
}
|
||||
```
|
||||
- @Primary
|
||||
- @Autowired注解是通过类型注入,如果相同类型存在多个bean时,可以通过@Primary注解来表明一个primary bean
|
||||
```java
|
||||
@Configuration
|
||||
public class BeanConfiguration {
|
||||
@Bean
|
||||
@Primary
|
||||
public String name_1() {
|
||||
return "kazusa";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String name_2() {
|
||||
return "ogiso";
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 此时,若通过@Autowired注入String类型,“kazusa”将会是默认值
|
||||
*/
|
||||
```
|
||||
- @Qualifier
|
||||
- 可以通过@Qualifier来指定bean的name导入特定bean,并且可以为bean指定默认的qualifier
|
||||
```java
|
||||
@Component
|
||||
public class Shiromiya {
|
||||
@Autowired
|
||||
@Qualifier("name_2")
|
||||
private String n_2;
|
||||
|
||||
private String n_1;
|
||||
|
||||
@Autowired
|
||||
public Shiromiya(@Qualifier("name_1") String n) {
|
||||
this.n_1=n;
|
||||
}
|
||||
}
|
||||
```
|
||||
- bean对象的qualifier并不需要唯一,可以为不同的bean对象赋值相同的qualifier,并且在注入bean集合的时候根据qualifier过滤
|
||||
```java
|
||||
@Configuration
|
||||
@Qualifier("config")
|
||||
class BeanConfiguration {
|
||||
@Bean
|
||||
@Qualifier("name")
|
||||
public String name_1() {
|
||||
return "kazusa";
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("name")
|
||||
public String name_2() {
|
||||
return "ogiso";
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("not-name")
|
||||
public String not_name_1() {
|
||||
return "fuck";
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
public class Person {
|
||||
/* 此nameList属性会注入qualifier为name的所有bean
|
||||
* 在此处为"kazusa"和"ogiso"
|
||||
*/
|
||||
@Autowired
|
||||
@Qualifier("name")
|
||||
Map<String,String> nameList;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("config")
|
||||
BeanConfiguration conf;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"nameList=" + nameList +
|
||||
", conf=" + conf +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
```
|
||||
- 作为一种回退机制,当bean的qualifier未被定义时,bean的name属性将会被作为其qualifier,autowired时会根据@Qualifier注解中指定的值匹配具有相同name的bean对象
|
||||
- 若想根据bean的name进行匹配,无需@Qualifier注解,只需要将注入点的name(filed的变量名,标注为@Autowired函数的形参名)和bean的name进行比较,如果相同则匹配成功,否则匹配失败
|
||||
- @Autowired同样支持自身引用的注入,但是自身引用的注入只能作为一种fallback机制。如果当前IOC容器中存在其他的同类型对象,那么其他对象会被优先注入,对象自己并不会参与候选的对象注入。但是,如果IOC中并不存在其他同类型对象,那么自身对象将会被作为引用注入。
|
||||
- @Resource
|
||||
- @Resource标签类似于@Autowired标签,但是@Resource具有一个name属性用来匹配bean对象的name属性
|
||||
# Spring Core IOC
|
||||
- ## IOC容器和bean简介
|
||||
- IOC简介:
|
||||
- IOC(控制反转)也被称之为依赖注入(DI),对象通过构造函数参数、工厂方法参数、或者在构造后通过setter来设置属性来定义依赖。在对象被创建时,IOC容器会将依赖注入到bean对象中,
|
||||
- IOC容器:
|
||||
- IOC容器接口:
|
||||
- BeanFactory:BeanFactory是一个接口,提供了高级配置功能来管理任何类型的对象
|
||||
- ApplicationContext:ApplicationContext是BeanFactory的一个子接口,在BeanFactory的基础上,其添加了一些更为特殊的特性。
|
||||
- IOC容器职责
|
||||
- IOC容器负责来初始化、配置、组装bean对象
|
||||
- ## 基于注解的Spring容器配置
|
||||
- @Required
|
||||
- @Required应用于bean对象属性的setter方法,表示该属性在配置时必须被填充,通过依赖注入或是用xml定义bean时显式指定值
|
||||
- 该注解当前已经被弃用
|
||||
- @Autowired
|
||||
- 通过在构造函数上标明@Autowired来对方法参数进行注入
|
||||
- 当在构造函数上标记@Autowired时,如果当前类中只有一个构造函数,那么@Autowired注解可以被省略;如果当前类有多个构造函数,那么应该在某个构造函数上指明@Autowired注解
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public Shiromiya(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate=jdbcTemplate;
|
||||
}
|
||||
}
|
||||
```
|
||||
- 同样,可以在setter上应用@Component
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public setJdbcTemplate(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate=jdbcTemplate;
|
||||
}
|
||||
}
|
||||
```
|
||||
- 将@Autowired应用于字段
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
}
|
||||
```
|
||||
- 通过@Autowired获取相同类型的所有bean对象
|
||||
```java
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private String[] waifus;
|
||||
/*
|
||||
* 这里,同样也支持Collections类型
|
||||
* 例如 List<String>
|
||||
*/
|
||||
|
||||
@Autowired
|
||||
public Shiromiya(String[] waifus) {
|
||||
this.waifus=waifus;
|
||||
}
|
||||
}
|
||||
|
||||
// 同样,可以通过Map类型来获取所有相同类型bean对象的name和value
|
||||
// key:对应bean对象的name
|
||||
// value:对应该bean对象的值
|
||||
@Component
|
||||
class Shiromiya {
|
||||
private Map<String,String> waifus;
|
||||
|
||||
@Autowired
|
||||
public void setWaifus(Map<String,String> waifus) {
|
||||
this.waifus=waifus;
|
||||
}
|
||||
}
|
||||
```
|
||||
- 在@Autowired标记在构造函数上时,即使required为true,在参数为多bean类型时,即使没有匹配的bean,该属性会赋值为{}(空集合)而不是抛出异常
|
||||
- @Autowired作用与构造函数的规则
|
||||
- 当required属性为其默认值true时,在bean类型中只有一个构造函数可以用@Autowired标注
|
||||
- 如果bean类型中有多个构造函数标注了@Autowired注解,那么那么他们都必须将required属性设置为false,并且所有标注了@Autowired属性的构造函数都会被视为依赖注入的候选构造函数
|
||||
- 如果有多个候选的构造函数,那么在IOC容器中可以满足的匹配bean最多的构造函数将会被选中
|
||||
- 如果没有候选函数被选中,那么其会采用默认构造函数,如无默认构造函数,则抛出异常
|
||||
- 如果bean有多个构造函数,并且所有构造函数都没有标明@Autowired注解,那么会采用默认构造函数,如果默认构造函数不存在,抛出异常
|
||||
- 如果bean类型只有一个构造函数,那么该构造函数会被用来进行依赖注入,即使该构造函数没有标注@Autowired注解
|
||||
- 除了使用@Autowired的required属性,还可以使用@Nullable注解来标注可为空
|
||||
```java
|
||||
@Component
|
||||
public class Shiromiya {
|
||||
@Autowired
|
||||
@Nullable
|
||||
private int num;
|
||||
}
|
||||
```
|
||||
- @Primary
|
||||
- @Autowired注解是通过类型注入,如果相同类型存在多个bean时,可以通过@Primary注解来表明一个primary bean
|
||||
```java
|
||||
@Configuration
|
||||
public class BeanConfiguration {
|
||||
@Bean
|
||||
@Primary
|
||||
public String name_1() {
|
||||
return "kazusa";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String name_2() {
|
||||
return "ogiso";
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 此时,若通过@Autowired注入String类型,“kazusa”将会是默认值
|
||||
*/
|
||||
```
|
||||
- @Qualifier
|
||||
- 可以通过@Qualifier来指定bean的name导入特定bean,并且可以为bean指定默认的qualifier
|
||||
```java
|
||||
@Component
|
||||
public class Shiromiya {
|
||||
@Autowired
|
||||
@Qualifier("name_2")
|
||||
private String n_2;
|
||||
|
||||
private String n_1;
|
||||
|
||||
@Autowired
|
||||
public Shiromiya(@Qualifier("name_1") String n) {
|
||||
this.n_1=n;
|
||||
}
|
||||
}
|
||||
```
|
||||
- bean对象的qualifier并不需要唯一,可以为不同的bean对象赋值相同的qualifier,并且在注入bean集合的时候根据qualifier过滤
|
||||
```java
|
||||
@Configuration
|
||||
@Qualifier("config")
|
||||
class BeanConfiguration {
|
||||
@Bean
|
||||
@Qualifier("name")
|
||||
public String name_1() {
|
||||
return "kazusa";
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("name")
|
||||
public String name_2() {
|
||||
return "ogiso";
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("not-name")
|
||||
public String not_name_1() {
|
||||
return "fuck";
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
public class Person {
|
||||
/* 此nameList属性会注入qualifier为name的所有bean
|
||||
* 在此处为"kazusa"和"ogiso"
|
||||
*/
|
||||
@Autowired
|
||||
@Qualifier("name")
|
||||
Map<String,String> nameList;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("config")
|
||||
BeanConfiguration conf;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"nameList=" + nameList +
|
||||
", conf=" + conf +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
```
|
||||
- 作为一种回退机制,当bean的qualifier未被定义时,bean的name属性将会被作为其qualifier,autowired时会根据@Qualifier注解中指定的值匹配具有相同name的bean对象
|
||||
- 若想根据bean的name进行匹配,无需@Qualifier注解,只需要将注入点的name(filed的变量名,标注为@Autowired函数的形参名)和bean的name进行比较,如果相同则匹配成功,否则匹配失败
|
||||
- @Autowired同样支持自身引用的注入,但是自身引用的注入只能作为一种fallback机制。如果当前IOC容器中存在其他的同类型对象,那么其他对象会被优先注入,对象自己并不会参与候选的对象注入。但是,如果IOC中并不存在其他同类型对象,那么自身对象将会被作为引用注入。
|
||||
- @Resource
|
||||
- @Resource标签类似于@Autowired标签,但是@Resource具有一个name属性用来匹配bean对象的name属性
|
||||
- @Resource标签首先会对具有相同name的bean对象,如果没有匹配到具有相同name的bean对象,才会fallback到类型匹配
|
||||
@@ -1,71 +1,71 @@
|
||||
# Spring Data Access
|
||||
- ## Spring事务
|
||||
- 本地事务和全局事务:
|
||||
- 全局事务:全局事务允许使用多个事务资源,应用服务器来对全局事务进行管理
|
||||
- 本地事务:本地事务无法管理多个事务资源
|
||||
- 本地事务和全局事务的优缺点
|
||||
- 全局事务的使用需要和服务器环境相绑定,降低了代码的重用性
|
||||
- 本地事务无法使用多个事务资源,无法通过JTA等框架来对多个事务资源进行管理,无法使用分布式事务
|
||||
- ## 声明式事务
|
||||
- Spring中声明式事务是通过AOP来实现的
|
||||
- 在声明式事务中,可以为方法级的粒度指定事务行为
|
||||
- 声明式事务的回滚规则:
|
||||
- 在Spring声明式事务中,可以为事务指定回滚规则,即指定针对哪些异常,事务会自动执行回滚操作
|
||||
- 在默认情况下,只有抛出unchecked异常(通常为RuntimeException)时,声明式事务才会进行回滚
|
||||
- 声明式事务的实现细节:
|
||||
- 声明式事务通过aop代理实现,并且事务的advice是通过xml元数据配置来驱动的
|
||||
- aop和事务元数据联合产生了一个aop代理对象,并且该代理对象通过使用TransactionInterceptor和TransactionManager来实现事务
|
||||
- @Transactional通常和线程绑定的事务一起工作,线程绑定的事务由PlatformTransactionManager管理。@Transactional会将事务暴露给当前执行线程中所有的dao操作
|
||||
- 声明式事务的回滚:
|
||||
- 在Spring事务中推荐让事务回滚的方式是在事务执行的方法中抛出一个异常
|
||||
- Spring事务在默认情况下只会针对unchecked异常(RuntimeException)进行回滚,对于Error,Spring事务也会执行回滚操作
|
||||
- checked异常并不会导致事务的回滚操作,可以注册rollback rule来指定对特定的异常(包括checked异常)进行回滚操作
|
||||
- rollback rule:
|
||||
- 回滚规则(rollback rule)通常用来指定当一个异常被抛出时,是否为该异常执行事务的回滚操作
|
||||
- 在@Transactional注解中,可以指定rollbackFor/noRollbackFor、rollbackForClassName/noRollbackForClassName来指定为那些异常类执行回滚操作
|
||||
> 当指定rollbackFor属性为checked异常时(如rollbackFor=FileNotFoundException.class),此时指定的异常不会覆盖其默认行为(为RuntimeException和Error异常执行回滚操作)。
|
||||
> 故而指定后其默认会为Error、RuntimeException、FileNotFoundException三类异常执行回滚操作
|
||||
|
||||
```java
|
||||
@Transactional(rollbackFor={MyException.class})
|
||||
public void myOperation() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- ## 基于注解的声明式事务
|
||||
- @Transactional既可以作用于类上,也可以作用于方法上。当作用于类上时,该声明类中所有的方法都会被声明是事务的,该类子类中所有的方法也都是事务的
|
||||
- @Transactional是可以继承的,被@Inherited元注解修饰。
|
||||
>@Inherited类是元注解,用来修饰注解类。如果一个注解类被@Inherited注解标识,那么在对class查询该注解类时,如果当前class没有声明该注解,将会在当前class的父类中查找该注解,依次递归。直到在父类中找到该注解或是到达继承结构的顶部(Object类)。@Inherited标注的注解仅能在类继承中有效,如注解被标注在接口上,那么将@Inherited标注的注解将不会被递归查询到。
|
||||
- 并且,class级别的@Transactional并不应用在从父类继承的方法上,即若一个类被@Transactional注解标注,并且该类从父类继承方法,那么该类从父类继承的方法并不会被看作是事务的,除非在该类中重新声明继承的方法。
|
||||
- 通过在Configuration类上注解@EnableTransactionManagement,配合@Transactional可以将一个bean对象声明是事务的
|
||||
- 当基于标准spring配置时,应该仅将@Transactional注解标注于public方法,当将@Transactional注解标注于非public方法时无效
|
||||
- 在Spring中,仅推荐将@Transactional注解应用于类上,不推荐将其应用在接口(接口方法)上。如果将其应用在接口上,那么该事务配置仅仅对基于接口的动态代理有效,对基于class的代理无效。
|
||||
- 当类级别和方法级别都设置了@Transactional注解时,方法级别的设置会优先被使用
|
||||
- ## @Transactional注解的配置
|
||||
- 事务的传播: 默认情况下,@Transactional的propagation属性是PROPAGATION_REQUIRED
|
||||
- 事务的隔离级别: 默认情况下,@Transactional的isolation属性是ISOLATION_DEFAULT,使用数据库默认的隔离级别
|
||||
- readOnly: 默认情况下,@Transactional的readOnly属性是false,默认事务是可读写的
|
||||
- timeout: 默认况下下,@Transactional的超时属性取决于底层的事务系统,如果底层事务系统不支持timeout,则timeout属性为none
|
||||
- rollbackFor: 默认情况下,@Transactional会针对unchecked异常和Error进行回滚操作
|
||||
- transactionManager: 默认情况下,@Transactional注解会使用项目中默认的事务管理器(即bean name为transactionManager的事务管理器)。可以为@Transactional注解指定value属性或是transactionManager属性来指定想要采用的事务管理器的bean name或是qualifier
|
||||
- ## Transaction Propagation
|
||||
- ### PROPAGATION.REQUIRED
|
||||
- 在Spring中,事务的传播行为默认是PROPAGATION_REQUIRED,默认情况下该选项会强制的要求一个物理事务
|
||||
- 如果当前作用域中不存在事务,那么会创建一个新的事务
|
||||
- 如果当前作用域的外层作用域已经存在事务,那么会加入到当前作用域的事务中去
|
||||
- 在Spring中,默认情况下,当嵌套事务加入到外层的事务中时,会忽略内层事务定义的隔离级别、timeout设置和读写标志等。
|
||||
> 如果想要对外层事务进行验证,可以手动将事务管理器的validateExistingTransaction属性设置为true。这样,当加入到一个隔离级别与内层事务完全不同的外层事务中时,该加入操作会被拒绝。在该设置下,如果read-write内层事务想要加入到外层的read-only事务中时,该加入操作也会被拒绝。
|
||||
- 在事务传播行为被设置为PROPAGATION_REQUIRED的情况下,会为每个被设置事务的方法创建一个逻辑的事务作用域。各个逻辑事务作用域之间都是相互独立的,在不同逻辑事务作用域之间都可以独立设置事务的rollback-only属性。但是,在PROPAGATION_REQUIRED的情况下,内层事务和外层事务都映射到同一个物理事务,内层事务加入到外层事务中,故而在内层逻辑事务中为物理事务设置rollback-only会切实影响到外层事务的提交。
|
||||
- 当事务传播行为被设置为PROPAGATION_REQUIRED时,如果内层事务设置了rollback-only标记,那么会导致外层物理事务的回滚。当外层事务尝试提交并失败回滚后,会抛出一个UnexceptedRollbackException异常,外层事务commit方法的调用者会接受到该UnexceptedRollbackException,代表内层发生了非预期的回滚操作
|
||||
- ### PROPAGATION.REQUIRES_NEW
|
||||
- 相对于PROPAGATION_REQUIRED,PROPAGATION.REQUIRES_NEW传播行为会一直使用独立的物理事务,而不会尝试区加入外部已经存在的物理事务。
|
||||
- 对于PROPAGATION_NEW,其内层事务和外层事务都可以独立的提交或回滚,内层事务的回滚并不会导致外层事务的回滚。
|
||||
- 将事务传播行为设置为PROPAGATION.REQUIRES_NEW时,内层事务可以独立定义自己的隔离级别、timeout值、read-only属性,而不必继承外部事务的这些属性。在PROPAGATION_REQUIRED中,内部事务自定义这些属性将会被忽略,内部事务加入外部事务后会采用外部事务的设置。
|
||||
- ### PROPAGATION.NESTED
|
||||
- 和PROPAGATION_REQUIRED类似,PROPAGATION_NESTED同样也只有一个物理事务。但是其支持多个savepoint(存档点),该物理事务可以回滚到特定的存档点而非必须回滚整个事务。
|
||||
- 由于PROPAGATION_NESTED对存档点的支持,故而在PROPAGATION_NESTED条件下,可以进行部分回滚。内层事务的回滚操作并不会造成外部事务的回滚,内层事务回滚后外层事务仍然能够继续执行和提交。
|
||||
> 由于PROPAGATION_NESTED需要JDBC savepoint(存档点)的支持,故而该设置仅仅对JDBC事务资源有效。
|
||||
|
||||
> 当事务被回滚之后,当前事务无法再被提交,故而:
|
||||
> 若在子事务中已经回滚(子事务传播行为为required),那么父事务的状态已经被回滚,即使父事务捕获子事务抛出的异常,那么在捕获异常之后执行的sql操作也不会被提交到数据库中,父事务状态处于已回滚,无法再次提交
|
||||
> ***但是,当子事务传播行为为nested时,子事务虽然和父事务共用一个事务,子事务回滚时只会回滚到子事务开启之前的存档点,父事务在捕获子事务抛出异常之后执行的sql语句仍然可以被提交***
|
||||
# Spring Data Access
|
||||
- ## Spring事务
|
||||
- 本地事务和全局事务:
|
||||
- 全局事务:全局事务允许使用多个事务资源,应用服务器来对全局事务进行管理
|
||||
- 本地事务:本地事务无法管理多个事务资源
|
||||
- 本地事务和全局事务的优缺点
|
||||
- 全局事务的使用需要和服务器环境相绑定,降低了代码的重用性
|
||||
- 本地事务无法使用多个事务资源,无法通过JTA等框架来对多个事务资源进行管理,无法使用分布式事务
|
||||
- ## 声明式事务
|
||||
- Spring中声明式事务是通过AOP来实现的
|
||||
- 在声明式事务中,可以为方法级的粒度指定事务行为
|
||||
- 声明式事务的回滚规则:
|
||||
- 在Spring声明式事务中,可以为事务指定回滚规则,即指定针对哪些异常,事务会自动执行回滚操作
|
||||
- 在默认情况下,只有抛出unchecked异常(通常为RuntimeException)时,声明式事务才会进行回滚
|
||||
- 声明式事务的实现细节:
|
||||
- 声明式事务通过aop代理实现,并且事务的advice是通过xml元数据配置来驱动的
|
||||
- aop和事务元数据联合产生了一个aop代理对象,并且该代理对象通过使用TransactionInterceptor和TransactionManager来实现事务
|
||||
- @Transactional通常和线程绑定的事务一起工作,线程绑定的事务由PlatformTransactionManager管理。@Transactional会将事务暴露给当前执行线程中所有的dao操作
|
||||
- 声明式事务的回滚:
|
||||
- 在Spring事务中推荐让事务回滚的方式是在事务执行的方法中抛出一个异常
|
||||
- Spring事务在默认情况下只会针对unchecked异常(RuntimeException)进行回滚,对于Error,Spring事务也会执行回滚操作
|
||||
- checked异常并不会导致事务的回滚操作,可以注册rollback rule来指定对特定的异常(包括checked异常)进行回滚操作
|
||||
- rollback rule:
|
||||
- 回滚规则(rollback rule)通常用来指定当一个异常被抛出时,是否为该异常执行事务的回滚操作
|
||||
- 在@Transactional注解中,可以指定rollbackFor/noRollbackFor、rollbackForClassName/noRollbackForClassName来指定为那些异常类执行回滚操作
|
||||
> 当指定rollbackFor属性为checked异常时(如rollbackFor=FileNotFoundException.class),此时指定的异常不会覆盖其默认行为(为RuntimeException和Error异常执行回滚操作)。
|
||||
> 故而指定后其默认会为Error、RuntimeException、FileNotFoundException三类异常执行回滚操作
|
||||
|
||||
```java
|
||||
@Transactional(rollbackFor={MyException.class})
|
||||
public void myOperation() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- ## 基于注解的声明式事务
|
||||
- @Transactional既可以作用于类上,也可以作用于方法上。当作用于类上时,该声明类中所有的方法都会被声明是事务的,该类子类中所有的方法也都是事务的
|
||||
- @Transactional是可以继承的,被@Inherited元注解修饰。
|
||||
>@Inherited类是元注解,用来修饰注解类。如果一个注解类被@Inherited注解标识,那么在对class查询该注解类时,如果当前class没有声明该注解,将会在当前class的父类中查找该注解,依次递归。直到在父类中找到该注解或是到达继承结构的顶部(Object类)。@Inherited标注的注解仅能在类继承中有效,如注解被标注在接口上,那么将@Inherited标注的注解将不会被递归查询到。
|
||||
- 并且,class级别的@Transactional并不应用在从父类继承的方法上,即若一个类被@Transactional注解标注,并且该类从父类继承方法,那么该类从父类继承的方法并不会被看作是事务的,除非在该类中重新声明继承的方法。
|
||||
- 通过在Configuration类上注解@EnableTransactionManagement,配合@Transactional可以将一个bean对象声明是事务的
|
||||
- 当基于标准spring配置时,应该仅将@Transactional注解标注于public方法,当将@Transactional注解标注于非public方法时无效
|
||||
- 在Spring中,仅推荐将@Transactional注解应用于类上,不推荐将其应用在接口(接口方法)上。如果将其应用在接口上,那么该事务配置仅仅对基于接口的动态代理有效,对基于class的代理无效。
|
||||
- 当类级别和方法级别都设置了@Transactional注解时,方法级别的设置会优先被使用
|
||||
- ## @Transactional注解的配置
|
||||
- 事务的传播: 默认情况下,@Transactional的propagation属性是PROPAGATION_REQUIRED
|
||||
- 事务的隔离级别: 默认情况下,@Transactional的isolation属性是ISOLATION_DEFAULT,使用数据库默认的隔离级别
|
||||
- readOnly: 默认情况下,@Transactional的readOnly属性是false,默认事务是可读写的
|
||||
- timeout: 默认况下下,@Transactional的超时属性取决于底层的事务系统,如果底层事务系统不支持timeout,则timeout属性为none
|
||||
- rollbackFor: 默认情况下,@Transactional会针对unchecked异常和Error进行回滚操作
|
||||
- transactionManager: 默认情况下,@Transactional注解会使用项目中默认的事务管理器(即bean name为transactionManager的事务管理器)。可以为@Transactional注解指定value属性或是transactionManager属性来指定想要采用的事务管理器的bean name或是qualifier
|
||||
- ## Transaction Propagation
|
||||
- ### PROPAGATION.REQUIRED
|
||||
- 在Spring中,事务的传播行为默认是PROPAGATION_REQUIRED,默认情况下该选项会强制的要求一个物理事务
|
||||
- 如果当前作用域中不存在事务,那么会创建一个新的事务
|
||||
- 如果当前作用域的外层作用域已经存在事务,那么会加入到当前作用域的事务中去
|
||||
- 在Spring中,默认情况下,当嵌套事务加入到外层的事务中时,会忽略内层事务定义的隔离级别、timeout设置和读写标志等。
|
||||
> 如果想要对外层事务进行验证,可以手动将事务管理器的validateExistingTransaction属性设置为true。这样,当加入到一个隔离级别与内层事务完全不同的外层事务中时,该加入操作会被拒绝。在该设置下,如果read-write内层事务想要加入到外层的read-only事务中时,该加入操作也会被拒绝。
|
||||
- 在事务传播行为被设置为PROPAGATION_REQUIRED的情况下,会为每个被设置事务的方法创建一个逻辑的事务作用域。各个逻辑事务作用域之间都是相互独立的,在不同逻辑事务作用域之间都可以独立设置事务的rollback-only属性。但是,在PROPAGATION_REQUIRED的情况下,内层事务和外层事务都映射到同一个物理事务,内层事务加入到外层事务中,故而在内层逻辑事务中为物理事务设置rollback-only会切实影响到外层事务的提交。
|
||||
- 当事务传播行为被设置为PROPAGATION_REQUIRED时,如果内层事务设置了rollback-only标记,那么会导致外层物理事务的回滚。当外层事务尝试提交并失败回滚后,会抛出一个UnexceptedRollbackException异常,外层事务commit方法的调用者会接受到该UnexceptedRollbackException,代表内层发生了非预期的回滚操作
|
||||
- ### PROPAGATION.REQUIRES_NEW
|
||||
- 相对于PROPAGATION_REQUIRED,PROPAGATION.REQUIRES_NEW传播行为会一直使用独立的物理事务,而不会尝试区加入外部已经存在的物理事务。
|
||||
- 对于PROPAGATION_NEW,其内层事务和外层事务都可以独立的提交或回滚,内层事务的回滚并不会导致外层事务的回滚。
|
||||
- 将事务传播行为设置为PROPAGATION.REQUIRES_NEW时,内层事务可以独立定义自己的隔离级别、timeout值、read-only属性,而不必继承外部事务的这些属性。在PROPAGATION_REQUIRED中,内部事务自定义这些属性将会被忽略,内部事务加入外部事务后会采用外部事务的设置。
|
||||
- ### PROPAGATION.NESTED
|
||||
- 和PROPAGATION_REQUIRED类似,PROPAGATION_NESTED同样也只有一个物理事务。但是其支持多个savepoint(存档点),该物理事务可以回滚到特定的存档点而非必须回滚整个事务。
|
||||
- 由于PROPAGATION_NESTED对存档点的支持,故而在PROPAGATION_NESTED条件下,可以进行部分回滚。内层事务的回滚操作并不会造成外部事务的回滚,内层事务回滚后外层事务仍然能够继续执行和提交。
|
||||
> 由于PROPAGATION_NESTED需要JDBC savepoint(存档点)的支持,故而该设置仅仅对JDBC事务资源有效。
|
||||
|
||||
> 当事务被回滚之后,当前事务无法再被提交,故而:
|
||||
> 若在子事务中已经回滚(子事务传播行为为required),那么父事务的状态已经被回滚,即使父事务捕获子事务抛出的异常,那么在捕获异常之后执行的sql操作也不会被提交到数据库中,父事务状态处于已回滚,无法再次提交
|
||||
> ***但是,当子事务传播行为为nested时,子事务虽然和父事务共用一个事务,子事务回滚时只会回滚到子事务开启之前的存档点,父事务在捕获子事务抛出异常之后执行的sql语句仍然可以被提交***
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user