首先我们先来介绍一下AOP:

AOP(Aspect Orient Programming),面向切面编程,是面向对象编程OOP的一种补充。面向对象编程是从静态角度考虑程序的结构,面向切面编程是从动态的角度考虑程序运行过程。

AOP底层,就是采用动态代理模式实现的。采用两种代理:JDK的动态代理,与CGLIB的动态代理。JDK的动态代理是面向接口的,CGLIB既可以实现有接口的,又可以实现没有接口的。(对动态代理不了解的可以看看我的其关于动态代理的介绍)

面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP容器的功能将切面植入到主业务逻辑中。所谓交叉业务逻辑是指:通用的,与主业务逻辑无关的代码,如安全检查,事务日志等。

Spring的AOP的几种用法:

通知:即我们的切面方法

  1. 前置通知
  2. 后置通知
  3. 环绕通知
  4. 异常通知

(一)前置通知

所谓前置通知,就是这个切面方法在我们的主业务方法之前执行。

首先我们先写一个目标接口:

  1. //目标接口
  2. public interface SomeServices {
  3. String doFirst();
  4. void doSecond();
  5. }

  1. //接口实现类,也就是主业务方法类
  2. public class SomeServiceImp implements SomeServices{
  3. @Override
  4. public String doFirst() {
  5. System.out.println("print first");
  6. return null;
  7. }
  8. @Override
  9. public void doSecond() {
  10. System.out.println("print second");
  11. }
  12. }

  1. //切面方法,需要实现:**MethodBeforeAdvice** 接口
  2. public class myBeforeMethodAdvice implements MethodBeforeAdvice {
  3. //method:业务方法
  4. //args:方法参数
  5. //target:目标类
  6. @Override
  7. public void before(Method method, Object[] arg1, Object target) throws Throwable {
  8. System.out.println("执行主业务前方法");
  9. }
  10. }

  1. <!--Spring主配置文件-->
  2. <bean id="service" class="com.test.beforeMethodAdvice.SomeServiceImp"/>
  3. <bean id="myAdvice" class="com.test.beforeMethodAdvice.myBeforeMethodAdvice"/>
  4. <bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
  5. <property name="target" ref="service"/>
  6. <!--<property name="target" value="service"/>-->
  7. <property name="interceptorNames" value="myAdvice"/>
  8. </bean>

接着是测试方法:

  1. public class test {
  2. @Test
  3. public void Test01() {
  4. String source = "com/test/beforeMethodAdvice/applicationContext.xml";
  5. ApplicationContext ac = new ClassPathXmlApplicationContext(source);
  6. SomeServices service = (SomeServices)ac.getBean("ProxyService");
  7. service.doFirst();
  8. service.doSecond();
  9. }
  10. }
  11. //控制台输出:
  12. //执行主业务前方法
  13. //print first
  14. //执行主业务前方法
  15. //print second

(二)后置通知

后置通知和前置通知雷同,只是切面方法的实现类不同,但是后置通知实现接口方法,多给用了一个returnValue参数,也就意味着我们可以获得主业务方法的返回值,我们来看看范例:

  1. //主业务接口
  2. public interface SomeServices {
  3. String doFirst();
  4. void doSecond();
  5. }

  1. //主业务方法实现类,doFirst()有返回值
  2. package com.test.afterMethodAdvice;
  3. public class SomeServiceImp implements SomeServices{
  4. @Override
  5. public String doFirst() {
  6. System.out.println("print first");
  7. return "abc";
  8. }
  9. @Override
  10. public void doSecond() {
  11. System.out.println("print second");
  12. }
  13. }

  1. //实现了**AfterReturningAdvice** 接口,实现这个接口的方法有一个返回值参数
  2. public class myAfterMethodAdvice implements AfterReturningAdvice {
  3. //returnValue:业务方法的返回值
  4. //method:业务方法属性类
  5. //args:方法参数
  6. //target:目标类
  7. @Override
  8. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  9. System.out.println("执行业务后方法");
  10. //只能获取到业务方法的返回值,但是不能进行修改
  11. System.out.println(returnValue);
  12. }
  13. }

  1. <!--配置文件没什么差别-->
  2. <bean id="service" class="com.test.afterMethodAdvice.SomeServiceImp"/>
  3. <bean id="myAdvice" class="com.test.afterMethodAdvice.myAfterMethodAdvice"/>
  4. <bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
  5. <property name="target" ref="service"/>
  6. <!--<property name="targetName" value="service"/>-->
  7. <property name="interceptorNames" value="myAdvice"/>
  8. </bean>

测试方法:

  1. public class test {
  2. @Test
  3. public void Test01() {
  4. String source = "com/test/afterMethodAdvice/applicationContext.xml";
  5. ApplicationContext ac = new ClassPathXmlApplicationContext(source);
  6. SomeServices service = (SomeServices)ac.getBean("ProxyService");
  7. service.doFirst();
  8. service.doSecond();
  9. }
  10. }
  11. //print first
  12. //执行业务后方法
  13. //abc
  14. //print second
  15. //执行业务后方法
  16. //null

(三)环绕通知

环绕通知就是既能实现前置通知又能实现后置通知,但是不同的是它能够对主业务方法进行修改。

  1. //主业务接口
  2. public interface SomeServices {
  3. String doFirst();
  4. void doSecond();
  5. }

  1. //主业务方法实现类
  2. public class SomeServiceImp implements SomeServices{
  3. @Override
  4. public String doFirst() {
  5. System.out.println("print first");
  6. return "abc";
  7. }
  8. @Override
  9. public void doSecond() {
  10. System.out.println("print second");
  11. }
  12. }

  1. //环绕通知,切面方法类,需要实现**MethodInterceptor**
  2. //并且调用参数的proceed方法,这个方法有一个返回值,也就是主业务方法的返回值,我们可以对它进行修改。
  3. public class MyMethodInterceptor implements MethodInterceptor {
  4. @Override
  5. public Object invoke(MethodInvocation invocation) throws Throwable {
  6. System.out.println("环绕通知,业务方法前");
  7. Object result = invocation.proceed();
  8. System.out.println("环绕通知,业务方法后");
  9. if(result != null) {
  10. result = ((String)result).toUpperCase();
  11. }
  12. return result;
  13. }
  14. }

  1. //环绕通知的配置文件
  2. <bean id="service" class="com.test.MethodInterceptor.SomeServiceImp"/>
  3. <bean id="myAdvice" class="com.test.MethodInterceptor.MyMethodInterceptor"/>
  4. <bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
  5. <property name="target" ref="service"/>
  6. <property name="interceptorNames" value="myAdvice"/>
  7. </bean>

  1. //测试方法:
  2. public class test {
  3. @Test
  4. public void Test01() {
  5. String source = "com/test/MethodInterceptor/applicationContext.xml";
  6. ApplicationContext ac = new ClassPathXmlApplicationContext(source);
  7. SomeServices service = (SomeServices)ac.getBean("ProxyService");
  8. String result = service.doFirst();
  9. System.out.println(result);
  10. service.doSecond();
  11. }
  12. }
  13. //控制台输出:
  14. //环绕通知,业务方法前
  15. //print first
  16. //环绕通知,业务方法后
  17. //ABC
  18. //环绕通知,业务方法前
  19. //print second
  20. //环绕通知,业务方法后

(四)异常通知:

异常通知就是当我们的主业务方法出现异常的时候,会对这个主业务方法进行加强!

例如:我们现在的主业务方法是对用户名和密码进行判断,如果用户名或者密码有误,我们就就分别抛出对应的错误,当无误的时候,程序正常执行。


  1. //主业务接口,判断用户名,密码是否正确
  2. public interface SomeServices {
  3. boolean checkedUser(String username,String password) throws UserException;
  4. }

  1. //实现类,实现了对用户和密码的校验
  2. public class SomeServiceImp implements SomeServices{
  3. @Override
  4. public boolean checkedUser(String username, String password)throws UserException {
  5. if(!"admin".equals(username.trim())) {
  6. throw new UsernameException("用户名错误");
  7. }
  8. if(!"123".equals(password.trim())){
  9. throw new PasswordException("密码错误");
  10. }
  11. return true;
  12. }
  13. }

上面两个是我们需要的主业务方法,里面我们定义了两个异常:UsernameException,PasswordException,它们都实现了父类UserException:


  1. //UserException
  2. public class UserException extends Exception {
  3. public UserException() {
  4. super();
  5. }
  6. public UserException(String message) {
  7. super(message);
  8. }
  9. }

  1. //UsernameException
  2. public class UsernameException extends UserException {
  3. public UsernameException() {
  4. super();
  5. }
  6. public UsernameException(String message) {
  7. super(message);
  8. }
  9. }

  1. //PasswordException
  2. public class PasswordException extends UserException {
  3. public PasswordException() {
  4. super();
  5. }
  6. public PasswordException(String message) {
  7. super(message);
  8. }
  9. }

定义好上面的异常后我们就要定义我们的通知类了:

  1. //这个异常通知需要实现ThrowsAdvice接口,接口源码上面有,我们追踪到源码会发现这个接口没有需要实现的方法,其实是由几个供我们选择,防止我们没有必要的实现全部方法
  2. public class MyThrowsAdvice implements ThrowsAdvice {
  3. public void afterThrowing(Exception ex) {
  4. System.out.println("执行异常通知方法:" + ex.getMessage());
  5. }
  6. }

配置文件没有什么变化:

  1. <bean id="service" class="com.test.afterExceptionAdvice.SomeServiceImp"/>
  2. <bean id="myAdvice" class="com.test.afterExceptionAdvice.MyThrowsAdvice"/>
  3. <bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
  4. <property name="target" ref="service"/>
  5. <property name="interceptorNames" value="myAdvice"/>
  6. </bean>

最后就是我们的测试方法:

  1. public class test {
  2. @Test
  3. public void Test01() {
  4. String source = "com/test/afterExceptionAdvice/applicationContext.xml";
  5. ApplicationContext ac = new ClassPathXmlApplicationContext(source);
  6. SomeServices service = (SomeServices)ac.getBean("ProxyService");
  7. //service.checkedUser("admin", "123");
  8. //service.checkedUser("ad", "123");
  9. try {
  10. service.checkedUser("admin", "12");
  11. } catch (UserException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  16. //控制台:
  17. //**报错**
  18. //执行异常通知方法:密码错误

本篇文章可能主要是代码的实现,原理上没有说的太多,因为前面关于动态代理的文章我也写了一篇,所以这里就没有赘述太多动态代理的知识。

本篇文章文章就介绍到这里,如有错误不吝赐教!

下一篇:AOP高级用法

08 Spring框架 AOP (一)的更多相关文章

  1. spring框架 AOP核心详解

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...

  2. 跟着刚哥学习Spring框架--AOP(五)

    AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入 ...

  3. spring框架aop用注解形式注入Aspect切面无效的问题解决

    由于到最后我的项目还是有个邪门的错没解决,所以先把文章大概内容告知: 1.spring框架aop注解扫描默认是关闭的,得手动开启. 2.关于Con't call commit when autocom ...

  4. Spring框架——AOP代理

    我们知道AOP代理指的就是设计模式中的代理模式.一种是静态代理,高效,但是代码量偏大:另一种就是动态代理,动态代理又分为SDK下的动态代理,还有CGLIB的动态代理.Spring AOP说是实现了AO ...

  5. Spring框架-AOP详细学习[转载]

    参考博客:https://blog.csdn.net/qq_22583741/article/details/79589910#4-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85% ...

  6. Spring框架 AOP面向切面编程(转)

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  7. 10 Spring框架 AOP (三) Spring对AspectJ的整合

    上两节我们讲了Spring对AOP的实现,但是在我们的开发中我们不太使用Spring自身的对AOP的实现,而是使用AspectJ,AspectJ是一个面向切面的框架,它扩展了Java语言.Aspect ...

  8. 09 Spring框架 AOP (二) 高级用法

    上一篇文章我们主要讲了一点关于AOP编程,它的动态考虑程序的运行过程,和Spring中AOP的应用,前置通知,后置通知,环绕通知和异常通知,这些都是Spring中AOP最简单的用法,也是最常用的东西, ...

  9. Spring框架AOP学习总结(下)

    目录 1. AOP 的概述 2. Spring 基于AspectJ 进行 AOP 的开发入门(XML 的方式): 3.Spring 基于AspectJ 进行 AOP 的开发入门(注解的方式): 4.S ...

随机推荐

  1. encoding和charset的区别~

    本文将简述字符集,字符编码的概念.以及在遭遇乱码时的一些常用诊断技巧 背景:字符集和编码无疑是IT菜鸟甚至是各种大神的头痛问题.当遇到纷繁复杂的字符集,各种火星文和乱码时,问题的定位往往变得非常困难. ...

  2. SSH框架-Struts2基础-Action

    Struts2的目录结构: 解压apps目录下的struts2-blank.war: 仿照这个最基本的项目,拷贝相关文件: 1.拷贝apps/struts2-blank/WEB-INF/classes ...

  3. mfc小工具开发之定时闹钟之---多线程急线程同步

    一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常 ...

  4. yalmip + lpsolve + matlab 求解混合整数线性规划问题(MIP/MILP)

    最近建立了一个网络流模型,是一个混合整数线性规划问题(模型中既有连续变量,又有整型变量).当要求解此模型的时候,发现matlab优化工具箱竟没有自带的可以求解这类问题的算法(只有bintprog求解器 ...

  5. oauth 2

    OAuth2是基于HTTP的认证API,一般与OAuth2搭配的API也是基于HTTP的REST风格API(比如新浪微博和github),很多人一定想过是否可以直接从浏览器端调用REST API. 我 ...

  6. 最简单的TabHost

    创建一个项目.Tab继承自TabActivity. main.xml: <?xml version="1.0" encoding="utf-8"?> ...

  7. zoj3662(dp)

    dp还是比较好想的,但是时间还是比较坑的. 要预处理还加些优化才行 . #include <stdio.h> #include <stdlib.h> #include < ...

  8. 网络虚拟化之FlowVisor:网络虚拟层(上)

    概念解释:切片:虚拟网络的一个实例 一. 网络虚拟化(虚拟网络) 人类社会的发展在很大方面得益于自然界,飞机受益于鸟,雷达受益于蝙蝠等等,所以专门有个学科为仿生学就是研究和模仿生物的特殊本质,利用生物 ...

  9. mac上的webStorm上配置gitHub

    一,webStorm下,首先打开Preferences; 二,在Version Control目录下,选择GitHub,填写有边的内容; 注意:填写完Login和Password的以后,点击Test一 ...

  10. 【BZOJ2208】[Jsoi2010]连通数 DFS

    [BZOJ2208][Jsoi2010]连通数 Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示 ...