iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Spring AOP标签怎么使用
  • 399
分享到

Spring AOP标签怎么使用

2023-06-02 17:06:05 399人浏览 泡泡鱼
摘要

这篇文章主要介绍“spring aop标签怎么使用”,在日常操作中,相信很多人在Spring AOP标签怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring AOP标签怎么使用”的疑惑有所帮助!

这篇文章主要介绍“spring aop标签怎么使用”,在日常操作中,相信很多人在Spring AOP标签怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring AOP标签怎么使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1
有一个接口Dao有insert、delete、update三个方法,在insert与update被调用的前后,打印调用前的毫秒数与调用后的毫秒数
首先定义一个Dao接口:

public interface Dao {     public void insert();         public void delete();         public void update();     }

然后定义一个实现类DaoImpl:

public class DaoImpl implements Dao {     @Override    public void insert() {        System.out.println("DaoImpl.insert()");    }     @Override    public void delete() {        System.out.println("DaoImpl.delete()");    }     @Override    public void update() {        System.out.println("DaoImpl.update()");    }     }

最原始的写法,我要在调用insert()与update()方法前后分别打印时间,就只能定义一个新的类包一层,在调用insert()方法与update()方法前后分别处理一下,新的类我命名为ServiceImpl,其实现为:

public class ServiceImpl {     private Dao dao = new DaoImpl();         public void insert() {        System.out.println("insert()方法开始时间:" + System.currentTimeMillis());        dao.insert();        System.out.println("insert()方法结束时间:" + System.currentTimeMillis());    }         public void delete() {        dao.delete();    }         public void update() {        System.out.println("update()方法开始时间:" + System.currentTimeMillis());        dao.update();        System.out.println("update()方法结束时间:" + System.currentTimeMillis());    }     }

这是最原始的写法,这种写法的缺点也是一目了然:

方法调用前后输出时间的逻辑无法复用,如果有别的地方要增加这段逻辑就得再写一遍
如果Dao有其它实现类,那么必须新增一个类去包装该实现类,这将导致类数量不断膨胀
使用装饰器模式
接着我们使用上设计模式,先用装饰器模式,看看能解决多少问题。装饰器模式的核心就是实现Dao接口并持有Dao接口的引用,我将新增的类命名为LogDao,其实现为:

public class LogDao implements Dao {     private Dao dao;         public LogDao(Dao dao) {        this.dao = dao;    }     @Override    public void insert() {        System.out.println("insert()方法开始时间:" + System.currentTimeMillis());        dao.insert();        System.out.println("insert()方法结束时间:" + System.currentTimeMillis());    }     @Override    public void delete() {        dao.delete();    }     @Override    public void update() {        System.out.println("update()方法开始时间:" + System.currentTimeMillis());        dao.update();        System.out.println("update()方法结束时间:" + System.currentTimeMillis());    } }

在使用的时候,可以使用”Dao dao = new LogDao(new DaoImpl())”的方式,这种方式的优点为:

透明,对调用方来说,它只知道Dao,而不知道加上了日志功能
类不会无限膨胀,如果Dao的其它实现类需要输出日志,只需要向LogDao的构造函数中传入不同的Dao实现类即可
不过这种方式同样有明显的缺点,缺点为:

输出日志的逻辑还是无法复用
输出日志的逻辑与代码有耦合,如果我要对delete()方法前后同样输出时间,需要修改LogDao
但是,这种做法相比最原始的代码写法,已经有了很大的改进。

使用代理模式
接着我们使用代理模式尝试去实现最原始的功能,使用代理模式,那么我们就要定义一个InvocationHandler,我将它命名为LogInvocationHandler,其实现为:

public class LogInvocationHandler implements InvocationHandler {     private Object obj;         public LogInvocationHandler(Object obj) {        this.obj = obj;    }         @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String methodName = method.getName();        if ("insert".equals(methodName) || "update".equals(methodName)) {            System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());            Object result = method.invoke(obj, args);            System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());                         return result;        }                 return method.invoke(obj, args);    }     }

其调用方式很简单,我写一个main函数:

public static void main(String[] args) {    Dao dao = new DaoImpl();             Dao proxyDao = (Dao)Proxy.newProxyInstance(LogInvocationHandler.class.getClassLoader(), new Class<?>[]{Dao.class}, new LogInvocationHandler(dao));             proxyDao.insert();    System.out.println("----------分割线----------");    proxyDao.delete();    System.out.println("----------分割线----------");    proxyDao.update();}

结果就不演示了,这种方式的优点为:

输出日志的逻辑被复用起来,如果要针对其他接口用上输出日志的逻辑,只要在newProxyInstance的时候的第二个参数增加Class<?>数组中的内容即可
这种方式的缺点为:

jdk提供的动态代理只能针对接口做代理,不能针对类做代理
代码依然有耦合,如果要对delete方法调用前后打印时间,得在LogInvocationHandler中增加delete方法的判断
使用CGLIB
接着看一下使用CGLIB的方式,使用CGLIB只需要实现MethodInterceptor接口即可:

public class DaoProxy implements MethodInterceptor {     @Override    public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {        String methodName = method.getName();                 if ("insert".equals(methodName) || "update".equals(methodName)) {            System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());            proxy.invokeSuper(object, objects);            System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());                         return object;        }                 proxy.invokeSuper(object, objects);        return object;    } }

代码调用方式为:

public static void main(String[] args) {    DaoProxy daoProxy = new DaoProxy();         Enhancer enhancer = new Enhancer();    enhancer.setSuperclass(DaoImpl.class);    enhancer.setCallback(daoProxy);             Dao dao = (DaoImpl)enhancer.create();    dao.insert();    System.out.println("----------分割线----------");    dao.delete();    System.out.println("----------分割线----------");    dao.update();}

使用CGLIB解决了JDK的Proxy无法针对类做代理的问题,但是这里要专门说明一个问题:使用装饰器模式可以说是对使用原生代码的一种改进,使用Java代理可以说是对于使用装饰器模式的一种改进,但是使用CGLIB并不是对于使用Java代理的一种改进。

前面的可以说改进是因为使用装饰器模式比使用原生代码更好,使用Java代理又比使用装饰器模式更好,但是Java代理与CGLIb的对比并不能说改进,因为使用CGLIB并不一定比使用Java代理更好,这两种各有优缺点,像Spring框架就同时支持Java Proxy与CGLIB两种方式。

从目前看来代码又更好了一些,但是我认为还有两个缺点:

无论使用Java代理还是使用CGLIB,编写这部分代码都稍显麻烦
代码之间的耦合还是没有解决,像要针对delete()方法加上这部分逻辑就必须修改代码

使用AOP

最后来看一下使用AOP的方式,首先定义一个时间处理类,我将它命名为TimeHandler:

public class TimeHandler {         public void printTime(ProceedingJoinPoint pjp) {        Signature signature = pjp.getSignature();        if (signature instanceof MethodSignature) {            MethodSignature methodSignature = (MethodSignature)signature;            Method method = methodSignature.getMethod();            System.out.println(method.getName() + "()方法开始时间:" + System.currentTimeMillis());                         try {                pjp.proceed();                System.out.println(method.getName() + "()方法结束时间:" + System.currentTimeMillis());            } catch (Throwable e) {                             }        }    }     }

到第8行的代码与第12行的代码分别打印方法开始执行时间与方法结束执行时间。我这里写得稍微复杂点,使用了aop:around的写法,其实也可以拆分为aop:before与aop:after两种,这个看个人喜好。

这里多说一句,切面方法printTime本身可以不用定义任何的参数,但是有些场景下需要获取调用方法的类、方法签名等信息,此时可以在printTime方法中定义JointPoint,Spring会自动将参数注入,可以通过JoinPoint获取调用方法的类、方法签名等信息。由于这里我用的aop:around,要保证方法的调用,这样才能在方法调用前后输出时间,因此不能直接使用JoinPoint,因为JoinPoint没法保证方法调用。此时可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保证方法调用,但是要注意一点,ProceedingJoinPoint只能和aop:around搭配,换句话说,如果aop.xml中配置的是aop:before,然后printTime的方法参数又是ProceedingJoinPoint的话,Spring容器启动将报错。

接着看一下aop.xml的配置:

<?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: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/aop  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">     <bean id="daoImpl" class="org.xrq.spring.action.aop.DaoImpl" />    <bean id="timeHandler" class="org.xrq.spring.action.aop.TimeHandler" />     <aop:config>        <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.spring.action.aop.Dao.*(..))" />        <aop:aspect id="time" ref="timeHandler">            <aop:before method="printTime" pointcut-ref="addAllMethod" />            <aop:after method="printTime" pointcut-ref="addAllMethod" />        </aop:aspect>    </aop:config>     </beans>

我不大会写expression,也懒得去百度了,因此这里就拦截Dao下的所有方法了。测试代码很简单:
=

public class AopTest {

    @Test    @SuppressWarnings("resource")    public void testAop() {        ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml");                 Dao dao = (Dao)ac.getBean("daoImpl");        dao.insert();        System.out.println("----------分割线----------");        dao.delete();        System.out.println("----------分割线----------");        dao.update();    }     }

AOP总结

结果就不演示了。到此我总结一下使用AOP的几个优点:

切面的内容可以复用,比如TimeHandler的printTime方法,任何地方需要打印方法执行前的时间与方法执行后的时间,都可以使用TimeHandler的printTime方法
避免使用Proxy、CGLIB生成代理,这方面的工作全部框架去实现,开发者可以专注于切面内容本身
代码与代码之间没有耦合,如果拦截的方法有变化修改配置文件即可
下面用一张图来表示一下AOP的作用:

我们传统的编程方式是垂直化的编程,即A–>B–>C–>D这么下去,一个逻辑完毕之后执行另外一段逻辑。但是AOP提供了另外一种思路,它的作用是在业务逻辑不知情(即业务逻辑不需要做任何的改动)的情况下对业务代码的功能进行增强,这种编程思想的使用场景有很多,例如事务提交、方法执行之前的权限检测、日志打印、方法调用事件等等。

AOP使用场景举例
上面的例子纯粹为了演示使用,为了让大家更加理解AOP的作用,这里以实际场景作为例子。

第一个例子,我们知道mybatis的事务默认是不会自动提交的,因此在编程的时候我们必须在增删改完毕之后调用sqlSession的commit()方法进行事务提交,这非常麻烦,下面利用AOP简单写一段代码帮助我们自动提交事务(这段代码我个人测试过可用):

public class TransactionHandler {     public void commit(JoinPoint jp) {        Object obj = jp.getTarget();        if (obj instanceof MailDao) {            Signature signature = jp.getSignature();            if (signature instanceof MethodSignature) {                SqlSession sqlSession = SqlSessionThrealLocalUtil.getSqlSession();                                                MethodSignature methodSignature = (MethodSignature)signature;                Method method = methodSignature.getMethod();                                  String methodName = method.getName();                if (methodName.startsWith("insert") || methodName.startsWith("update") || methodName.startsWith("delete")) {                    sqlSession.commit();                }                                 sqlSession.close();            }        }    }     }

这种场景下我们要使用的aop标签为aop:after,即切在方法调用之后。

这里我做了一个SqlSessionThreadLocalUtil,每次打开会话的时候,都通过SqlSessionThreadLocalUtil把当前会话SqlSession放到ThreadLocal中,看到通过TransactionHandler,可以实现两个功能:

insert、update、delete操作事务自动提交
对SqlSession进行close(),这样就不需要在业务代码里面关闭会话了,因为有些时候我们写业务代码的时候会忘记关闭SqlSession,这样可能会造成内存句柄的膨胀,因此这部分切面也一并做了
整个过程,业务代码是不知道的,而TransactionHandler的内容可以充分再多处场景下进行复用。

第二个例子是权限控制的例子,不管是从安全角度考虑还是从业务角度考虑,我们在开发一个WEB系统的时候不可能所有请求都对所有用户开放,因此这里就需要做一层权限控制了,大家看AOP作用的时候想必也肯定会看到AOP可以做权限控制,这里我就演示一下如何使用AOP做权限控制。我们知道原生的Spring mvc,Java类是实现Controller接口的,基于此,利用AOP做权限控制的大致代码如下(这段代码纯粹就是一段示例,我构建的Maven工程是一个普通的Java工程,因此没有验证过):

public class PermissionHandler {     public void hasPermission(JoinPoint jp) throws Exception {        Object obj = jp.getTarget();                 if (obj instanceof Controller) {            Signature signature = jp.getSignature();            MethodSignature methodSignature = (MethodSignature)signature;                         // 获取方法签名            Method method = methodSignature.getMethod();            // 获取方法参数            Object[] args = jp.getArgs();                         // Controller中唯一一个方法的方法签名ModelAndView handleRequest(httpservletRequest request, HttpServletResponse response) throws Exception;            // 这里对这个方法做一层判断            if ("handleRequest".equals(method.getName()) && args.length == 2) {                Object firstArg = args[0];                if (obj instanceof HttpServletRequest) {                    HttpServletRequest request = (HttpServletRequest)firstArg;                    // 获取用户id                    long userId = Long.parseLong(request.getParameter("userId"));                    // 获取当前请求路径                    String requestUri = request.getRequestURI();                                         if(!PermissionUtil.hasPermission(userId, requestUri)) {                        throw new Exception("没有权限");                    }                }            }        }             }     }

毫无疑问这种场景下我们要使用的aop标签为aop:before。这里我写得很简单,获取当前用户id与请求路径,根据这两者,判断该用户是否有权限访问该请求,大家明白意思即可。

到此,关于“Spring AOP标签怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: Spring AOP标签怎么使用

本文链接: https://www.lsjlt.com/news/230614.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring AOP标签怎么使用
    这篇文章主要介绍“Spring AOP标签怎么使用”,在日常操作中,相信很多人在Spring AOP标签怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring AOP标签怎么使用”的疑惑有所帮助!...
    99+
    2023-06-02
  • Spring之Bean标签怎么使用
    今天小编给大家分享一下Spring之Bean标签怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Bean标签基本配置用...
    99+
    2023-07-02
  • Spring P标签的使用详解
    目录Spring P标签的使用本例设计对象Topic、Speech和Speakerspring配置p标签问题今天学习spring遇到这样的一个问题解决方法如下Spring P标签的使...
    99+
    2024-04-02
  • Java的Spring AOP怎么用
    这篇文章主要介绍Java的Spring AOP怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!什么是AOP&作用AOP 为 Aspect Oriented Programming 的缩写,意思为...
    99+
    2023-06-29
  • @Around注解怎么在Spring AOP中使用
    这期内容当中小编将会给大家带来有关@Around注解怎么在Spring AOP中使用,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。@Around注解可以用来在调用一个具体方法前和调用后来完成一些具体的任务...
    99+
    2023-06-06
  • POM.xml标签怎么使用
    这篇文章主要介绍“POM.xml标签怎么使用”,在日常操作中,相信很多人在POM.xml标签怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”POM.xml标签怎么使用”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-04
  • noscript标签怎么使用
    本文小编为大家详细介绍“noscript标签怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“noscript标签怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Javascript 的日益强大使我们...
    99+
    2023-07-05
  • html的B标签和Strong标签怎么使用
    本篇内容介绍了“html的B标签和Strong标签怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一...
    99+
    2024-04-02
  • HTML标签怎么使用
    这篇文章主要讲解了“HTML标签怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“HTML标签怎么使用”吧!HTML的全称为超文本标记语言,是一种标记语言。它包括一系列标签.通过这些标签...
    99+
    2023-06-27
  • FreeMarker标签怎么使用
    本篇内容介绍了“FreeMarker标签怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在前台模板使用FreeMarker标签获取数据...
    99+
    2023-06-26
  • HTML5的audio标签和video标签怎么使用
    这篇文章主要介绍了HTML5的audio标签和video标签怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇HTML5的audio标签和video标签怎么使用文章都会有所...
    99+
    2024-04-02
  • spring bean标签中的init-method和destroy-method怎么使用
    这篇文章主要介绍了spring bean标签中的init-method和destroy-method怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇spring bean标签中的ini...
    99+
    2023-07-06
  • 如何使用Spring自定义Xml标签
    目录前言正文自定义NameSpaceHandler自定义schemaParserDecorator总结前言 在早期基于Xml配置的Spring Mvc项目中,我们往往会使用<...
    99+
    2024-04-02
  • html div标签怎么使用
    这篇文章主要介绍“html div标签怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“html div标签怎么使用”文章能帮助大家解决问题。 ...
    99+
    2024-04-02
  • CSS div标签怎么使用
    这篇文章主要介绍“CSS div标签怎么使用”,在日常操作中,相信很多人在CSS div标签怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”CSS div标签怎么使用”...
    99+
    2024-04-02
  • 使用SpringBoot怎么对Spring AOP进行集成
    今天就跟大家聊聊有关使用SpringBoot怎么对Spring AOP进行集成,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。需要的jar包添加到工程里。新增Maven依赖如下:<...
    99+
    2023-05-31
    springboot spring aop
  • uniapp怎么使用audio标签
    随着移动互联网的发展,音频相关应用也越来越受到用户的欢迎,比如音乐播放器、语音聊天、语音识别等等。而使用uniapp开发这些音频应用是非常方便的,其中有一个重要的组件就是<audio>标签。本文将介绍uniapp中如何使用<...
    99+
    2023-05-14
  • 在Spring中AOP怎么使用注解来实现
    这期内容当中小编将会给大家带来有关在Spring中AOP怎么使用注解来实现,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。spring对AOP的实现提供了很好的支持。下面我们就使用Spring的注解来完成A...
    99+
    2023-05-31
    spring aop
  • Spring AOP怎么使用切入点创建通知
    这篇文章主要介绍“Spring AOP怎么使用切入点创建通知”,在日常操作中,相信很多人在Spring AOP怎么使用切入点创建通知问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解...
    99+
    2024-04-02
  • 怎么使用Spring AOP预处理Controller的参数
    这篇文章主要为大家展示了“怎么使用Spring AOP预处理Controller的参数”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“怎么使用Spring AOP预处理Controller的参数”...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作