概述

在上一篇,我们了解了通过实现接口和XML配置的方式来实现AOP,在实现注解方式AOP之前,先了解一下AspectJ。AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译时实现代码的注入。Spring通过集成ApsectJ实现了以注解方式定义通知类,大大减少了配置文件的工作量。本文主要讲解通过注解方式和Schema方式实现AOP编程,仅供学习分享使用,如有不足之处,还请指正。

实现AOP的三种方式:

  1. 通过实现相应接口,并配置Xml进行实现,可以参考上一篇。
  2. 通过注解方式实现。
  3. 通过Schema的方式实现。

通过注解方式实现AOP

@Aspect 放在类的上面,表示这个类在Spring容器中是一个切点注入类。

@Component("logAnnotation") 表示此类是一个Bean,在Spring Ioc容器里面进行注入。

步骤如下:

1. 定义一个类,并在类上面增加@Aspect注解,表名此类为切面通知类。

如下所示:

 package com.hex.second;

 import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; /**
* @Aspect:声明类为一个通知类
* @Component("logAnnotation"):通过注解方法生成一个Bean,但是需要配置注解的支持
* 通过注解的方式声明通知
* @author Administrator
*
*/
@Component("logAnnotation")
@Aspect
public class LogAspectAnnotation { }

2. 前置通知函数

@Before("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))") 表示前置通知,用两个参数,默认为value值,指示切入点,告诉Spring在哪些地方进行注入。

JoinPoint 可以获取切入点的所有内容,包括目标对象,函数名称,参数,返回值等等。如下所示:

 /**
* 前置通知,@Before的参数为目标通知类的表达式
* JoinPoint 用来获取目标函数的参数及对象等信息
*/
@Before("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))")
public void MyBefore(JoinPoint jp){
System.out.println("我是注解方式的前置通知");
System.out.println("method="+jp.getSignature().getName()+",args数量="+jp.getArgs().length+",target="+jp.getTarget());
}

3. 后置通知

@AfterReturning(pointcut = "execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",returning="returningValue") 表示后置通知,其中value和poingcut都表示切入点,功能一样。returning表示定义目标函数的返回值。如下所示:

 /**
* 功能:后置通知
* 注解形式实现AOP通知时,参数不能随便写,否则和目标函数对应不上,会报错
* @param jp :切入点目标对象
* @param returningValue 返回值
*/
@AfterReturning(pointcut = "execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",returning="returningValue")
public void MyAfterReturning(JoinPoint jp,Object returningValue){
System.out.println("我是注解方式的后置通知");
System.out.println("返回值是:"+returningValue);
}

4. 异常通知

@AfterThrowing(pointcut="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",throwing="e") 表示异常通知,其中trowing表示将抛出异常绑定到参数中。当切入函数抛出异常时将会触发,如下所示:

 /**
* 异常通知
*/
@AfterThrowing(pointcut="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",throwing="e")
public void MyAfterThrow(JoinPoint jp,Throwable e){
System.out.println("我是注解方式的异常通知");
}

5. 环绕通知

@Around(value="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))") 环绕通知功能最全面,可以实现其他几种,其中参数使用ProceedingJoinPoint,是JoinPoint的子类。

 /**
* 环绕通知
* @param jp 才用的是JoinPoint的子类
*/
@Around(value="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))")
public void MyAround(ProceedingJoinPoint jp){
Object obj = null;
try { // 前置通知
System.out.println("注解环绕实现前置通知。。。");
System.out.println("环绕通知:target="+jp.getTarget()+",method="+jp.getSignature().getName()+",args="+jp.getArgs().length);
// 控制目标方法的执行 obj表示目标方法的返回值,表示执行addStudent(student)方法
//此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知
obj = jp.proceed();
// 后置通知
System.out.println("注解环绕实现后置通知。。。");
} catch (Throwable e) {
// 异常通知
System.out.println("注解环绕实现异常通知。。。");
}finally{
//最终通知
System.out.println("注解环绕实现最终通知。。。");
}
}

6. 最终通知

@After("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))") 表示最终通知,不管是否抛出异常,都会得到执行,类似于finally。如下所示:

 /**
* 最终通知,@After的参数为目标通知类的表达式
* JoinPoint 用来获取目标函数的参数及对象等信息
*/
@After("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))")
public void MyAfter(JoinPoint jp){
System.out.println("我是注解方式最终通知");
System.out.println("method="+jp.getSignature().getName()+",args数量="+jp.getArgs().length+",target="+jp.getTarget());
}

7. 除了上述注解之外,还需要在Sping容器中,配置对注解的支持和AOP的自动扫描。

如下所示:

 <?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 服务类 -->
<bean id="studentService" class="com.hex.second.StudentServiceImpl"> </bean>
<!-- 将addStudent和通知进行关联 -->
<!-- 配置对注解方式AOP的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置对注解的扫描 -->
<context:component-scan base-package="com.hex.second"></context:component-scan>
</beans>

通过Schema方式实现AOP

通过Schema方式实现步骤如下:

1. 定义一个普通的类,分别实现各种功能的通知,参数和注解方式的一致。

如下所示:

 package com.hex.second;

 import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; /**
* 通过Schema配置的方式实现通知
* @author Administrator
*
*/
public class LogAspectSchema {
/**
* 前置通知,@Before的参数为目标通知类的表达式
* JoinPoint 用来获取目标函数的参数及对象等信息
*/
public void MyBefore(JoinPoint jp){
System.out.println("我是Schema方式的前置通知");
System.out.println("method="+jp.getSignature().getName()+",args数量="+jp.getArgs().length+",target="+jp.getTarget());
} /**
* 功能:后置通知
* Schema形式实现AOP通知时,参数不能随便写,否则和目标函数对应不上,会报错
* @param jp :切入点目标对象
* @param returningValue 返回值
*/
public void MyAfterReturning(JoinPoint jp,Object returningValue){
System.out.println("我是Schema方式的后置通知");
System.out.println("返回值是:"+returningValue);
} /**
* 异常通知
*/
public void MyAfterThrow(JoinPoint jp ,Throwable ex){
System.out.println("我是Schema方式的异常通知");
System.out.println("ex:"+ex.getMessage());
} /**
* 环绕通知
* @param jp 才用的是JoinPoint的子类
*/
public void MyAround(ProceedingJoinPoint jp){
Object obj = null;
try { // 前置通知
System.out.println("Schema环绕实现前置通知。。。");
System.out.println("Schema环绕通知:target="+jp.getTarget()+",method="+jp.getThis()+",args="+jp.getArgs().length);
// 控制目标方法的执行 obj表示目标方法的返回值,表示执行addStudent(student)方法
//此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知
obj = jp.proceed();
// 后置通知
System.out.println("Schema环绕实现后置通知。。。");
} catch (Throwable e) {
// 异常通知
System.out.println("Schema环绕实现异常通知。。。");
}finally{
//最终通知
System.out.println("Schema环绕实现最终通知。。。");
}
} /**
* 最终通知
* @param jp
*/
public void MyAfter(JoinPoint jp){
System.out.println("我是Schema方式的最终通知");
}
}

2. 在Spring容器中配置

首先将通知类注入到Spring IOC容器中,然后配置<aop:config>将业务类和切面类关联起来。如下所示:

 <?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 服务类 -->
<bean id="studentService" class="com.hex.second.StudentServiceImpl"> </bean>
<bean id="logSchema" class="com.hex.second.LogAspectSchema"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(public void com.hex.second.StudentServiceImpl.deleteStudent(int)) or execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))" id="pc"/>
<aop:aspect ref="logSchema">
<!-- 通过Schema实现的通知 -->
<aop:before method="MyBefore" pointcut-ref="pc"/>
<aop:after-returning method="MyAfterReturning" pointcut-ref="pc" returning="returningValue"/>
<aop:after-throwing method="MyAfterThrow" pointcut-ref="pc" throwing="ex" />
<aop:around method="MyAround" pointcut-ref="pc"/>
<aop:after method="MyAfter" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>

备注

假如你不够快乐
也不要把眉头深锁
人生本来短暂
为什么 还要栽培苦涩

打开尘封的门窗
让阳光雨露洒遍每个角落
走向生命的原野
让风儿熨平前额

博大可以稀释忧愁
深色能够覆盖浅色

一起学Spring之注解和Schema方式实现AOP的更多相关文章

  1. Spring的xml文件配置方式实现AOP

    配置文件与注解方式的有很大不同,多了很多配置项. beans2.xml <?xml version="1.0" encoding="UTF-8"?> ...

  2. Spring(十二)使用Spring的xml文件配置方式实现AOP

    配置文件与注解方式的有非常大不同,多了非常多配置项. beans2.xml <?xml version="1.0" encoding="UTF-8"? & ...

  3. 2018-02-11 发布 spring 自定义注解(annotation)与 aop获取注解

    知识点: Java自定义注解.spring aop @aspect的使用 首先我们先介绍Java自定义注解. 在开发过程中,我们实现接口的时候,会出现@Override,有时还会提示写@Suppres ...

  4. 【转】spring 自定义注解(annotation)与 aop获取注解

    首先我们先介绍Java自定义注解. 在开发过程中,我们实现接口的时候,会出现@Override,有时还会提示写@SuppressWarnings.其实这个就是Java特有的特性,注解. 注解就是某种注 ...

  5. spring 代理注解 <aop:aspectj-autoproxy />

    spring默认使用jdk的代理方式,使用jdk的代理方式我们知道,代理的类需要实现一个接口,若果没有就会报,java.lang.NoSuchMethodException: com.sun.prox ...

  6. Spring的注解方式实现AOP

    Spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,asp ...

  7. 使用Spring的注解方式实现AOP

    Spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,asp ...

  8. Spring注解和配置方式

    Spring提供了一个org.springframework.beans.factory.FactoryBean工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑. 从Spring3.0开始, ...

  9. Spring 使用注解方式进行事务管理

    转载:http://www.cnblogs.com/younggun/archive/2013/07/16/3193800.html 使用步骤: 步骤一.在spring配置文件中引入<tx:&g ...

随机推荐

  1. html基础——div/span

    div是一个块标签/盒子标签,单独占据一行 span不会占据一块,一般是用来修改某几个文字的格式 比如一行字,需要将每一句的首字母大写,就可以使用span标签 如果是要将一个段落的字加样式,两个都可以 ...

  2. 【2018寒假集训Day 1】【位运算】生成字符串

    生成字符串(strs) [问题描述] 假设字符串只由字符“0”,“1”,“”组成,其中字符“”表示该字符可由 字符“0”或“1”替代. 现有一些字符串,根据这些字符串生成所有可生成的字符串.如: {1 ...

  3. mybatis源码学习(一) 原生mybatis源码学习

    最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...

  4. Linux LVM 配置

    本文出自 “www.kisspuppet.com” 博客,请务必保留此出处http://dreamfire.blog.51cto.com/418026/1084729 许多Linux使用者安装操作系统 ...

  5. python的Requests库的使用

    Requests模块: Requests 是用Python语言编写,基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库.它比 urllib 更加方便,可以节约我们大量 ...

  6. 依赖注入利器 - Dagger ‡

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/53715960 本文出自: [HansChen的博客] 概述 声明需要注入的对象 如何 ...

  7. DOM属性

    节点属性: 文档里的每个节点都有以下属性. nodeName nodeName属性将返回一个字符串,其内容是给定节点的名字: name = node.nodeName 如果给定节点是一个属性节点,no ...

  8. length of the longest substring without repeating character

    Given a string, find the length of the longest substring without repeating characters. 来源:力扣(LeetCod ...

  9. P2415 集合求和(一道洛谷好题鸭)(虽然可以水过,但有必研究DP)

    此题坑点: 结果必须要用long long存,int存不下 如果想要像cout<<sum*pow(2,num-1)这样在输出时计算会错:long long在计算过程被隐式转换成了doubl ...

  10. CentOS 7 Cobbler 安装

    Cobbler介绍 Cobbler是一个Linux服务器快速网络安装的服务,而且在经过调整也可以支持网络安装windows. 使用python开发,小巧轻便(才15k行python代码),可以通过网络 ...