天天看点

AOP通知类型

分类

  1. 前置通知
    • 在目标类的方法执行之前执行。
    • 配置文件信息:
    • 应用:可以对方法的参数来做校验
  2. 最终通知
    • 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
    • 在配置文件中编写具体的配置:
    • 应用:例如像释放资源
  3. 后置通知
    • 方法正常执行后的通知。
    • 在配置文件中编写具体的配置:
    • 应用:可以修改方法的返回值
  4. 异常抛出通知
    • 在抛出异常后通知
    • 在配置文件中编写具体的配置:
    • 应用:包装异常的信息
  5. 环绕通知
    • 方法的执行前后执行。
    • 在配置文件中编写具体的配置:
    • 要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。

注意:AOP依赖与IoC,在spring里不是使用注解就是配置xml,所以,下面会用到配置文件进行配置通知

步骤

使用maven导包spring-context,会自动引入spring-aop

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>
           

提供代理对象PhoneImpl.java

public class PhoneImpl {

    public void product5S() {
        System.out.println("生产iPhone 5S中");
    }
    //为下面的异常通知使用
    public void testException() {
        //设置异常
        int i=/;
        System.out.println("这是异常下面的代码");
    }
}
           

提供一个

applicationContext.xml

,提供一个

Test.java

测试类

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans>
           

前置通知

提供代理对象

BeforeProxy.java

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeProxy implements MethodBeforeAdvice{

    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        System.out.println("前置通知");
    }

}
           

配置文件applicationContext.xml中配置前置通知

<!-- 创建要代理的对象  一下的id随便起名字-->
    <bean id="phone" class="com.phone.PhoneImpl"></bean>
    <!-- 创建增强对象,也就是通知对象 -->
    <bean id="before" class="com.proxy.BeforeProxy"></bean>
    <!-- 处理要代理的对象与增强对象之间的关系 -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 设置要代理的对象,也就是目标对象 -->
        <property name="target" ref="phone"></property>
        <!-- 设置拦截器,也就是增强对象 注意使用的是value属性-->
        <property name="interceptorNames" value="before"></property>
        <!-- 如果为true表示jdk代理,要有接口,如果为false表示可以没有接口 -->
        <property name="optimize" value="false"></property>
    </bean>
           

测试

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        PhoneImpl phone=(PhoneImpl)applicationContext.getBean("proxy");
        phone.product5S();
           

运行结果

前置通知
生产iPhone 5S中
           

后置通知

提供后置通知对象

AfterProxy.java

,注意实现的接口

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterProxy implements AfterReturningAdvice{

    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
        System.out.println("后置通知");
    }

}
           

配置文件applicationContext.xml中增加bean

<!-- 创建要代理的对象  一下的id随便起名字-->
    <bean id="phone" class="com.henu.phone.PhoneImpl"></bean>
    <!-- 创建增强对象,也就是通知对象 -->
    <bean id="before" class="com.henu.proxy.BeforeProxy"></bean>
    <!--增加的地方-->
    <bean id="after" class="com.henu.proxy.AfterProxy"></bean>
    <!-- 处理要代理的对象与增强对象之间的关系 -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 设置要代理的对象,也就是目标对象 -->
        <property name="target" ref="phone"></property>
        <!-- 设置拦截器,也就是增强对象 注意使用的是value属性-->
        <!--修改的地方,同时提供前置和后置通知,这个地方为什么不用ref引用呢,如果用ref会报错,这里可以引入多个对象,如果用ref只能引入一个对象-->
        <property name="interceptorNames" value="after,before"></property>
        <!-- 如果为true表示jdk代理,要有接口,如果为false表示可以没有接口 -->
        <property name="optimize" value="false"></property>
    </bean>
           

运行结果

前置通知
生产iPhone 5S中
后置通知
           

环绕通知

提供

RoundProxy.java

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class RoundProxy implements MethodInterceptor{
    //可以替代目标方法
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕通知");
        //如果没有下面这些,执行的时候目标方法会被替代,只显示上面的一句话
        invocation.proceed();//执行原来的方法
        return null;
    }

}
           

修改配置文件

<!-- 创建要代理的对象  一下的id随便起名字-->
    <bean id="phone" class="com.henu.phone.PhoneImpl"></bean>
    <!-- 创建增强对象,也就是通知对象 -->
    <bean id="before" class="com.henu.proxy.BeforeProxy"></bean>
    <bean id="after" class="com.henu.proxy.AfterProxy"></bean>
    <bean id="round" class="com.henu.proxy.RoundProxy"></bean>
    <!-- 处理要代理的对象与增强对象之间的关系 -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 设置要代理的对象,也就是目标对象 -->
        <property name="target" ref="phone"></property>
        <!-- 设置拦截器,也就是增强对象 注意使用的是value属性-->
        <property name="interceptorNames" value="after,before,round"></property>
        <!-- 如果为true表示jdk代理,要有接口,如果为false表示可以没有接口 -->
        <property name="optimize" value="false"></property>
    </bean>
           

运行结果

前置通知
环绕通知
生产iPhone 5S中
后置通知
           

异常通知

提供异常通知代理类

ExceptionProxy.java

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;
//该接口没有任何方法需要实现,需要自己写
public class ExceptionProxy implements ThrowsAdvice{
    /*public void afterThrowing(Throwable throwable) {

    }*/ 
    public void afterThrowing(Method m,Object[] os,Object obj,Throwable throwable) {
        System.out.println("异常通知");
    }
}
           

修改配置文件

<!-- 创建要代理的对象  一下的id随便起名字-->
    <bean id="phone" class="com.henu.phone.PhoneImpl"></bean>
    <!-- 创建增强对象,也就是通知对象 -->
    <bean id="before" class="com.henu.proxy.BeforeProxy"></bean>
    <bean id="after" class="com.henu.proxy.AfterProxy"></bean>
    <bean id="round" class="com.henu.proxy.RoundProxy"></bean>
    <bean id="throwss" class="com.henu.proxy.ExceptionProxy"></bean>
    <!-- 处理要代理的对象与增强对象之间的关系 -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 设置要代理的对象,也就是目标对象 -->
        <property name="target" ref="phone"></property>
        <!-- 设置拦截器,也就是增强对象 注意使用的是value属性-->
        <property name="interceptorNames" value="after,before,round,throwss"></property>
        <!-- 如果为true表示jdk代理,要有接口,如果为false表示可以没有接口 -->
        <property name="optimize" value="false"></property>
    </bean>
           

修改测试类

Test.java

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        PhoneImpl phone=(PhoneImpl)applicationContext.getBean("proxy");
        //要修改的地方
        phone.testException();
           

测试结果,打印出了 异常通知

前置通知
环绕通知
异常通知
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at com.henu.phone.PhoneImpl.testException(PhoneImpl.java:)
    at com.henu.phone.PhoneImpl$$FastClassBySpringCGLIB$$edc957f1.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:)
    at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:)
    at com.henu.proxy.RoundProxy.invoke(RoundProxy.java:)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:)
    at com.henu.phone.PhoneImpl$$EnhancerBySpringCGLIB$$d04c134a.testException(<generated>)
    at com.henu.proxy.Test.main(Test.java:)
           

最终通知

无论目标方法是否遇到异常都会执行,相当于代码中的finnaly

。。。这个以后补充。。。