1.概念:Aspect Oriented Programming 面向切面编程
在方法的前后添加方法
 
2.作用:本质上来说是一种简化代码的方式
     继承机制
     封装方法
     动态代理
     ……
    
3.情景举例
  ①数学计算器接口[MathCalculator]
          int add(int i,int j);
          int sub(int i,int j);
          int mul(int i, int j);
          int div(int i,int j);
 
因为后面的通知方法需要返回值,所以在这里类型声明为 int 类型

public interface MathCaculator {
public int add(int i,int j);
public int sub(int i,int j);
public int mul(int i,int j);
public int div(int i,int j);
}
 
   ②提供简单实现[EasyImpl]
      一定要在类上写注解,否则在 IOC容器中找不到

@Component
public class CacultorEasyImpl implements MathCaculator{
@Override
public void add(int i, int j) {
System.out.println("[日志],【参数:】"+i+","+j);
int result = i + j;
System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
} @Override
public void sub(int i, int j) {
System.out.println("[日志],【参数:】"+i+","+j);
int result = i - j;
System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
} @Override
public void mul(int i, int j) {
System.out.println("[日志],【参数:】"+i+","+j);
int result = i * j;
System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
} @Override
public void div(int i, int j) {
System.out.println("[日志],【参数:】"+i+","+j);
int result = i / j;
System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
}
}
<context:component-scan base-package="com.neuedu.aop"></context:component-scan>

  ③在简单实现的基础上让每一个计算方法都能够打印日志[LoginImpl]

private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test() {
CacultorEasyImpl bean = ioc.getBean(CacultorEasyImpl.class);
bean.add(10, 2);
bean.sub(10, 2);
bean.mul(10, 2);
bean.div(10, 2);
}

  ④缺陷
          [1]手动添加日志繁琐,重复
          [2]统一修改不便
          [3]对目标方法本来要实现的核心功能有干扰,使程序代码很臃肿,不易于开发维护
         
      ⑤使用动态代理实现
          [1]创建一个类,让这个类能够提供一个目标对象的代理对象
          [2]在代理对象中打印日志

4.AOP术语![参见图例和doc文档]
      AOP概述
          ●AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论
            是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
          ●参见图例和doc文档解释AOP的各个术语!
          ●Spring的AOP既可以使用xml配置的方式实现,也可以使用注解的方式来实现!
 
 

5.在Spring中使用AOP实现日志功能
  ①Spring中可以使用注解或XML文件配置的方式实现AOP。
  ②导入jar包
            com.springsource.net.sf.cglib -2.2.0.jar
            com.springsource.org.aopalliance-1.0.0 .jar
            com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
            commons-logging-1.1.3. jar
            spring-aop-4.0.0.RELEASE.jar
            spring-aspects-4.0.0.RELEASE.jar
            spring-beans-4.0.0.RELEASE.jar
            spring-context-4.0.0.RELEASE.jar
            spring-core-4.0.0.RELEASE.jar
            spring-expression-4.0.0.RELEASE. jar
           
  ③开启基于注解的AOP功能 < aop:aspectj-autoproxy />

<context:component-scan base-package="com.neuedu.aop"></context:component-scan>
<aop:aspectj-autoproxy/>

  ④声明一个切面类,并把这个切面类加入到 IOC容器中

   在类上加以下两个注解
          @Aspect  :表示这是一个切面类
          @Component  :加入IOC容器
         
  ⑤在切面类中声明通知方法
          [1] 前置通知:@Before
          [2] 返回通知:@AfterReturning
          [3] 异常通知:@AfterThrowing
          [4] 后置通知:@After
          [5] 环绕通知:@Around :环绕通知是前面四个通知的集合体!

@Component
@Aspect
public class CaculatorAspect { @Before(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
public void showBeginLog(){
System.out.println("日志开始");
} @After(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
public void showReturnLog(){
System.out.println("日志正常返回");
} @AfterThrowing(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
public void showExceptionLog(){
System.out.println("日志有错");
} @AfterReturning(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
public void showAfterLog(){
System.out.println("日志最终结束");
}
}

  ⑥被代理的对象也需要加入IOC容器

@Component
public class RawCaculatorImpl implements MathCaculator{ @Override
public void add(int i, int j) {
int result = i + j;
System.out.println(i+"+"+j+"="+result);
} @Override
public void sub(int i, int j) {
int result = i - j;
System.out.println(i+"-"+j+"="+result);
} @Override
public void mul(int i, int j) {
int result = i * j;
System.out.println(i+"*"+j+"="+result);
} @Override
public void div(int i, int j) {
int result = i / j;
System.out.println(i+"/"+j+"="+result);
}
}

  Test 中 用 id 查找,通过强转,调用加减乘除四个方法

private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test() {
MathCaculator bean = (MathCaculator) ioc.getBean("rawCaculatorImpl");
bean.add(10, 2);
bean.sub(10, 2);
bean.mul(10, 2);
bean.div(10, 2);
}
6.切入点表达式:
(1)上述案例通过junit测试,会发现,我们调用目标类的四个方法只有add方法被加入了4个通知
        如果想所有的方法都加上这些通知,可以在切入点表达式处:
        将execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 换成: execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))
        这样只要是有两个参数,且参数类型为int的方法在执行的时候都会执行其相应的通知方法!
  

@Component
@Aspect
public class CaculatorAspect { @Before(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
public void showBeginLog(){
System.out.println("日志开始");
} @After(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
public void showReturnLog(){
System.out.println("日志正常返回");
} @AfterThrowing(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
public void showExceptionLog(){
System.out.println("日志有错");
} @AfterReturning(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
public void showAfterLog(){
System.out.println("日志最终结束");
} }

  如果方法中的参数类型不一致,可以用 (..) 代替 (int,int)

@Component
@Aspect
public class CaculatorAspect { @Pointcut(value="execution(* com.neuedu.aop.RawCaculatorImpl.*(..))")
public void showLog(){} @Before(value="showLog()")
public void showBeginLog(JoinPoint point){
Object[] args = point.getArgs();//获取参数
List<Object> asList = Arrays.asList(args);//转为list类型
Signature signature = point.getSignature();//获取签名
String name = signature.getName();//获取方法名字
System.out.println("【前置通知】目标方法名:"+name+",参数为:"+asList);
} @After(value="showLog()")
public void showReturnLog(){
System.out.println("【后置通知】日志最终返回");
} @AfterThrowing(value="showLog()",throwing="ex")
public void showExceptionLog(JoinPoint point,Exception ex){
System.out.println("【异常通知】异常信息为:"+ex.getMessage());
} @AfterReturning(value="showLog()",returning="result")
public void showAfterLog(JoinPoint point,Object result){
System.out.println("【返回通知】方法的返回值:"+result);
System.out.println();
}
}
(2)①切入点表达式的语法格式[参见第5章AOP细节]
          execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
    
       参见第5章AOP细节:演示验证
       1.任意参数,任意类型
       2.任意返回值
       3.用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可!
          需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!

最详细的切入点表达式:
          execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
     最模糊的切入点表达式:

          execution (* *.*(..))
          第一个 * 代表   权限和返回类型
     但是不可以 无权限却有返回类型   例如: execution(* int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
 
7.统一声明切入点表达式
     @Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))")
     public void myPointCut(){}
    
    
8.通知方法的细节
 
 ①在通知中获取目标方法的方法名和参数列表
          [1]在通知方法中声明一个JoinPoint类型的形参
               JointPoint 是切入点,
          [2]调用JoinPoint对象的getSignature()方法获取目标方法的签名
          [3]调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表
 

@Before(value="showLog()")
public void showBeginLog(JoinPoint point){
Object[] args = point.getArgs();//获取参数
List<Object> asList = Arrays.asList(args);//转为list类型
Signature signature = point.getSignature();//获取签名
String name = signature.getName();//获取方法名字
System.out.println("目标方法名:"+name+",参数为:"+asList);
System.out.println("日志开始");
}
  
    ②在返回通知中获取方法的返回值
          [1]在 @AfterReturning   注解中添加returning属性
          [2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致     
              不知道返回什么类型,所以用 Object 类型

@AfterReturning(value="showLog()",returning="result")
public void showAfterLog(JoinPoint point,Object result){
System.out.println("方法的返回值:"+result);
System.out.println("日志正常结束");
System.out.println();
}
  
   ③在异常通知中获取异常对象
          [1]在 @AfterThrowing 注解中添加 throwing 属性
          [2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致
@AfterThrowing(value="showLog()",throwing="ex")
public void showExceptionLog(JoinPoint point,Exception ex){
System.out.println("异常信息为:"+ex.getMessage());
System.out.println("日志有错");
}

9.根据接口类型获取target对象时,实际上真正放在IOC容器中的对象是代理对象,而并不是目标对象本身!

10.环绕通知:@Around
    1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数
          @Around(value="pointCut()")
          public void around(ProceedingJoinPoint joinPoint){
          }
     2.环绕通知会将其他4个通知能干的,自己都给干了!

          注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理!
                  但是不知道返回值的类型 ,所以用 Object 
 
@Around(value="execution(public * com.neuedu.aop.RawCaculatorImpl.*(..))")
public Object showLog(ProceedingJoinPoint point){
Object[] args = point.getArgs();
List<Object> asList = Arrays.asList(args);
Signature signature = point.getSignature();//获取签名
String name = signature.getName();
Object result=null;
try {
try{
System.out.println("【前置通知】目标方法名:"+name+",参数为:"+asList);
result = point.proceed(args);
}finally{
System.out.println("【后置通知】日志最终返回");
}
System.out.println("【返回通知】方法的返回值:"+result);
} catch (Throwable e) {
System.out.println("【异常通知】异常信息为:"+e.getMessage());
}
System.out.println();
return result;
}
 
11.切面的优先级
     对于同一个代理对象,可以同时有多个切面共同对它进行代理。
     可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高!
     没有 @Order 注解默认最大

@Component
@Aspect
@Order(value=20)
public class BAspect { @Around(value="execution(* com.neuedu.aop.RawCaculatorImpl.*(..))")
public Object showLog(ProceedingJoinPoint point){
Object[] args = point.getArgs();
List<Object> asList = Arrays.asList(args);
Signature signature = point.getSignature();//获取签名
String name = signature.getName();
Object result=null;
try {
try{
System.out.println("【前置】目标方法名:"+name+",参数为:"+asList);
result = point.proceed(args);
}finally{
System.out.println("【后置】日志最终返回");
}
System.out.println("【返回】方法的返回值:"+result);
} catch (Throwable e) {
System.out.println("【异常】异常信息为:"+e.getMessage());
}
System.out.println();
return result;
}
}
12.注意:上面的AOP都是通过注解实现的,AOP实际上也可以通过xml配置的方式实现!
     将注解全删掉

<!-- 将需要加载到IOC容器中的bean配置好 -->
<bean id="caculatorAspect" class="com.neuedu.aop.CaculatorAspect"></bean>
<bean id="bAspect" class="com.neuedu.aop.BAspect"></bean>
<bean id="rawCaculatorImpl" class="com.neuedu.aop.RawCaculatorImpl"></bean> <!-- 配置AOP,需要导入AOP名称空间 -->
<aop:config>
<!-- 声明切入点表达式 -->
<aop:pointcut expression="execution(* com.neuedu.aop.RawCaculatorImpl.*(..))" id="myPointCut"/>
<!-- 配置日志切面类,引用前面的类 ,通过order属性控制优先级-->
<aop:aspect ref="caculatorAspect" order="20">
<!-- 通过method属性指定切面类的切面方法,通过pointcut-ref指定切入点表达式 -->
<aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
<aop:after method="showAfterLog" pointcut-ref="myPointCut"/>
<aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/>
<aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/>
</aop:aspect>
<!-- 配置事务切面类,引用前面的类 -->
<aop:aspect ref="bAspect" order="25">
<aop:around method="showLog" pointcut-ref="myPointCut"/>
</aop:aspect> </aop:config>

需要知道的是:事务的管理是和AOP是有很大关系的,即声明式事务的底层是用AOP实现的!

 

Spring(三)--AOP【面向切面编程】、通知类型及使用、切入点表达式的更多相关文章

  1. Spring(三)AOP面向切面编程

    原文链接:http://www.orlion.ga/205/ 一.AOP简介 1.AOP概念 参考文章:http://www.orlion.ml/57 2.AOP的产生 对于如下方法:     pub ...

  2. Spring:AOP面向切面编程

    AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...

  3. Spring 08: AOP面向切面编程 + 手写AOP框架

    核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...

  4. spring框架学习(三)——AOP( 面向切面编程)

    AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...

  5. Spring框架 AOP面向切面编程(转)

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  6. spring:AOP面向切面编程02

    参考: https://blog.csdn.net/jeffleo/article/details/54136904 一.AOP的核心概念AOP(Aspect Oriented Programming ...

  7. Spring注解 - AOP 面向切面编程

    基本概念: AOP:Aspect Oriented Programming,即面向切面编程 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式 前置通知(@Before):在目标 ...

  8. Spring的AOP面向切面编程

    什么是AOP? 1.AOP概念介绍 所谓AOP,即Aspect orientied program,就是面向方面(切面)的编程. 功能: 让关注点代码与业务代码分离! 关注点: 重复代码就叫做关注点: ...

  9. Spring框架——AOP面向切面编程

    简介 AOP练习 使用动态代理解决问题 Spring AOP 用AspectJ注解声明切面 前置后置通知 利用方法签名编写AspectJ切入点表达式 指定切面的优先级 基于XML的配置声明切面 Spr ...

  10. Spring之AOP(面向切面编程)_入门Demo

    AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可 ...

随机推荐

  1. 解决React Native unable to load script from assets index.android.bundle on windows

    React Native运行的时候,经常碰到React Native unable to load script from assets index.android.bundle on windows ...

  2. FZU 2253 salty fish

    https://vjudge.net/problem/FZU-2253 题意:略 思路: 一开始改变区间,还以为是线段树...还是dp的题做得太少了. 这题一开始我们可以统计出一共有多少只翻身的咸鱼, ...

  3. Ajax获取服务器信息

    xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && ...

  4. 蓝桥杯比赛javaB组练习《牌型种数》

    牌型种数 小明被劫持到X赌城,被迫与其他3人玩牌.一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张.这时,小明脑子里突然冒出一个问题:如果不考虑花色,只考虑点数,也不考虑自己得到的牌 ...

  5. HDU6055 Regular polygon(计算几何)

    Description On a two-dimensional plane, give you n integer points. Your task is to figure out how ma ...

  6. 从.net到java,记录下这三个月的工作

    从事.NET开发已经4个年头,经过十余个项目的学习与沉淀,终于有了一套自己熟悉并且相对完善的技术体系,面对未知,不再惧怕.期间完成并广泛用于公司项目的作品包括: abp的二次开发框架BodeAbp 基 ...

  7. 【思维】【水】 南阳oj 喷水装置(一)

    描述 现有一块草坪,长为20米,宽为2米,要在横中心线上放置半径为Ri的喷水装置,每个喷水装置的效果都会让以它为中心的半径为实数Ri(0<Ri<15)的圆被湿润,这有充足的喷水装置i(1& ...

  8. TP3.2二级导航与高亮显示

    闲来无事,记记笔记 二级导航的实现,就是一个二级联动,嗯 先来看看数据库,我喜欢用一个表,表多了就不好玩了 二级代号取一级导航的代号就行了,做个简单的栗子: 代码部分: 看控制器,大头在这: < ...

  9. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...

  10. 搭建ElasticSearch+MongoDB检索系统

    ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...