Spring4详解系列(四)面向切面的Spring

1、什么是面向切面编程

AOP(Aspect-Oriented Programming), 即面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角,也可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系,而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。使用“横切”技术。

AOP把软件系统分为两个部分:核心关注点(核心业务逻辑横切关注点(横向的通用逻辑。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。

2、Spring对AOP的支持

Spring提供了四种类型的AOP支持

  1. 经典的基于代理的AOP
  2. @AspectJ注解驱动的切面
  3. 纯POJO切面
  4. 注入式AspectJ切面

前三种都是Spring AOP现实的变体、Spring AOP构建在动态代理的基础之上、因此,Spring对AOP的支持局限于方法拦截

3、AOP的相关概念

1.通知(Advice) 通知定义了在切入点代码执行时间点附近需要做的工作。

Spring支持五种类型的通知:

  • @Before(前置通知) org.apringframework.aop.MethodBeforeAdvice
  • @AfterReturning(后置通知) org.springframework.aop.AfterReturningAdvice
  • @AfterThrowing(异常抛出) org.springframework.aop.ThrowsAdvice
  • @Arround(环绕通知) org.aopaliance.intercept.MethodInterceptor
  • @Introduction(引入) org.springframework.aop.IntroductionInterceptor

2.连接点(Joinpoint) 程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法调用时、异常抛出时、方法返回后等等。

3.切入点(Pointcut) 通知定义了切面要发生的“故事”,连接点定义了“故事”发生的时机,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定。

4.切面(Aspect) 通知、连接点、切入点共同组成了切面:时间、地点和要发生的“故事”。

5.引入(Introduction) 引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。

6.目标(Target) 即被通知的对象,如果没有AOP,那么通知的逻辑就要写在目标对象中,有了AOP之后它可以只关注自己要做的事,解耦合!

7.代理(proxy) 应用通知的对象,详细内容参见设计模式里面的动态代理模式。

8.织入(Weaving) 把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

  1. 编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器;
  2. 类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码;
  3. 运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术。

4、相关知识点

AOP 有哪些实现方式?

实现 AOP 的技术,主要分为两大类: 静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强; 编译时编织(特殊编译器实现) 类加载时编织(特殊的类加载器实现)。 动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。 JDK 动态代理 CGLIB

Spring AOP and AspectJ AOP 有什么区别?

Spring AOP 基于动态代理方式实现;AspectJ 基于静态代理方式实现。 Spring AOP 仅支持方法级别的 PointCut;提供了完全的 AOP 支持,它还支持属性级别的 PointCut。

如何理解 Spring 中的代理?

将 Advice 应用于目标对象后创建的对象称为代理。在客户端对象的情况下,目标对象和代理对象是相同的。

指出在 spring aop 中 concern 和 cross-cutting concern 的不同之处。

concern 是我们想要在应用程序的特定模块中定义的行为。它可以定义为我们想要实现的功能。cross-cutting concern 是一个适用于整个应用的行为,这会影响整个应用程序。例如,日志记录,安全性和数据传输是应用程序几乎每个模块都需要关注的问题,因此它们是跨领域的问题。

5、使用注释创建切面

@Aspect
@Component
public class Spring4AopConfig {
	
	/**
	 * execution 用于匹配方法执行的连接点;
     * within 用于匹配指定类型内的方法执行;
	 */
//	@Pointcut("within(com.spr.demo.service..*)")
//	public void commonServiceLogger(){}
	@Pointcut(value = "execution(** com.spr.demo.controller.AopCpntroller.*(..))")
	public void commonExecution(){}
	
	@Before("commonExecution()")
	public void startMethod(JoinPoint point) {
		System.out.println(point.getSignature().getName() + "前置通知...");
	}
	
	@AfterReturning(pointcut = "commonExecution()",returning="result")
	public void afterMethod(JoinPoint point,Object result) {
		System.out.println(point.getSignature().getName() + "后置通知:" + result);
	}
	
	@AfterThrowing(pointcut = "commonExecution()",throwing = "e")
	public void throwMethod(JoinPoint point, Throwable e) {
		System.out.println(point.getSignature().getName() + "异常通知:" + e);
	}
	
	/**
	 * joinpoint和proceedingjoinpoint区别
	 * joinpoint:
	   public interface JoinPoint {  
		   String toString();         //连接点所在位置的相关信息  
		   String toShortString();     //连接点所在位置的简短相关信息  
		   String toLongString();     //连接点所在位置的全部相关信息  
		   Object getThis();         //返回AOP代理对象  
		   Object getTarget();       //返回目标对象  
		   Object[] getArgs();       //返回被通知方法参数列表  
		   Signature getSignature();  //返回当前连接点签名  
		   SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
		   String getKind();        //连接点类型  
		   StaticPart getStaticPart(); //返回连接点静态部分  
  	   }  
	 * proceedingjoinpoint:
	 * public interface ProceedingJoinPoint extends JoinPoint {  
	       public Object proceed() throws Throwable;  
	       public Object proceed(Object[] args) throws Throwable;  
 	   }
 	   
 	   JoinPoint.StaticPart:提供访问连接点的静态部分,如被通知方法签名、连接点类型等:
	   public interface StaticPart {  
		   Signature getSignature();    //返回当前连接点签名  
		   String getKind();          //连接点类型  
		   int getId();               //唯一标识  
		   String toString();         //连接点所在位置的相关信息  
		   String toShortString();     //连接点所在位置的简短相关信息  
		   String toLongString();     //连接点所在位置的全部相关信息  
	   } 
	   环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,
	   这也是环绕通知和前置、后置通知方法的一个最大区别。
     Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。
     proceed很重要,这个是aop代理链执行的方法。
	 */
	
	@Around("commonExecution()")
//	public void aroundMethod(ProceedingJoinPoint jp,Throwable e){
	public void aroundMethod(ProceedingJoinPoint point){
		try {
			System.out.println("< 环绕通知开始执行 >...");
			Object proceed = point.proceed();
			System.out.println("< 环绕通知执行完成 > : " + 
					point.getSignature().getName() + ","+
					point.getSignature().getDeclaringTypeName()+","+
					proceed);
		} catch (Throwable e) {
			System.err.println("< 环绕通知异常 > : " + e.getMessage());
		}
	}

}

如果不想要使用@Component注解加载切面,也可以通过组件加载:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConfigCenter {
	@Bean
	public Spring4AopConfig spring4AopConfig() {
		return new Spring4AopConfig();
	}
}

6、通过注解引入新功能

使用@DeclareParents注解,将接口引入到Spring Bean中。

  @DeclareParents注解由三部分组成:

    1)value属性指定了哪种类型的bean要引入该接口。加号(+)表示是该类型的所有子类型,而不是该类型本身。

    2)defaultImpl属性指定了为引入功能提供实现的类。

    3)@DeclareParents注解所标注的静态属性指明了要引入的接口。

EncoreableIntroducer是一个切面,同时声明它是一个bean。但是,它与我们 之前所创建的切面不同,它并没有提供前置、后置或环绕通知,而是 通过@DeclareParents注解,将Encoreable接口引入 到Performance bean中:

// 创建一个接口
public interface Encoreable {void performEncore();}

// 实现接口
public class DefaultEncoreable implements Encoreable {
    @Override
    public void performEncore() {
         System.out.println("接口实现");
    }
}

//新建切面
@Aspect
@Component
public class EncoreableIntroducer {
    @DeclareParents(value = "concert2.Performance+", defaultImpl = DefaultEncoreable.class)
    public static Encoreable encoreable;
}

7、注入AspectJ切面

虽然Spring AOP能够满足许多应用的切面需求,但是与AspectJ相比,Spring AOP 是一个功能比较弱的AOP解决方案。AspectJ提供了Spring AOP所不能支持的许多类型的切点。

//定义切面
public aspect CriticAspect {
    public CriticAspect(){}
 
    //定义切点
    pointcut performance():execution(* perform(..));
 
    afterReturning : performance(){
        System.out.println(criticismEngine.getCriticism());
    }
 
    private CriticismEngine criticismEngine;

    // 注入criticismEngine
    public void setCriticismEngine(CriticismEngine criticismEngine){
        this.criticismEngine = criticismEngine;
    }
}




public class CriticismEngineImpl extends CriticismEngine {
 
    public CriticismEngineImpl(){}
    private String[] criticismPool;
 
    @Override
    public String getCriticism(){
        int i = (int)(Math.random() * criticismPool.length);
        return criticismPool[i];
    }
    
    public void setCriticismPool(String[] criticismPool){
        this.criticismPool = criticismPool;
    }
}

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值