Software_programming_Java_framework_Spring

2019-10-09



Spring

2019-10-09

 reference 《Spring in action》

Spring Bean Management

 

Spring Expression Language, SpEL

  • 使用 bean 的 ID 来引用 bean,
  • 调用方法和访问对象的属性
  • 对值进行算数,关系和逻辑运算
  • 正则表达式匹配
  • 集合操作

Spring MVC 应用中使用 Thymeleaf 模板作为视图的话,这些模板可用SpEL 表达式引用模型数据。

#{ ... }

#{T(System).currentTimeMillis() }  T() 表达式会将 java.lang.System 视为 Java 中对应的类型,

T( java.lang.Math) , 结果会是一个 Class 对象,需要的话可以装配到一个Class类型的 bean 属性中。

将 PI 值装配到 bean属性中

T(java.lang.Math).PI

T() 能够访问目标类型的静态方法和常量, 

#{beanIDproperty.attribute}  SpEL 表达式引用 bean的属性,该表达式会计算 beanIDproperty 的 bean的 attribute 的属性

#{systemProperties['disc.title'] }  通过 systemProperties 对象引用系统属性。

 

1. 通过组件扫描创建bean的话,在注入属性和构造器参数时,可以使用 @Value注解,类似与属性占位符,但我们使用 SpEL。

 

 

2. XML 配置中, 将 SpEL 传入 <property> 或 <constructor-arg> 的 value 属性中,或将其作为 p- 命名红箭或 c- 命名空间条目的值。

SpEL 可以调用 bean 上的方法,以及相应的 Java 方法,

为避免NullPointerException, 可以使用类型安全的运算符:

#{ artistSelector.selectArtist()?.toUpperCase() }

" ?. " 符号,能够在访问它右边的内容前,确保对应的元素不是 null, 若返回为null, 则不会调用 toUpperCase() 方法。表达式的返回值会是 null.

 

SpEL 与集合和数组相关操作。

按照索引获取元素  []

查询运算符  ( .?[ ] ) 对集合进行过滤,得到集合的一个子集。

. ^[ ]  和 .$ [ ] 用在集合中查询第一个匹配项和最后一个 匹配项

.! [ ] 投影运算符, 从集合的每个成员中选择特定的属性放到另外的一个集合中。

保持 SpEL 简洁, 毕竟只是 String 类型的值。 测试复杂性。

 ===============================================================================================================

2019-10-10

AoP

编译期注入

运行时 代理模式(Spring ) ,只能在方法上标记切点,无法在 构造器和属性字段上

Spring 会是延迟实例化 bean.

 

注解其表达式:带限定范围

 

 

Spring 使用 AspectJ注解来声明通知方法

anotation advise
@After 在目标方法返回或抛出异常后调用
@AfterReturning 在目标方法返回后调用
@AfterThrowing 在目标方法抛出异常后调用
@Around 将目标方法封装起来
@Before 在目标方法调用之前执行

 

 

实践:

自定义切面

package framework.Spring.AOP;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class Audience {

    @Before("execution(**concert.Performance.perofrm(..))")
    public void silenceCellPhones(){
        System.out.println("Silencing cell phones.");
    }

    @Before("execution(**concert.Performance.perform(..))")
    public void takeSeats(){
        System.out.println("Taking seats.");
    }

    @AfterReturning("execution(**concert.Performance.perform(..))")
    public void applause(){
        System.out.println("CLAP CLAP CLAP !");
    }

    @AfterThrowing("execution(**concert.Performance.perform(..))")
    public void demandRefund(){
        System.out.println("Demanding a refund.");
    }
}

 

切点表达式 重复了四次 (”execution(**concert.Performance.perform(..)"))

@Pointercut 注解 在一个 @AspectJ 切面内定义可重用的切点。

 

 

note : performancePct() 方法的实际内容并不重要,应该是空的,只是一个标识,供@Pointcut 注解依附

除了注解和没有实际操作的 performancePct() ,Audience 类依然是一个 POJO,能够像使用其他的Java类那样

调用它的方法,它的方法也能够独立地进行单元测试,只是通过注解表明作为切面使用。

它可以装配为 Spring 中的 bean:

@Bean

public Audience auditence(){

  return new Audience();

}

 

使用 JavaConfig 的时候,在配置类的类级别上通过使用 EnableAspectJ-AutoProxy 注解启用自动代理功能:

 

 AspectJ 自动代理会为使用@Aspect注解的 bean 创建一个代理,这个代理会围绕着所有该切面的切点所匹配的

bean, 这种情况下,会为 Concertbean 创建一个代理,Audience类中的通知方法将会在 perform() 调用前后执行。

本质上是依然是 Spring 基于代理的切面。

环绕通知 实际上就是在 一个通知方法中同时编写前置通知和后置通知。

 1 package framework.Spring.AOP;
 2 
 3 import org.aspectj.lang.ProceedingJoinPoint;
 4 import org.aspectj.lang.annotation.*;
 5 
 6 @Aspect
 7 public class Audience {
 8 
 9     @Pointcut("execution(**concert.Performance.perform(..))")
10     public void performance(){}
11 
12     @Around("performance()")
13     public void watchPerformance(ProceedingJoinPoint jp){
14         try{
15             System.out.println("Silence cell phones.");
16             System.out.println("Taking seats.");
17             jp.proceed();
18             System.out.println("CLAP CLAP CLAP !");
19         }catch (Throwable e){
20             System.out.println("Demanding a refound");
21         }
22     }
23 }

注意 ProceedingJoinPoint 参数, 该对象是必须的,要在通知中通过它来调用被通知的方法。通知方法中可以

做任何时请,当要将控制权交给被通知的方法时,需要调用 ProceedingJoinPoint的 Proceed() 方法。

注意别忘记调用 proceed() 方法。不调用的后果是,阻塞对被通知方法的调用。更多情况下时希望在某个点上执行被通知的方法。

可以不调用 proceed() 方法,从而阻塞对被通知方法的访问,与之类似,可以在通知中对它进行多次调用,

这样做的一个场景就是实现重试逻辑,在被通知方法失败后,进行重复尝试。

 

 

 

 

 1 package framework.Spring.AOP.concert;
 2 
 3 import org.aspectj.lang.annotation.Aspect;
 4 import org.aspectj.lang.annotation.Before;
 5 import org.aspectj.lang.annotation.Pointcut;
 6 
 7 import java.util.HashMap;
 8 import java.util.Map;
 9 
10 @Aspect
11 public class TrackCounter {
12 
13     private Map<Integer,Integer> trackCounts =
14             new HashMap<>();
15 
16     @Pointcut(
17             "execution(* framework.Spring.IoC.springActiveConfig.BlankDisc.playTrack(int))" +
18                     "&& args(trackNumber))")
19 
20     public void trackPlayed(int trackNumber){}
21 
22     @Before("trackPlayed(trackNumber)")
23     public void countTrack(int trackNumber){
24         int currentCount = getPlayCount(trackNumber);
25         trackCounts.put(trackNumber, currentCount + 1);
26     }
27 
28     private int getPlayCount(int trackNumber) {
29         return trackCounts.containsKey(trackNumber)
30                 ? trackCounts.get(trackNumber) : 0;
31     }
32 }

表明传递给 playTrack() 方法的 int 类型参数也会传递到通知中去。参数的名称 trackNumber 也与切点方法签名中的参数相匹配。

这个参数会传递到 通知方法中去, 此例子中通过 @Before 注解和命名切点 trackPlayed(trackNumber) 定义。

切点定义中的参数与切点方法中的参数名称时一样的。这样就完成了从命名切点到通知方法的参数转移。

可在 Spring 配置中将 BlankDisc 和 TrackCounter 定义为 bean, 并启用 AspectJ 自动代理。

 ===========================================================================

为现有的对象添加功能, Ruby 和 Groovy 有开放类的理念,不用直接修改对象或类的定义就能够为对象或类增加新的方法。

但Java 不是动态语言,一旦类编译完成了,就很难再为该类添加新的功能。

但 AOP  的 引入, 切面可为 Spring bean 添加新的方法。

 

 

注意:当引入接口的方法被调用时,代理会把此调用委托给实现了新接口的某个其他对象。

实际上,一个bean的实现被分拆到了多个类中。

 

 

 

使用 AOP 的引入功能,可以不必在设计上妥协或者侵入性地改变现有的实现。

创建一个新的切面。

 

 和其他切面一样,需要在 Spring应用中将 EncoreableIntroducer 声明为一个bean

Spring 的自动代理机制会获取到它的声明,当Spring 发现了一个bean 使用@Aspect 注解时,Spring就会创建一个代理,然后将调用委托给被代理的bean或被引入的实现。这取决于调用的方法属于被代理的bean 还是属于被引入的接口。

注意:面向注解的切面声明有一个明显的劣势: 必须能够为通知类添加注解。意为 要有源码。

若没有源码,或者不想将 AspectJ 注解放到你的代码之中,Spring 为切面提供了 在 Spring XML 配置文件中声明切面。

构造器切点非常方便,不像某些其他面向对象语言中的构造器,Java构造器不同于其他的正常方法。使得Spring基于代理的 AOP 无法把通知应用于对象的创建过程。

 

AspectJ切面与 Spring 是相互独立的。 在应用 Aspect 切面时几乎不会涉及到 Spring。

精心设计且有意义的切面很可能依赖其他类来完成它们的工作。

如果在执行通知时,切面依赖于一个或多个类,可以在切面内部实例化这些协作的对象,但更好的方式是,

借助 Spring 依赖注入把 bean 装配进 AspectJ 切面中。

 

posted @ 2019-10-09 16:47  君子之行  阅读(12)  评论(0)    收藏  举报