Loading... <h2>前言</h2> <p>本篇文章主要介绍的是SpringBoot切面Aop的demo简单讲解。</p> <h2>SpringBoot Aop</h2> <p><strong>说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码。</strong></p> <h3>切面(Aop)</h3> <p><strong>一、概念</strong></p> <blockquote> <p>AOP(Aspect OrientedProgramming):面向切面编程,面向切面编程(也叫面向方面编程),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。</p> </blockquote> <p><strong>二、用途</strong></p> <blockquote> <p>日志记录,性能统计,安全控制,权限管理,事务处理,异常处理,资源池管理。</p> </blockquote> <p><strong>三、详解</strong></p> <p>1.切面(Aspect):<br> 官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如:AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中<a href="aop:aspect" target="_blank">aop:aspect</a>来配置。</p> <p>2.连接点(Joinpoint):<br> 程序执行过程中的某一行为,例如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。</p> <p>3.通知(Advice):<br> “切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如TestAspect。Advice共有如下5种类型:</p> <ul> <li>A 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。xml中在<a href="aop:aspect" target="_blank">aop:aspect</a>里面使用<a href="aop:before" target="_blank">aop:before</a>元素进行声明;例如,TestAspect中的doBefore方法。注解中使用@Before声明;例如,TestAnnotationAspect中的doBefore方法。</li> <li>B 后通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。xml中在<a href="aop:aspect" target="_blank">aop:aspect</a>里面使用<a href="aop:after" target="_blank">aop:after</a>元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行。注解中使用@After声明。</li> <li>C 返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。xml中在<a href="aop:aspect" target="_blank">aop:aspect</a>里面使用 元素进行声明。注解中使用@AfterReturning声明; </li> <li>D 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。xml中在<a href="aop:aspect" target="_blank">aop:aspect</a>里面使用<a href="aop:around" target="_blank">aop:around</a>元素进行声明。例如,TestAspect中的doAround方法。注解中使用@Around声明。</li> <li>E 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。xml中在<a href="aop:aspect" target="_blank">aop:aspect</a>里面使用<a href="aop:after-throwing" target="_blank">aop:after-throwing</a>元素进行声明。例如,TestAspect中的doThrowing方法。注解中使用@AfterThrowing声明。</li> <li>通知执行顺序:前置通知→环绕通知连接点之前→连接点执行→环绕通知连接点之后→返回通知→后通知 →(如果发生异常)异常通知→后通知。</li> </ul> <p>4.切入点(Pointcut)<br> 匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service.<em>.</em>(..))来决定。</p> <p><strong>注:以上的理论知识参考:<a href="https://www.cnblogs.com/yepei/p/4735298.html" target="_blank">https://www.cnblogs.com/yepei/p/4735298.html</a></strong></p> <h3>开发准备</h3> <p><strong>环境要求</strong></p> <p><strong>JDK</strong>:1.8</p> <p><strong>SpringBoot</strong>:2.2.6.RELEASE</p> <p>首先还是Maven的相关依赖,基本和普通springboot项目一样,就是多了<code>spring-boot-starter-aop</code>这jar的依赖。</p> <p><strong>pom.xml</strong>文件如下:</p> <pre><code><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.68</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <scope>test</scope> </dependency> </dependencies> </code></pre> <p><code>application.properties</code>的文件的配置:</p> <pre><code>banner.charset=UTF-8 server.tomcat.uri-encoding=UTF-8 spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true spring.http.encoding.force=true spring.messages.encoding=UTF-8 spring.application.name=springboot-aspect server.port=8180 </code></pre> <h3>代码编写</h3> <p>SpringBoot在使用切面的时候,只需要在自定义的一个切面类中加上 <code>@Aspect</code> 注解进行声明,然后在自定义切面的类方法中加上对应的注解即可。<br> 比如这里的示例我们想做一个请求响应的加解密切面处理,业务层只需关心代码逻辑实现,而不用关心请求参数和响应参数的加解密实现。那么首先我们需要自定义一个加解密的切面类,在该类添加<code>@Aspect</code>注解,然后在定义一个公共的切入点(Pointcut),指向需要处理的包,然后在定义一个前置通知(添加<code>@Before</code>注解)和后置通知(添加<code>@AfterReturning</code>)方法实现即可。</p> <p>这里我们就将切入点设置为控制层包里所有的请求。</p> <p><strong>切入点代码示例:</strong></p> <pre><code> @Pointcut("execution(public * com.pancm.web.*.*(..))") public void doOperation() { } </code></pre> <p>然后在定义一个前置通知,实现对请求参数的数据解密,这里我们就用User这个实体类的名称,对该数据进行解密。实际在运用是可以根据自身的情况来编写。</p> <p><strong>前置通知代码示例:</strong></p> <pre><code> @Before("doOperation()") public void before(JoinPoint joinPoint) throws Throwable{ Object[] objs = joinPoint.getArgs(); for (Object obj : objs) { User user =(User) obj; System.out.println("前置通知接受的参数:"+user); String name =base64DeStr(user.getName()); user.setName(name); } } </code></pre> <p>在编写完前置通知的方法之后,我们在编写后置通知的代码,这块基本和前置通知的一样,就是把返回的数据进行加密而已。</p> <p><strong>后置通知代码示例:</strong></p> <pre><code> @AfterReturning(returning = "object", pointcut = "doOperation()") public void doAfterReturning(Object object) { ResultBody resultBody = (ResultBody) object; String str =null; try { str=base64EnStr(resultBody.getResult()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } resultBody.setResult(str); System.out.println("后通知响应的参数:"+resultBody); } </code></pre> <p><strong>完整代码示例:</strong></p> <pre><code> @Aspect @Component public class ParamAspect { @Pointcut("execution(public * com.pancm.web.*.*(..))") public void doOperation() { } @Before("doOperation()") public void before(JoinPoint joinPoint) throws Throwable{ Object[] objs = joinPoint.getArgs(); for (Object obj : objs) { User user =(User) obj; System.out.println("前置通知接受的参数:"+user); String name =base64DeStr(user.getName()); user.setName(name); } } @AfterReturning(returning = "object", pointcut = "doOperation()") public void doAfterReturning(Object object) { ResultBody resultBody = (ResultBody) object; String str =null; try { str=base64EnStr(resultBody.getResult()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } resultBody.setResult(str); System.out.println("前置通知响应的参数:"+resultBody); } public String base64EnStr(String str) throws UnsupportedEncodingException { return Base64.getEncoder().encodeToString(str.getBytes("UTF-8")); } public static String base64DeStr(String encodeStr) throws UnsupportedEncodingException { byte[] decodeStr = Base64.getDecoder().decode(encodeStr); return new String(decodeStr, "UTF-8"); } </code></pre> <p><strong>实体类代码</strong></p> <pre><code> public class User { private Long id; private String name; private Integer age; //getter 和 setter 略 } </code></pre> <p>控制层z这块的代码和普通的一样,我们这里只需简单的做下处理即可,这里为了方便理解,没有编写service层和dao的代码。:</p> <p>** 控制层代码**</p> <pre><code> @RestController @RequestMapping(value = "/api") public class UserRestController { @GetMapping("/user") public ResultBody findByUser(User user) { System.out.println("用户查询接口请求的参数:"+user); ResultBody resultBody = new ResultBody(); List<User> userList =new ArrayList<>(); User user2=new User(); user2.setId(1L); user2.setName("xuwujing"); user2.setAge(18); userList.add(user2); resultBody.setCode("0"); resultBody.setResult(userList.toString()); System.out.println("用户查询接口响应的参数:"+resultBody); return resultBody; } } </code></pre> <p><strong>App 入口</strong></p> <p>和普通的SpringBoot项目基本一样!</p> <p><strong>代码如下:</strong></p> <pre><code> @SpringBootApplication public class AspectApp { public static void main( String[] args ) { SpringApplication.run(AspectApp.class, args); System.out.println("Aspect启动成功!"); } } </code></pre> <h3>功能测试</h3> <p>编写完代码之后,我们启动程序,因为是Get请求,在浏览器或者使用<a href="https://blog.csdn.net/qazwsxpcm/article/details/70578600" target="_blank">Postman</a>输入地址都可以进行测试,需要注意的是这里我们需要对name的值进行base64加密请求。</p> <p>输入:</p> <blockquote> <p><a href="http://localhost:8180/api/user?name=eHV3dWppbmc=" target="_blank">http://localhost:8180/api/user?name=eHV3dWppbmc=</a></p> </blockquote> <p>控制台打印:</p> <blockquote> <p>前置通知接受的参数:{"name":"eHV3dWppbmc="}<br> 用户查询接口请求的参数:{"name":"xuwujing"}<br> 用户查询接口响应的参数:{"code":"0","result":"[{"age":18,"id":1,"name":"xuwujing"}]"}<br> 后通知响应的参数:{"code":"0","result":"W3siYWdlIjoxOCwiaWQiOjEsIm5hbWUiOiJ4dXd1amluZyJ9XQ=="}</p> </blockquote> <p>请求响应参数:</p> <blockquote> <p>{<br> "code": "0",<br> "message": null,<br> "result": "W3siYWdlIjoxOCwiaWQiOjEsIm5hbWUiOiJ4dXd1amluZyJ9XQ=="<br> }</p> </blockquote> <p>示例图:<br> <img src="https://img-blog.csdnimg.cn/20200518160714223.png" class="aligncenter"><br> <img src="https://img-blog.csdnimg.cn/20200518160724904.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FhendzeHBjbQ==,size_16,color_FFFFFF,t_70" class="aligncenter"></p> <h2>其它</h2> <p>关于SpringBoot切面Aop的demo简单讲解的文章就讲解到这里了,如有不妥,欢迎指正!</p> <h3>项目地址</h3> <p>SpringBoot 的aop的项目工程地址:<br> <a href="https://github.com/xuwujing/springBoot-study/tree/master/springboot-aspect" target="_blank">https://github.com/xuwujing/springBoot-study/tree/master/springboot-aspect</a></p> <p>SpringBoot整个集合的地址:<br> <a href="https://github.com/xuwujing/springBoot-study" target="_blank">https://github.com/xuwujing/springBoot-study</a></p> <h3>SpringBoot整合系列的文章</h3> <ul> <li> <p><a href="http://www.panchengming.com/2018/02/28/pancm73/" target="_blank">springBoot配置文件的读取以及过滤器和拦截器的使用</a></p> </li> <li> <p><a href="http://www.panchengming.com/2018/01/10/pancm67/" target="_blank">SpringBoot的Restful风格的服务</a></p> </li> <li> <p><a href="http://www.panchengming.com/2018/04/27/pancm81/" target="_blank">SpringBoot+Mybatis+ Druid+PageHelper实现多数据源并分页</a></p> </li> <li> <p><a href="http://www.panchengming.com/2018/05/07/pancm82/" target="_blank">SpringBoot整合ElasticSearch实现多版本的兼容</a></p> </li> <li> <p><a href="http://www.panchengming.com/2018/05/10/pancm83/" target="_blank">SpringBoot整合Kafka和Storm</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/9297165.html" target="_blank">SpringBoot整合Jsp和Thymeleaf</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/9321395.html" target="_blank">SpringBoot整合Netty并使用Protobuf进行数据传输</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/9471802.html" target="_blank">SpringBoot简单打包部署</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/10835571.html" target="_blank">SpringBoot整合Redis使用Restful风格实现CRUD功能</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/10933082.html" target="_blank">SpringBoot优雅的全局异常处理</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/10945698.html" target="_blank">SpringBoot项目实现文件上传和邮件发送</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/11042674.html" target="_blank">SpringBoot整合Swagger和Actuator</a></p> </li> <li> <p><a href="https://www.cnblogs.com/xuwujing/p/11184162.html" target="_blank">SpringBoot事物Transaction实战讲解教程</a></p> </li> </ul> <h3>音乐推荐</h3> <blockquote> <p>翩若惊鸿,婉若游龙,荣曜秋菊,华茂春松。仿佛兮若轻云之蔽月,飘飘兮若流风之回雪。远而望之,皎若太阳升朝霞;迫而察之,灼若芙蕖出渌波。--网易云网友评论</p> </blockquote> <p>原创不易,如果感觉不错,希望给个推荐!您的支持是我写作的最大动力!<br> 版权声明:<br> 作者:虚无境<br> 博客园出处:<a href="http://www.cnblogs.com/xuwujing" target="_blank">http://www.cnblogs.com/xuwujing</a><br> CSDN出处:<a href="http://blog.csdn.net/qazwsxpcm" target="_blank">http://blog.csdn.net/qazwsxpcm</a> <br> 个人博客出处:<a href="http://www.panchengming.com" target="_blank">http://www.panchengming.com</a></p><hr class="content-copyright" style="margin-top:50px" /><blockquote class="content-copyright" style="font-style:normal"><p class="content-copyright">版权属于:撩人的无眠兔</p><p class="content-copyright">本文链接:<a class="content-copyright" href="https://www.wumiantu.com/technology/2qGN0S.html">https://www.wumiantu.com/technology/2qGN0S.html</a></p><p class="content-copyright">转载时须注明出处及本声明</p></blockquote> © 允许规范转载 赞赏 看了文章不打款? ×Close 赞赏作者 扫一扫支付 支付宝支付 微信支付