AOP全解

一、概述
(一)基本概念
1 、什么是AOP?
    面向方面编程。所谓方面即是指日志、权限、异常处理、事务处理等。

2 、AOP的3个关键概念
   ( 1 )切入点( Pointcut ): Pointcut 是 Join Point 的集合, Join Point 就是需要注入 Adivce 的位置,也就是需要插入日志输出代码、事务处理代码等“方面”( Aspect ,也就是 AOP 中的 A )代码的地方。

    比如我现在要写一个存钱的方法: saving ()

    通常情况下我就得在这个 saving ()方法前后写些事务代码

    如:



          logger.log(Level.INFO,”start”);

Saving();

                         logger.log(Level.INFO,”end”);

      

         对于事务代码而言, saving ()方法的前后就都是 Join Point 了。在 Spring 中它对应 config.xml 中设定的方法,这个方法就是类( class )中需要进行某方面处理的方法( method )。

         ( 2 )通知( Advice ): 就是指 Join Point 对应的代码(方法)。比如日志输出这个方面,指的就是日志输出的代码或方法了。在 Spring 中,它对应类( class )。

        ( 3 ) Advisor : 是 Poincut 和 Advice 的配置器,它包括 Pointcut 和 Advice ,是将 Advice 注入程序中 Pointcut 位置的代码。在 Sping 中,它对应 config.xml 中的配置段 <bean id=logAdvisor class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”> 。

  <bean id=”logAdvisor” class=”org.springframework.aop.support.RegexpMetho

dPointcutAdvisor”>

    //advice 属性对应的当然就是输出日志的类,也就是 ① 对应的那个bean

    <property name=”advice”>

         <ref bean=”log”/>

    </property>

   //patterns 属性指出指定要代理的方法,使用的是正则表达式

    <property name=”patterns”>

         <value>.*doAuditing.*</value>

    </property>

</bean>

  

(二)框架图
  创建代理的两种方法

ProxyFactoryBean

动态代理


  

Spring4 种 Advice

Interception Around

Before

After Returning

Throw


  

两种代理方式

Java 动态代理

CGLIB 代理


  


(三)何时使用什么
  1 、创建代理的两种方法中首选动态代理。
第 1 种:针对某个类进行配置。可以指定某个类中所有方法都调用方面,也可以指定某个类中的某个方法,此时由于用到正则表达式,于是需要引入 jakarta-oro-2.0.8.jar 包。

第 2 种:针对某个方法进行配置。

  2 、Spring4种Advice:
      第 1 种:在需要调用方面的方法前后都调用处理方面的代码

      第 2 种:在需要调用方面的方法之前调用处理方面的代码

第 3 种:在需要调用方面的方法之后都调用处理方面的代码

第 4 种:在需要调用方面的方法发生异常时调用处理方面的代码

3 、两种代理方式首选第1种。
          第 1 种:面向接口,必须先定义接口,这是好的习惯,应该提倡

          第 2 种:当没有接口的时候,可以使用这种方法。需引入 cglib-nodep-2.1_3,jar 包。

二、详细
(一)、创建AOP代理的两种方法:
1 、用ProxyFactoryBean创建AOP代理
   (需要指明代理目标类)

( 1 )代理目标类的所有方法

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

// 下面 interceptorNames 属性(property)中的value值就是这个bean的id,其主要对应的是写入日志的那个类,也就是Spring AOP概念中的Advice(通知)。

① <bean id="log" class="com.gc.action.LogAround"/>

// 要输出日志的那个类(因为这种方法必须要指明代理目标类)

② <bean id="timeBook" class="com.gc.action. timeBook "/>

// 下面开始定义代理类,也就是ProxyFactoryBean,这是Spring自带的类,这也是Spring AOP中的Advisor

<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

yBean ”>

// 第一个属性,是指明要代理的类的接口,因为这个例子中使用的是Java动态代理机制来实现AOP的,因此必须指明接口

    <property name=”proxyInterfaces”>

        <value>com.gc.impl.TimeBookInterface</value>

    </property>

// 这个属性是指明代理目标(target)类,也就是 ② 定义的那个类

    <property name=”target”>

        <ref bean=”timeBook”/>

    </property>

[U1]   // 这个属性是用来指明插入哪个Advice,此处使用list,应该表示这个类不只是可以调用这一个log类

    <property name=”interceptorNames”>

         <list>

              <value>log</value> // 这个值(log)对应 ① 中定义的那个id为log的bean

         </list>

    </property>

[U2]   </bean>

</beans>

( 2 )代理目标类的指定方法

<?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,用来指明要输出日志的指定方法(上一个例子是所有方法都输出日志)

<bean id=”logAdvisor” class=”org.springframework.aop.support.RegexpMetho

dPointcutAdvisor”>

    //advice 属性对应的当然就是输出日志的类,也就是 ① 对应的那个bean

    <property name=”advice”>

         <ref bean=”log”/>

    </property>

   //patterns 属性指出指定要代理的方法,使用的是正则表达式

    <property name=”patterns”>

         <value>.*doAuditing.*</value>

    </property>

</bean>

<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>

  

2 、用DefaultAdvisorAutoProxyCreator创建自动代理
   (好处:不用指明代理目标类,如果一个大项目中有很多类也不必一个一个设置 AOP 代理)

<?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.Log Aop "/>

<bean id="timeBook" class="com.gc.action. timeBook "/>

// 使用DefaultAdvisorAutoProxyCreator(红色代码)替代ProxyFactoryBean(绿色代码),因为绿色代码的作用是为具体的类(即所谓代理目标类)设置advice。

<bean id=”autoProxyCreator” class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”>

<bean id=”logAdvisor [U3]   ” class=”org.springframework.aop.support.RegexpMetho

dPointcutAdvisor”>

     <property name=”advice”>

         <ref bean=”log”/>

    </property>

     <property name=”patterns”>

         <value>.*doAuditing.*</value> [U4]   

    </property>

</bean>

/*<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>

  

3 、总结
实际上, DefaultAdvisorAutoProxyCreator 和 ProxyFactoryBean 就是两种代理类,前者是自动的将 Advisor 和目标类联系起来,后者是通过指定的方式,将目标类和 Advisor 组合起来。

而 advisor ,对应的就是 org.springframework.aop.support.RegexpMethodPointcutAdvisor ,通过正则表达式来匹配类中的方法(设定 Pointcut )。

(二)Spring四种通知(Advice)形式
1 、Interception Around通知
       ( 1 )负责输出日志的类

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 Objectinvoke(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] + " 审核数据结束....");

        }        

   }

}      

  ( 2 )配置文件

<?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.Log Aop "/>

<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>

  

  

2 、Before通知
( 1 )负责输出日志的类

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] + " 开始审核数据 ....");

   }

}

( 2 )配置文件

     与第 1 种方法相同。

3 、After Returning通知
( 1 )负责输出日志的类

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] + " 开始审核数据 ....");

   }

}

( 2 )配置文件

     与第 1 种方法相同。

4 、Throw通知
( 1 )负责输出日志的类

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] + " 开始审核数据 ....");

   }

}

( 2 )配置文件

     与第 1 种方法相同。

5 、测试程序
   ( 1 )使用自动代理

public class TestHelloWorld {

       public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

              ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

              TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("timeBook");

              timeBookProxy.doAuditing(" 张三 ");

   ( 2 )不使用自动代理

public class TestHelloWorld {

       public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

              ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

              TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");

              timeBookProxy.doAuditing(" 张三 ");

使用自动代理,则直接调用该类的名字( timeBook ),否则调用相应的代理 bean ( logProxy )。因为第 1 种方法中根本就没有对应的代理 bean ,只有一个 Spring 的自动代理类

  

(三)Spring两种代理方式
1 、Java动态代理
( 1 )配制文件(不使用自动代理)

<?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.Log Aop "/>

<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>

  

( 2 )测试程序

public class TestHelloWorld {

       public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

              ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

              TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");

              timeBookProxy.doAuditing(" 张三 ");

  

2 、CGLIB代理
( 1 )配制文件(不使用自动代理)

<?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.Log Aop "/>

<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>

  

( 2 )测试程序

public class TestHelloWorld {

       public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

              ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

              TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");

              timeBookProxy.doAuditing(" 张三 ");

  

  


--------------------------------------------------------------------------------

[U1] Join Point

[U2] Advice

[U3] 这个名字自己可以随便起

[U4] 如果想对类的所有方法都有效,就要写成 .*.*

 

 

 

 

零雨其蒙原创

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值