`

spring aop学习笔记

阅读更多
理解spring aop的路径:最初级的做法是通过使用代理将业务代码和系统代码分离,也就是在向代理类中注入业务接口实现类,然后在调用业务接口代码时调用系统代码;
//******* TimeBook.java**************
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
public class TimeBook {
         private Logger logger = Logger.getLogger(this.getClass().getName()); 
         //审核数据的相关程序
         public void doAuditing(String name) { 
                  logger.log(Level.INFO, name + " 开始审核数据...."); 
                  //审核数据的相关程序
                  ……    
                  logger.log(Level.INFO, name + " 审核数据结束...."); 
         }
}
//******* TestHelloWorld.java**************
package com.gc.test;
import com.gc.action.TimeBook;
public class TestHelloWorld {
         public static void main(String[] args) { 
                   TimeBook timeBook = new TimeBook();
                   timeBook.doAuditing("张三");
    } 
}
//******* TimeBookInterface.java**************
package com.gc.impl;
import org.apache.log4j.Level;
//通过面向接口编程实现日志输出
public interface TimeBookInterface 
{
         public void doAuditing(String name);
}
//******* TimeBook.java**************
package com.gc.action;
import com.gc.impl.TimeBookInterface;
public class TimeBook implements TimeBookInterface 
{
public void doAuditing(String name) { 
//审核数据的相关程序
……    
}
}
//******* TimeBookProxy.java**************
package com.gc.action;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.gc.impl.TimeBookInterface;
public class TimeBookProxy 
{
   private Logger logger = Logger.getLogger(this.getClass().getName()); 
   private TimeBookInterface timeBookInterface; 
   //在该类中针对前面的接口TimeBookInterface编程,而不针对具体的类
     public TimeBookProxy(TimeBookInterface timeBookInterface) 
     { 
         this.timeBookInterface = timeBookInterface; 
     }
          //实际业务处理
          public void doAuditing(String name) 
           { 
                   logger.log(Level.INFO, name + " 开始审核数据...."); 
                   timeBookInterface.doAuditing(name);    //调用方法
                   logger.log(Level.INFO, name + " 审核数据结束...."); 
          }
}
//******* TestHelloWorld.java**************
package com.gc.test;
import com.gc.action.TimeBook;
import com.gc.action.TimeBookProxy;
public class TestHelloWorld {
         public static void main(String[ ] args) 
             { 
                     //这里针对接口进行编程
                     TimeBookProxy timeBookProxy  = new TimeBookProxy(new TimeBook());
                     timeBookProxy .doAuditing("张三");
             } 
}

为了更加通用, 引入java的动态代理机制来解除代理类注入的业务类必须实现指定接口的限制, 这个要将前面所说的代理类进行修改,让其实现InvocationHandler 接口, 该接口有两个方法:bind and invoke methods;在bind方法中和前面一样用来注入业务类(只不过该业务类不在是特定的类, 而是一个Object对象), 在invoke中将业务逻辑调用代码和系统代码进行混合(其中也使用了反射机制);
//******* LogProxy.java**************
package com.gc.action;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
//代理类实现了接口InvocationHandler
public class LogProxy implements InvocationHandler 
{
         private Logger logger = Logger.getLogger(this.getClass().getName()); 
         private Object delegate; 
         //绑定代理对象 
         public Object bind(Object delegate) 
             { 
                     this.delegate = delegate; 
                     return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), 
                     delegate.getClass().
                     getInterfaces(), this); 
             } 
        //针对接口编程 
         public Object invoke(Object proxy, Method method, Object[ ] args) throws Throwable 
             { 
                     Object result = null; 
                              try { 
                                         //在方法调用前后进行日志输出
                                        logger.log(Level.INFO, args[0] + " 开始审核数据...."); 
                                        result = method.invoke(delegate, args); //调用绑定对象的方法
                                        logger.log(Level.INFO, args[0] + " 审核数据结束....");
                                  } 
                            catch (Exception e)
                                 { 
                                       logger.log(Level.INFO, e.toString()); 
                                 } 
                      return result; 
           } 
}
//******* TestHelloWorld.java**************
package com.gc.test;
import com.gc.action.TimeBook;
import com.gc.action.TimeBookProxy;
import com.gc.impl.TimeBookInterface;
import com.gc.action.LogProxy;
public class TestHelloWorld {
         public static void main(String[ ] args) 
                { 
                      //实现了对日志类的重用
                      LogProxy logProxy  = new LogProxy(); 
                      TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy.bind(new TimeBook()); 
                      timeBookProxy.doAuditing("张三");
                } 
}

spring 的aop实现正是建立在java的动态代理机制上。要理解aop还必须理解几个概念, 第一个就是PointCut(切入点),可以将其理解为所有要进行代理的业务对象及其方法的集合(也可以理解为JoinPoint的集合,说穿了就是注入业务代码的位置, 而这个位置就是JoinPoint), 这一点可以从Spring AOP的PointCut接口定义中看出来:
package org.springframework.aop;
public interface Pointcut 
{
         //用来将切入点限定在给定的目标类中
         ClassFilter getClassFilter();
         //用来判断切入点是否匹配目标类给定的方法
         MethodMatcher getMethodMatcher();
         Pointcut TRUE = TruePointcut.INSTANCE;
}

跟PointCut对应的是JoinPoint(连接点),也就是插入系统代码的方法调用、异常抛出等
最后一个概念就是通知(Advice)也就是用来放系统代码的地方, 而Advisor = Advise+PointCut(这里是指的具体的位置,比如指定的方法名)
常用的Advisor是org.springframework.aop.support.RegexpMethodPointcutAdvisor
这个需要理解正则表达式的一些概念:
引用

(1)“.”,可以用来匹配任何一个字符。比如:正则表达式为“g.f”,它就会匹配“gaf”、“g1f”、“g*f”和“g #f”等。
(2)“[]”,只有[]里指定的字符才能匹配。比如:正则表达式为“g[abc]f”,它就只能匹配“gaf”、“gbf”和“gcf”,而不会匹配“g1f”、“g*f”和“g#f”等。
(3)“*”,表示匹配次数,可以任意次,用来确定紧靠该符号左边的符号出现的次数。比如:正则表达式为“g.*f”,它能匹配“gaf”、“gaaf”、“gf”和“g*f”等。
(4)“?”,可以匹配0或1次,用来确定紧靠该符号左边的符号出现的次数。比如:正则表达式为“g.?f”,它能匹配“gaf”“g*f”等。
(5)“\”,是正则表达式的连接符。比如:正则表达式为“g.\-f”,它能匹配“g-f”、“ga-f”和“g*-f”等。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="log" class="com.gc.action.LogAround"/>
<bean id="timeBook" class="com.gc.action.TimeBook"/>
<!--代理目标类的指定方法-->
<bean id="logAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
        <property name="advice"> 
            <ref bean="log"/> 
        </property> 
               <!--指定要代理的方法-->
        <property name="patterns"> 
            <value>.*doAuditing.* </value> 
        </property> 
    </bean>
<!--设定代理类-->
<bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> 
        <property name="proxyInterfaces"> 
            <value>com.gc.impl.TimeBookInterface</value>
        </property>
        <property name="target">
            <ref bean="timeBook"/>
        </property>
        <property name="interceptorNames">
            <list>
                <value>logAdvisor</value>
            </list>
        </property>
    </bean>
</beans>



spring提供了四种Advice:
第1种:在需要调用方面的方法前后都调用处理方面的代码
第2种:在需要调用方面的方法之前调用处理方面的代码
第3种:在需要调用方面的方法之后都调用处理方面的代码
第4种:在需要调用方面的方法发生异常时调用处理方面的代码
示例配置代码如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
 "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="log" class="com.gc.action.LogAop"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean”>
    <property name=”proxyInterfaces”>
        <value>com.gc.impl.TimeBookInterface</value>
    </property>
    <property name=”target”>
        <ref bean=”timeBook”/>
    </property>
    <property name=”interceptorNames”>
         <list>
              <value>log</value>
         </list>
    </property>
</bean>
</beans>

所有的配置都一样, 只是Advice不同而已:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
 
public class LogAround implements MethodInterceptor{
      private Logger logger = Logger.getLogger(this.getClass().getName()); 
    
    public Object invoke(MethodInvocation methodInvocation) throws Throwable { 
           logger.log(Level.INFO, methodInvocation.getArguments()[0] + " 开始审核数据...."); 
        try { 
          Object result = methodInvocation.proceed(); 
          return result; 
        } 
        finally { 
                 logger.log(Level.INFO, methodInvocation.getArguments()[0] + " 审核数据结束....");
        }        
   } 
}     

import java.lang.reflect.Method;
 
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;
 
public class LogBefore implements MethodBeforeAdvice {
       private Logger logger = Logger.getLogger(this.getClass().getName()); 
    
    public void before(Method method, Object[] args, Object target) throws Throwable { 
              logger.log(Level.INFO, args[0] + " 开始审核数据....");
   }
}

import java.lang.reflect.Method;
 
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
 
public class LogAfterReturning implements AfterReturningAdvice {
       private Logger logger = Logger.getLogger(this.getClass().getName()); 
    
    public void afterReturning(Method method, Object[] args, Object target) throws Throwable { 
              logger.log(Level.INFO, args[0] + " 开始审核数据....");
   }
}

import java.lang.reflect.Method;
 
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.aop.ThrowsAdvice;
 
public class LogThrow implements ThrowsAdvice {
       private Logger logger = Logger.getLogger(this.getClass().getName()); 
    
    public void afterThrowing(Method method, Object[] args, Object target,Throwable subclass) throws Throwable { 
              logger.log(Level.INFO, args[0] + " 开始审核数据....");
   }
}

除了使用ProxyFactoryBean来创建AOP代理外,还可以使用DefaultAdvisorAutoProxyCreator来创建自动代理, 当在配置文件中包括DefaultAdvisorAutoProxyCreator bean定义,那么在Bean定义档被读取完之后, DefaultAdvisorAutoProxyCreator会自动搜寻所有的Advisor(因为DefaultAdvisorAutoProxyCreator实现了BeanProcessor接口),并自动将Advisor应用至符合Pointcut的目标业务类上。实际上这是一个偷懒的做法, 将advisor和具体业务类的关联管理处理交给spring去处理了。

在指定业务对象的同时还需要指定业务对象所实现的接口(面向接口编程), 如果业务对象没有实现接口就需要借助cglib(这个一般是针对那些不能修改源代码的遗留系统的做法),对应的配置文件应该这样写:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
 "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="log" class="com.gc.action.LogAop"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean”>
//增加如下属性,就表示使用的是CGLIB代理(对目标类直接代理)
    <property name=”proxyTargetClass”>
        <value>true</value>
    </property>
   /*然后去掉下面的属性,也就是说此种方法不需要面向接口,或不需要指出接口 
<property name=”proxyInterfaces”>
        <value>com.gc.impl.TimeBookInterface</value>
    </property>*/
    <property name=”target”>
        <ref bean=”timeBook”/>
    </property>
    <property name=”interceptorNames”>
         <list>
              <value>log</value>
         </list>
    </property>
</bean>
</beans>


在spring2.0之后, 提供了基于schema的aop配置, 与以前的配置相比,它对spring的一些aop实现细节做了进一步的屏蔽(比如代理和拦截器),对spring aop的使用者来说更简单了。

基于schema的aop借用了一些AspectJ中的一些做法, 如果对AspectJ比较熟悉的话, 使用起来是非常容易的。
首先对里面的一些aop元素进行一下说明:
aop:config是aop配置中的一个顶级元素, 所有的aop的配置定义都必须包含在该元素中

aop:aspect类似于以前spring2.0以前配置中那个Advisor(但是又不完全是,因为还有一个aop:advisor元素与之对应),它包含了PointCut和Advice信息, 它会有一个对应的bean,许多advice信息也包含在里面,不过不用在象以前那样实现指定的BeforeXxx, AroundXxxx之类的接口了, 可以直接通过 org.aspectj.lang.ProceedingJoinPoint来调用指定切面对象的方法。
比如:
<aop:aspect id="aroundExample" ref="aBean">
	<aop:around
	  pointcut-ref="businessService"
	  method="doBasicProfiling"/>
	...
</aop:aspect>

方法doBasicProfiling就是aBean中定义的一个方法, 它的定义是这样的:
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}


aop:pointcut 是切入点定义,跟以前的切入点定义没有什么区别,不过象以前那种正则表达式定义放到了expression属性中, 而且使用了AspectJ的语法。它可以定义在aop:aspect中, 也可以定义在aop:config中

aop:after-returning(before, after-throwing, after, around)这个是Advice的定义, 它里面指定了要跟已定义的哪个切入点关联(pointcut-ref属性), 并且使用aspect中定义的哪个方法(method属性)。
11
0
分享到:
评论

相关推荐

    spring aop 学习笔记

    本学习笔记将深入探讨Spring AOP的核心概念、工作原理以及实际应用。 1. **核心概念** - **切面(Aspect)**:切面是关注点的模块化,包含业务逻辑之外的横切关注点,如日志、事务管理。 - **连接点(Join Point...

    Spring Aop 学习笔记

    Spring Aop 学习笔记

    SpringAop学习笔记以及实现Demo

    **Spring AOP 学习笔记及实现Demo** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架中的一个重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。AOP的主要目的...

    Spring AOP学习笔记

    NULL 博文链接:https://linres.iteye.com/blog/281221

    SpringAOP学习笔记

    ,文章属于基础级文章,适合入门级的小伙伴,它的概念,应用场景,实现原理及Spring的AOP的开发。全称:面向切面编程(AspectOrientedProgramming),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。...

    spring ioc aop mvc boot-学习笔记.docx

    Spring框架是Java开发中不可或缺的一部分,它为开发者提供了强大的依赖注入(IOC)和面向切面编程(AOP)功能,以及用于构建Web应用程序的MVC框架。Spring Boot则是基于Spring框架构建的应用程序启动器,旨在简化...

    Spring AOP 使用笔记

    本笔记主要探讨了如何在Spring应用中使用AOP来实现横切关注点,如日志、事务管理、性能监控等。 首先,理解AOP的基本概念至关重要。AOP的核心是切面(Aspect),它封装了跨越多个对象的行为或责任。切面由两个主要...

    Spring框架学习笔记

    这份"Spring框架学习笔记"涵盖了Spring框架的基础知识、核心组件以及高级特性,对于初学者来说是一份宝贵的资料。 一、Spring框架概述 Spring框架是为了解决企业应用开发的复杂性而设计的,它提供了一个全面的基础...

    马士兵老师spring框架学习笔记

    马士兵老师是知名的Java教育专家,他的Spring框架学习笔记深入浅出,对于初学者和进阶者来说都是一份宝贵的资源。这份笔记涵盖了Spring的核心概念、配置、AOP(面向切面编程)、DI(依赖注入)等关键知识点。 1. **...

    Spring学习笔记&源码

    本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...

    Java Spring框架学习笔记(内附源码).pdf

    在本次的Java Spring框架学习笔记中,将对Spring框架的核心概念进行详细解析,包括Spring的 IOC(控制反转)、AOP(面向切面编程)、jdbcTemplate、事务管理、Spring5新特性以及与Mybatis的整合。本学习笔记提供了...

    spring指南学习笔记

    标题和描述均提到了“spring指南学习笔记”,这意味着文档聚焦于Spring框架的学习心得与关键概念。Spring是一个开源的Java企业级应用框架,以其强大的依赖注入(Dependency Injection, DI)和面向切面编程(Aspect ...

    Spring学习笔记+学习源码.zip

    这份"Spring学习笔记+学习源码.zip"资源包含了深入学习Spring及其相关技术的知识点,以及实践代码,对提升Spring技能将大有裨益。 首先,我们来详细讨论Spring框架的主要组件和功能: 1. **依赖注入(Dependency ...

    spring框架学习笔记

    Spring框架是Java应用开发中广泛使用的轻量级框架,它以IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)为核心,提供了丰富的功能,包括但不限于组件管理、AOP(Aspect Oriented ...

    Spring入门学习笔记|Spring学习.pdf

    Spring入门学习笔记,内容包括Spring介绍,Spring配置文件,Spring配置数据源,Spring的注解开发,Spring集成Junit,Spring的AOP,jdbcTemplate介绍,Spring控制事务流程,Spring集成web。

    Spring的学习笔记

    以下将详细介绍Spring学习笔记中的主要知识点。 **面向抽象编程** 面向抽象编程是一种设计原则,强调在代码中使用接口或抽象类,而不是具体实现类。这使得系统更具有灵活性,易于扩展和维护。在Spring框架中,我们...

    Spring学习笔记.zip

    根据提供的压缩包文件名,我们可以推测这是一个逐步学习Spring的系列笔记。从"Spring_day1"开始,可能涵盖了Spring的基础概念、环境搭建和基本配置。"Spring_day2"可能涉及了依赖注入和AOP的深入讲解。"Spring_day3...

    Spring技术内幕 学习笔记

    总之,《Spring技术内幕 学习笔记》涵盖了Spring框架的众多核心知识点,从IoC容器、AOP到Web开发和数据访问,对于提升Spring开发技能具有很高的价值。通过深入学习和实践,开发者能够更好地理解和掌握Spring框架,...

Global site tag (gtag.js) - Google Analytics