基于注解实现Spring AOP使用

AOP:

AOP(Aspect Oriented Programming),即面向切面编程,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。AOP是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP利用"横切"技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

AOP核心概念:

  1. 连接点(joinpoint):被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  2. 切入点(pointcut):对连接点进行拦截的定义
  3. 通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
  4. 引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
  5. 目标对象(target):代理的目标对象
  6. 织入(weave):将切面应用到目标对象并导致代理对象创建的过程
  7. 代理(proxy):一个类被AOP织入增强后,就产生一个结果代理类
  8. 切面(aspect):切入点和通知的结合

关于 AOP Proxy:

一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象。Spring AOP 默认使用标准的 JDK 动态代理(dynamic proxy)技术来实现 AOP 代理, 通过它,我们可以为任意的接口实现代理。如果要为一个类实现代理,可以使用CGLIB代理。 当一个业务逻辑对象没有实现接口时, 那么Spring AOP 就默认使用 CGLIB 来作为 AOP 代理了。即如果我们需要为一个方法织入 advice,,但是这个方法不是一个接口所提供的方法,,则此时 Spring AOP 会使用 CGLIB 来实现动态代理.。因此, Spring AOP 建议基于接口编程, 对接口进行 AOP 而不是类。

Spring注解形式实现AOP:

定义切面类:@aspect标注的注解为切面类。

定义通知类型:注解用于切面类中的方法上。

  •  前置通知(@Before):在目标方法运行之前运行
  • 后置通知(@After):在目标方法运行结束之后运行(无论方法正常结束还是异常结束)
  • 返回通知(@AfterReturning):在目标方法正常返回之后运行
  • 异常通知(@AfterThrowing):在目标方法出现异常以后运行
  • 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())

定义切入点表达式: execution(   方法修饰符  方法返回值  方法所属类 匹配方法名 (  方法中的形参表 )  方法申明抛出的异常  )

关于切入点表达式详细信息可以参考文章:https://blog.csdn.net/zhoushimiao1990/article/details/89852516

开启AOP功能:@EnableAspectJAutoProxy,在注解类中添加该注解。

示例代码:

引入SpringAOP需要的jar包

  <properties>
  	 <spring.version>4.1.3.RELEASE</spring.version>
  	 <aspectj.version>1.6.11</aspectj.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
 
<!--测试包,自动生成-->
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <!--spring aop包-->
      <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
	</dependency>
	<!--spring上下文包,在加载spring配置文件时用到-->
	  <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
	</dependency>

    <!--使用AspectJ方式注解需要相应的包-->
      <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
 

定义切入点:

@Component
public class SpringAOPImpl implements SpringAOP {
	public String testAOP() {
		System.out.println("testAOP Method");
		return "AOP return";
	}
}

定义切面类和通知类型:


@Aspect
public class SpringAOPAspect {
	
	@Pointcut("execution(public String cn.zsm.service.impl.SpringAOPImpl.*())")
	public void pointCut(){}; //提取公共切入点表达式信息
		
	@Before(value = "pointCut()") // 设置前置通知
	public void beforeAspect(JoinPoint joinPoint){
		System.out.println(joinPoint.getSignature().getName()+"=====beforeAspect");
	}
	
	@After(value = "pointCut()") // 设置后置通知
	public void afterAspect(JoinPoint joinPoint){
		System.out.println(joinPoint.getSignature().getName()+"=====afterAspect");
	}

	@AfterReturning(value = "pointCut()",returning = "result")  //返回值通知
	public void afterReturningAspect(JoinPoint joinPoint,Object result){
		System.out.println(joinPoint.getClass().getName()+"=====afterReturningAspect   + return result = " + result);
	}
	
	@AfterThrowing(value = "pointCut()",throwing="exception")  //异常通知
	public void afterThrowingAspect(JoinPoint joinPoint,Exception exception){
		System.out.println(joinPoint.getClass().getName()+"=====afterThrowingAspect and Exception = "+ exception);
	}

}

定义配置类,并注入切入点类和切面类:

@Configuration
@EnableAspectJAutoProxy  //开启AOP功能
public class SpringAOPConfig {
	@Bean
	public SpringAOPAspect springAOPAspect(){
		return new SpringAOPAspect();
	}
		
	@Bean("springAOPImpl")
	public SpringAOPImpl springAOPImpl(){
		return new SpringAOPImpl();
	}
}

测试:

	@Test
	public void aop(){
		AnnotationConfigApplicationContext application = new AnnotationConfigApplicationContext(SpringAOPConfig.class);  
          /*
           这里强转时要使用接口,不要用实现类。
            动态代理分为两种:1、针对接口的动态代理;2、针对普通类的动态代理。
           java中的jdk动态代理是针对接口的动态代理,而cglib是针对普通类的动态代理,
           javaEE的依赖包中有jdk动态代理包,Spring的jar包中也包含了cglib相关jar包,因此即可以对接口也可以对普通类进行动态代理
           @EnableAspectJAutoProxy 中的proxyTargetClass="true/false"属性,决定是基于接口的还是基于类的代理被创建。
         */
		SpringAOP bean = (SpringAOP)application.getBean("springAOPImpl"); 
		bean.testAOP();
	}

输出结果:

可以看到切面类中定义的通知类型缺少@Around环绕通知,因为around advice 比较特别, 可以在一个方法内完成前置、后置、异常(@AfterThrowing)等通知所实现的功能。下面重点看一下@Around注解:
 

	@Around(value = "pointCut()")
	public void aroundAspect(ProceedingJoinPoint  joinPoint) throws Throwable{
		try {
			System.out.println(joinPoint.getSignature().getName()+"=====as before aspect");//相当于前置通知     
			Object retVal = joinPoint.proceed();//执行切入点方法        
			System.out.println(joinPoint.getSignature().getName()+"=====as after aspect");//相当于后置通知
		} catch (Exception e) {
			System.out.println(joinPoint.getSignature().getName()+"=====as aferThrowing aspect");//相当于异常通知
		}
	}

测试运行结果:

注意:切面类和切入点类必须都注册进Spring容器,AOP才能生效。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值