文档章节

Spring AOP 创建增强类

o
 onedotdot
发布于 01/27 10:28
字数 2005
阅读 99
收藏 0

AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强:

     1)前置增强:org.springframework.aop.BeforeAdvice 代表前置增强,因为Spring 只支持方法级的增强,所有MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定义的;
     2)后置增强:org.springframework.aop.AfterReturningAdvice 代表后增强,表示在目标方法执行后实施增强;
     3)环绕增强:org.aopalliance.intercept.MethodInterceptor 代表环绕增强,表示在目标方法执行前后实施增强;
     4)异常抛出增强:org.springframework.aop.ThrowsAdvice 代表抛出异常增强,表示在目标方法抛出异常后实施增强;
     5)引介增强:org.springframework.aop.IntroductionInterceptor 代表引介增强,表示在目标类中添加一些新的方法和属性。
 
1、前置增强
    模拟服务员向顾客表示欢迎和对顾客提供服务。
Waiter接口:
package com.yyq.advice;
public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}

NaiveWaiter服务类:

复制代码
package com.yyq.advice;
public class NaiveWaiter implements Waiter {
    @Override
    public void greetTo(String name) {
        System.out.println("greet to " + name + "...");
    }
    @Override
    public void serveTo(String name) {
        System.out.println("serving to " + name + "...");
    }
}
复制代码

前置增强实现类:

复制代码
package com.yyq.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        String clientName = (String) objects[0];
        System.out.println("How are you ! Mr." + clientName + ".");
    }
}
复制代码

测试方法:

复制代码
@Test
    public void testBeforeAdvice(){
        Waiter target = new NaiveWaiter();
        BeforeAdvice advice = new GreetingBeforeAdvice();
        ProxyFactory pf = new ProxyFactory(); //Spring提供的代理工厂
        pf.setTarget(target);    //设置代理目标
        pf.addAdvice(advice);
        Waiter proxy = (Waiter)pf.getProxy();    //生成代理实例
        proxy.greetTo("John");
        proxy.serveTo("Tom");
    }
复制代码
输出结果:
How are you ! Mr.John.
greet to John...
How are you ! Mr.Tom.
serving to Tom...
 
在Spring中配置:beans.xml
复制代码
<bean id="greetingAdvice" class="com.yyq.advice.GreetingBeforeAdvice"/>
<bean id="target" class="com.yyq.advice.NaiveWaiter"/>

<bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
     p:proxyInterfaces="com.yyq.advice.Waiter"
     p:interceptorNames="greetingAdvice"
     p:target-ref="target"/>
复制代码

  ProxyFactoryBean 是FactoryBean接口的实现类。

    · target:代理的目标对象;
    · proxyInterfaces:代理所实现的接口,可以是多个接口。该属性还有一个别名属性interfaces;
    · interceptorNames:需要织入目标对象的Bean列表,采用Bean的名称指定。这些Bean必须是实现了org.aopalliance.intercept.Method 或 org.springframework.aop.Advisor的Bean,配置中的顺序对应调用的顺序;
    · singleton:返回的代理是否是单实例,默认为单实例;
    · optimize:当设置为true时,强制使用CGLib代理。对于singleton的代理,我们推荐使用CGLib,对于其他作用域类型的代理,最好使用JDK代理。原因是CGLib创建代理时速度慢,而创建出的代理对象运行效率较高,而使用JDK代理的表现正好相反;
    · proxyTargetClass:是否对类进行代理(而不是对接口进行代理),设置为true时,使用CGLib代理。
测试方法:
复制代码
@Test
    public void testBeforeAdvice2(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter1");
        waiter.greetTo("Joe");
    }
复制代码
输出结果:
How are you ! Mr.Joe.
greet to Joe...
 
2、后置增强
    后置增强在目标类方法调用后执行。模拟服务员在每次服务后使用礼貌用语。
GreetingAfterAdvice后置增强实现类:
复制代码
package com.yyq.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class GreetingAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o2) throws Throwable {
        System.out.println("Please enjoy yourself.");
    }
}
复制代码

 在beans.xml文件添加后置增强:

复制代码
<bean id="greetingBefore" class="com.yyq.advice.GreetingBeforeAdvice"/>
<bean id="greetingAfter" class="com.yyq.advice.GreetingAfterAdvice"/>
<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
     p:proxyInterfaces="com.yyq.advice.Waiter"
     p:interceptorNames="greetingBefore,greetingAfter"
     p:target-ref="target"/>
复制代码

测试方法:

复制代码
 @Test
    public void testBeforeAndAfterAdvice(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter2");
        waiter.greetTo("Joe");
    }
复制代码
结果输出:
How are you ! Mr.Joe.
greet to Joe...
Please enjoy yourself.
 
3、环绕增强
    环绕增强允许在目标类方法调用前后织入横切逻辑,它综合实现了前置、后置增强两者的功能。
GreetingInterceptor环绕增强实现类:
复制代码
package com.yyq.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class GreetingInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object[] args = methodInvocation.getArguments();
        String clientName = (String) args[0];
        System.out.println("Hi,Mr " + clientName + ".");
        Object obj = methodInvocation.proceed();
        System.out.println("Please enjoy yourself~");
        return obj;
    }
}
复制代码

   Spring 直接使用AOP联盟所定义的MethodInterceptor作为环绕增强的接口。该接口拥有唯一的接口方法 Object invoke(MethodInvocation invocation), MethodInvocation不但封装目标方法及其入参数组,还封装了目标方法所在的实例对象,通过MethodInvocation的getArguments()可以获取目标方法的入参数组,通过proceed()反射调用目标实例相应的方法。

在beans.xml文件添加环绕增强:
<bean id="greetingAround" class="com.yyq.advice.GreetingInterceptor"/>
    <bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.yyq.advice.Waiter"
          p:interceptorNames="greetingAround"
          p:target-ref="target"/>

测试方法:

复制代码
  @Test
    public void testAroundAdvice(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter3");
        waiter.greetTo("Joe");
    }
复制代码
结果输出:
Hi,Mr Joe.
greet to Joe...
Please enjoy yourself~
 
4、异常抛出增强
    异常抛出增强最适合的应用场景是事务管理,当参与事务的某个Dao发送异常时,事务管理器就必须回滚事务。
Forum业务类:
复制代码
package com.yyq.advice;
public class Forum {
    private int forumId;
    public int getForumId() {
        return forumId;
    }
    public void setForumId(int forumId) {
        this.forumId = forumId;
    }
}
复制代码

 ForumService业务类:

复制代码
package com.yyq.advice;
import java.sql.SQLException;
public class ForumService {
    public void removeForum(int forumId){
        System.out.println("removeForum....");
        throw new RuntimeException("运行异常");
    }
    public void updateForum(Forum forum)throws Exception{
        System.out.println("updateForum");
        throw new SQLException("数据更新操作异常。");
    }
}
复制代码

TransactionManager异常抛出增强实现类:

复制代码
package com.yyq.advice;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class TransactionManager implements ThrowsAdvice {
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
        System.out.println("----------------");
        System.out.println("method:" + method.getName());
        System.out.println("抛出异常:" + ex.getMessage());
        System.out.println("成功回滚事务。");
    }
}
复制代码
    ThrowAdvice异常抛出增强接口没有定义任何方法,它是一个标识接口,在运行期Spring使用反射的机制自行判断,我们采用以下签名形式定义异常抛出的增强方法:void afterThrowing(Mehod method, Object[] args, Object target, Throwable);方法名必须为afterThrowing,方法入参规定,前三个参数是可选的,要么三个入参提供,要么不提供,最后一个入参是Throwable或者子类。
在beans.xml文件添加异常抛出增强:
复制代码
<bean id="transactionManager" class="com.yyq.advice.TransactionManager"/>
    <bean id="forumServiceTarget" class="com.yyq.advice.ForumService"/>
    <bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="transactionManager"
          p:target-ref="forumServiceTarget"
          p:proxyTargetClass="true"/>
复制代码

 测试方法:

复制代码
@Test
    public void testThrowsAdvice(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        ForumService fs = (ForumService)ctx.getBean("forumService");
        try{
            fs.removeForum(10);
        } catch (Exception e) {}
        try{
            fs.updateForum(new Forum());
        } catch (Exception e) {}
    }
复制代码
结果输出:
----------------
method:removeForum
抛出异常:运行异常
成功回滚事务。
updateForum
----------------
method:updateForum
抛出异常:数据更新操作异常。
成功回滚事务。
 
5、引介增强
        引介增强为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的。通过引介增强,我们可以为目标类添加一个接口的实现,即原来目标类未实现某个接口,通过引介增强可以为目标类创建实现某个接口的代理。Spring定义了引介增强接口IntroductionInterceptor,该接口没有定义任何的方法,Spring为该接口提供了DelegatingIntroductionInterceptor实现类。
Monitorable:用于标识目标类是否支持性能监视的接口
package com.yyq.advice;
public interface Monitorable {
    void setMonitorActive(boolean active);
}

 ControllablePerformanceMonitor 为引介增强实现类:

复制代码
package com.yyq.advice;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class ControllablePerformanceMonitor extends DelegatingIntroductionInterceptor implements Monitorable {
    private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();
    @Override
    public void setMonitorActive(boolean active) {
        MonitorStatusMap.set(active);
    }
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object obj = null;
        if (MonitorStatusMap.get() != null && MonitorStatusMap.get()) {
            PerformanceMonitor.begin(mi.getClass().getName() + "." + mi.getMethod().getName());
            obj = super.invoke(mi);
            PerformanceMonitor.end();
        } else {
            obj = super.invoke(mi);
        }
        return obj;
    }
}
复制代码

PerformanceMonitor监视类:

复制代码
package com.yyq.advice;

public class PerformanceMonitor {
    private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
    public static void begin(String method) {
        System.out.println("begin monitor...");
        MethodPerformance mp = new MethodPerformance(method);
        performanceRecord.set(mp);
    }
    public static void end(){
        System.out.println("end monitor...");
        MethodPerformance mp = performanceRecord.get();
        mp.printPerformance();
    }
}
复制代码

MethodPerformance记录性能信息:

复制代码
public class MethodPerformance {
    private long begin;
    private long end;
    private String serviceMethod;
    public MethodPerformance(String serviceMethod){
        this.serviceMethod = serviceMethod;
        this.begin = System.currentTimeMillis();
    }
    public void printPerformance(){
        end = System.currentTimeMillis();
        long elapse = end - begin;
        System.out.println(serviceMethod + "花费" + elapse + "毫秒。");
    }
}
复制代码

在beans.xml文件添加引介增强:

复制代码
<bean id="pmonitor" class="com.yyq.advice.ControllablePerformanceMonitor"/>
<bean id="forumServiceImplTarget" class="com.yyq.advice.ForumServiceImpl"/>
<bean id="forumService2" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interfaces="com.yyq.advice.Monitorable"
          p:interceptorNames="pmonitor"
          p:target-ref="forumServiceImplTarget"
          p:proxyTargetClass="true"/>
复制代码

 

测试方法:
复制代码
@Test
    public void testIntroduce(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        ForumServiceImpl forumServiceImpl = (ForumServiceImpl)ctx.getBean("forumService2");
        forumServiceImpl.removeForum(23);
        forumServiceImpl.removeTopic(1023);
        Monitorable monitorable = (Monitorable)forumServiceImpl;
        monitorable.setMonitorActive(true);
        forumServiceImpl.removeForum(22);
        forumServiceImpl.removeTopic(1023);
    }
复制代码
结果输出:
模拟删除Forum记录:23
模拟删除Topic记录:1023
begin monitor...
模拟删除Forum记录:22
end monitor...
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeForum花费40毫秒。
begin monitor...
模拟删除Topic记录:1023
end monitor...
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeTopic花费21毫秒。
 

本文转载自:https://www.cnblogs.com/yangyquin/p/5463697.html

o
粉丝 11
博文 434
码字总数 16064
作品 0
朝阳
私信 提问
加载中

评论(0)

Spring中的AOP(二)——AOP基本概念和Spring对AOP的支持

AOP的基本概念 AOP从运行的角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中的各个步骤,希望以更好的方式来组合业务逻辑的各个步骤。AOP框架并不与特定的代码耦合,AOP...

摆渡者
2014/03/17
2.2K
3
Spring AOP 创建代理的源码解析

相关文章 Spring AOP 注解方式源码解析 Spring AOP 功能使用详解 Spring 的 getBean 方法源码解析 Spring bean 创建过程源码解 Spring 中 bean 注册的源码解析 前言 在上篇文章 Spring AOP 注...

TSMYK
2019/01/01
252
0
spring 学习(三):aop 学习

spring 学习(三):aop 学习 aop 概念 1 aop:面向切面(方面)编程,扩展功能不修改源代码实现 2 AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码 3 aop底层使用动态代理实现 (1...

希希里之海
2018/08/19
0
0
day33_Spring学习笔记_02

一、AOP 1.1、AOP介绍 1.1.1、什么是AOP? 在软件业,AOP为Aspect Oriented Programming的缩写,意为:,通过和实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开...

黑泽明军
2018/08/03
0
0
哪些方法不能够实施Spring AOP事务

哪些方法不能够实施Spring AOP事务 由于Spring事务管理是基于接口代理或动态字节码技术。通过AOP实施事务增强。 基于接口动态代理的AOP事务增强,接口方法必须都是public的。 实现类的方法也...

hyssop
2014/12/06
122
0

没有更多内容

加载失败,请刷新页面

加载更多

 企业信息平台的快速搭建,框架如何选?

Web端开发框架如何选 目前,大部分的企业信息集成系统都在web端运行,而搭建框架的选择对一个企业的发展至关重要,不过其最终目的都是要符合企业发展逻辑,助力企业战略的实施。 而在框架的选...

我想造火箭
17分钟前
25
0
安装mysql 实操截图

前言: CentOS 7 版本将MySQL数据库软件从默认的程序列表中移除,用MariaDB代替了,MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可。开发这个分支的原因之...

冥焱
21分钟前
57
0
FecMall 多商户分销系统 - 价格公式计算

FecMall Fecbdc 分销价格公式计算 本章详解讲述分销平台的各个价格,以及相应的设置,本章节非常重要,贯穿分销系统的整个流程,请仔细阅读 官网: http://www.fecmall.com/ 业务逻辑设计 系...

FecShop
22分钟前
33
0
Java Web 学习笔记(7)

文件下载 package com.janeroad.servlet;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.......

JaneRoad
26分钟前
41
0
如何在JavaScript中更改span元素的文本

如果我有跨度,请说: <span id="myspan"> hereismytext </span> 如何使用JavaScript将“ hereismytext”更改为“ newtext”? #1楼 对于现代浏览器,您应该使用: document.getElementByI......

技术盛宴
28分钟前
46
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部