博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring总结六:AOP(面向切面编程)
阅读量:5235 次
发布时间:2019-06-14

本文共 7011 字,大约阅读时间需要 23 分钟。

概述:

  AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。AOP是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。

  AOP就是将公用功能提取出来,如果以后公用功能的需求发生变化,只需要改动公用的模块的代码即可,多个调用的地方则不需要改动。所谓面向切面,就是只关注通用功能,而不关注业务逻辑。实现方式一般是通过拦截。一般应用在日志记录,事务管理,权限验证,性能监测(统计方法运行时间),异常拦截等。

  1,Aspect(切面):通常是一个类,里面可以定义切入点和通知

  2,JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
  3,Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
  4,Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
  5,AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类,原理我们后面有时间再写一篇博客。

 

我们先通过例子来了解一下基于xml配置的AOP:

简单的实现转账功能,刘德华给张学友转账10000元:

首先我们把pom.xml中需要包引进去(我是通过父工程管理的jar包版本,所以没有写版本信息):

org.springframework
spring-context
org.springframework
spring-test
org.springframework
spring-aspects
junit
junit

我们的dao和service代码:

public class AccountDao {    /**     * 转入方法     */    public void shiftto(){        System.out.println("张学友的账户转入了 10000...");    }    /**     * 转出方法     */    public void rollout(){        System.out.println("刘德华的账户转出了 10000...");    }}public class AccountService {    private AccountDao accountDao;    /**     * 基于xml配置 提供一个set方法     */    public void setAccountDao(AccountDao accountDao) {        this.accountDao = accountDao;    }    /**     * 转账方法     */    public String transfer() {        //转出        accountDao.rollout();        //转入        accountDao.shiftto();        return "我是AccountService类的transfer方法的返回值";    }}

然后配置spring配置文件中对应的bean:

如果只有上面这些的话(不使用AOP的情况),我们可以通过测试代码:

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:applicationContext.xml")public class AccountServiceTest {    @Value("#{accountService}")    private AccountService service;    @Test    public void transfer() throws Exception {        service.transfer();    }}

得出的结果如下:

 

那么如果现在我们要把转账的功能放在事务里的话,正常情况下需要在实现的service中加入事务的代码,但是使用AOP的话我们可以这样做:

新建自己的Aspect类:

public class MyAspect {    /**     * before 前置通知方法     */    public void before(JoinPoint joinPoint) {        System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "类开启事务");    }    /**     * afterReturning 后置通知方法     */    public void afterReturning(JoinPoint joinPoint, Object returnVal) {        System.out.println("---------------提交事务,返回值为:" + returnVal);    }    /**     * after 最终通知方法     */    public void after(JoinPoint joinPoint) {        System.out.println("---------------释放资源");    }    /**     * afterThrowing 后置异常通知方法     */    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {        System.out.println("---------------事务回滚,程序异常信息:" + ex.getMessage());    }}

这个时候 我们需要在spring配置文件applicationContext.xml中这样配置:

上面的配置的切入点语法需要解释一下:

Spring 只支持AspectJ的部分切入点语法 4.3.1 语法一: execution(修饰符? 返回值 方法名(参数) 异常?)execution(* *(..))  匹配所有spring管理对象所有方法, 第一个*任意返回值 ,第二个*任意方法, .. 任意参数 execution(* com.zy.service.*..*(..)) 匹配com.zy.service包中所有对象所有方法execution(* com.zy.service.AccountService.s*(..)) 匹配com.zy.service.AccountService中s开头方法 4.3.2 语法二:bean(beanName) 匹配目标Bean所有方法bean(*Service) 匹配所有以Service结尾BeanName 的对象  4.3.3 语法三:within(包.*) 匹配包下所有类的所有方法within(com.zy.service..*) 匹配spring包及其子包中类所有方法  注意: 一个.代表子目录; 两个点.. 表示后代目录

 

最后再运行我们的测试代码会得出以下结果:

这样我们就把事务加进去了,这样做最大的好处就是可以随时把事务去掉或者是修改其实现代码。

 

但是如果感觉这样在xml文件中配置比较麻烦的话,这里讲述一个稍微简单的方式,就是我们文章开始标红的通知类型 around(环绕通知):

修改MyAspect,加入around方法:

/**     * around 环绕通知     */    public void around(ProceedingJoinPoint joinPoint) throws Throwable {        Object returnVal = null;        try {            //执行before            System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "类开启事务");            //执行具体业务            returnVal = joinPoint.proceed();            //执行afterReturning            System.out.println("---------------提交事务,返回值为:" + returnVal);        } catch (Exception ex) {            //执行afterThrowing            System.out.println("---------------事务回滚,程序异常信息:" + ex.getMessage());        } finally {            //执行after            System.out.println("---------------释放资源");        }    }

修改xml配置:

可以看出 一行around通知的配置代替了之前的 before afterReturning afterThrowing after,但是效果是一样的。

 

下面我们来说一下基于注解的方式:

首先我们的spring配置文件改写成如下这样:

给我们的dao和service添加注解:

@Repository("accountDao")public class AccountDao {    /**     * 转入方法     */    public void shiftto(){        System.out.println("张学友的账户转入了 10000...");    }    /**     * 转出方法     */    public void rollout(){        System.out.println("刘德华的账户转出了 10000...");    }}@Service("accountService")public class AccountService {    @Value("#{accountDao}")    private AccountDao accountDao;    /**     * 转账方法     */    public String transfer() {        //转出        accountDao.rollout();        //转入        accountDao.shiftto();        return "我是AccountService类的transfer方法的返回值";    }}

给我们的MyAspect类添加注解:

@Component("myAspect")@Aspect //通知这个类是个切面public class MyAspect {    /**     * 提出来公用的切入点     */    @Pointcut("bean(*Service)")    private void myPointcut() {    }    /**     * before 前置通知方法     */    @Before("myPointcut()")    public void before(JoinPoint joinPoint) {        System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "类开启事务");    }    /**     * afterReturning 后置通知方法     */    @AfterReturning(pointcut = "myPointcut()", returning = "returnVal")    public void afterReturning(JoinPoint joinPoint, Object returnVal) {        System.out.println("---------------提交事务,返回值为:" + returnVal);    }    /**     * after 最终通知方法     */    @After("myPointcut()")    public void after(JoinPoint joinPoint) {        System.out.println("---------------释放资源");    }    /**     * afterThrowing 后置异常通知方法     */    @AfterThrowing(pointcut = "myPointcut()", throwing = "ex")    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {        System.out.println("---------------事务回滚,程序异常信息:" + ex.getMessage());    }}

运行测试代码得到如下结果:

到此 完全ojbk。

 

转载于:https://www.cnblogs.com/blazeZzz/p/9310859.html

你可能感兴趣的文章
链表的各种函数
查看>>
POJ 1258 Agri-Net(最小生成树 Prim 模版题)
查看>>
会爆栈,dfs传参,只能传1个~~RMQLCA在线
查看>>
使用laravel搭建CURD后台页面
查看>>
CSS-font
查看>>
FFMpeg音频重采样和视频格式转
查看>>
eclipse中安装svn插件实现版本控制
查看>>
Orchard学习资料,适合入门上手
查看>>
HTML/CSS/Javascript代码在线压缩、格式化(美化)工具
查看>>
UMDH
查看>>
模板语言
查看>>
利用IDEA上传文件到coding仓库 使用git上传文件github
查看>>
推荐文章(关于终端)
查看>>
C# .NET 使用第三方类库DotNetZip解压/压缩Zip文件
查看>>
关于Github Pages
查看>>
程序员练级之路 (作者:陈皓)
查看>>
win7下virtualbox遇到的问题
查看>>
iOS开发数据库SQLite的使用
查看>>
(转)一次被黑经历与反思
查看>>
【WebService】WebService学习笔记
查看>>