这个命题其实是讲了的,但是之前没有做,发现一些面试会问到,结合自己之前的学习经历。简单把这个问题描述一下。

aop是跟oop相对应的一个概念。分别是aspect oriented programming 和 object oriented programming。也即面向切面编程和面向对象编程。那面向对象是我们一个很熟悉的概念了,【在面向对象之前还有面向过程的编程,这个概念这里面就不厘了】。画个示意图,希望能够阐述这样一个概念。

那么对于前面的就是得到一个对象,或者得到一个完整的过程,直接对他进行操作。【事实上前面儿这个图画的也不理想】

对于后面的图来说,面向的是整个过程的一个剖儿面。

比如说下面的存储过程。

原来的逻辑是 一个数据库的存储操作,现在,我们要在这个 完成的逻辑上面添加一些日志,比如说,在存储之前,我们让日志信息记录说,UserDaoImpl请求添加一条记录到数据库中,在存储操作完成以后,让日志信息再打印一个 UserDaoImpl已经成功添加了一条数据。

对于这个完整过程来说,原来的逻辑是完完全全没有问题的。但是如果在一个大型系统中出了问题,如果在每个地方都有相应的提示信息的话,可以帮助我们快速定位到问题从而顺利解决。所以如果打印到“UserDaoImpl请求添加一条记录到数据库中”,然后报了错,下面那句话没有执行,是不是就很方便可以定位到存储时候出了问题。【这里只是举个例子说明织入也就是aop这个概念。之前dao都没出过问题,加上了切面反而出了问题,那还要这个切面干毛线?这个假设简直不能再糟糕,笑哭】【想到一个可能的逻辑,囧,比如在oracle里面的年龄字段写了check 18 to 60,而在前端的校验只定义成了0-150合理,这样在junit测试里面没有把例子跑全,或者自己只写了一个简单的测试用例比如数字都是23,34,59。导致在集成测试的时候有一个78被放行,这样就有可能出问题,在修改的时候可能回去数据库里面更改check 或者 去js验证里面,更改放行数字。终于圆回来了,反正总之现在的需求就是加一个aop!!!】

好了问题引入完毕,先来扯点儿蛋。

aop的知识点是从代理模式引入的。

那么什么是代理,为什么要代理。代理是怎么一回事儿?

代理分为两种形式,静态代理,动态代理。

假设有一个接口UserDao,【这个包名我就很喜欢】

package com.letben.dao;

/**
* userdao的 接口
* @author Administrator
*
*/
public interface UserDao {
public void saveUser();
}

还有一个它的实现类UserDaoImpl。

package com.letben.dao;
/**
* 这个地方用来实现userdao的 真实逻辑
* @author Administrator
*/
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("存储用户到数据库");
}
}

那原来这两个就能完成存储逻辑。

现在需求改变我们要加一个代理,实现在存储之前之后打印日志。那么新写一个UserDaoStaticProxy

package com.letben.dao;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 同样是userdao的一个 实现类,没有更多的业务逻辑。只是为了在 实现逻辑的前后 打印日志
* @author Administrator
*/
public class UserDaoStaticProxy implements UserDao {
//得到打印日志的对象,它属于util包
Logger logger = Logger.getLogger(UserDaoStaticProxy.class.getName());
UserDao userDao;
public UserDaoStaticProxy(UserDao userDao){
this.userDao = userDao;
}
@Override
public void saveUser() {
logger.log(Level.INFO, "存储用户之前");
userDao.saveUser();
logger.log(Level.INFO,"存储用户之后");
}
}

然后添加一段测试代码:

package com.letben.dao;
public class Test {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
UserDao userDaoApplication = new UserDaoStaticProxy(userDao);
userDaoApplication.saveUser();
}
}

运行结果:

二月 15, 2016 9:50:34 上午 com.letben.dao.UserDaoStaticProxy saveUser
信息: 存储用户之前
存储用户到数据库
二月 15, 2016 9:50:35 上午 com.letben.dao.UserDaoStaticProxy saveUser
信息: 存储用户之后

在测试代码中就能够理解是为什么了。我们首先创建了一个UserDaoImpl也就是最开始用来完成存储业务的类。然后又创建了UserDaoImpl来增加日志信息。这样aop的概念,就比较好解释了,就是在这个逻辑不改变的情况下,多那么一点点东西,让整个流程更加清楚、完善。

但是静态代理毕竟可复用性太差,要重新写java代码,这就比较糟糕。

那动态代理的部分,就是利用框架,来实现这样一种需求的嵌入。这还有两种形式,一种是xml的配置形式,还有一种是 注解的形式。

形式一:xml配置形式

创建web工程。

导入jar包。【没有的在下面写邮箱吧,或者小纸条我也行。】

目录结构。

1、那我们其实真正做的就是后来,导入了一些专用的jar包。

2、添加一个切面包。以及文件:AspectDemo.java。

package com.letben.aspect;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类
* @author Administrator
*/
public class AspectDemo {
Logger log = Logger.getLogger(AspectDemo.class.getName());
/**
* 在某断点之前添加通知,我们这里就是 给 添加方法 之前设置的 所以 这个 方法的名字,也是 这么取名的
*/
public void beforeSaveUser(){
log.log(Level.INFO, "存储前");
}
/**
* 在saveUser方法 调用之后 执行此方法
*/
public void afterSaveUser(){
log.log(Level.INFO,"存储后");
}
public void aroundSaveUser(ProceedingJoinPoint point){
log.log(Level.INFO,"环绕-前");
try {
point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
log.log(Level.INFO,"环绕-后");
}
public void exceptionAboutSave(Exception e){
//在这里并不会有结果,因为 上一个 已经 处理了异常,并且 我们 的存储方法 是正确的 不存在 输入错误 这回事。。所以 为了巩固 我们再写一个 面向切面代理出错版本。
log.log(Level.WARNING,"出现异常");
}
}

3、在beans.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 要有 对应 实现逻辑的 userDao -->
<bean id="userDao" class="com.letben.dao.UserDaoImpl"></bean> <!-- 切面类 对应的 实体对象 -->
<bean id="aspectDemo" class="com.letben.aspect.AspectDemo"></bean>
<!-- aop的配置 -->
<aop:config> <!-- 选定某一个类为切面 -->
<aop:aspect ref="aspectDemo">
<!-- 切入点表达式,就是 我们为了做切入,一定要有一个切入点,用来表示 在某一个地方开始 执行我们的某些程序-->
<!-- 文档中 6.2.3.4里面的各个 示例 -->
<!-- 解释:任意的修饰符 【空格】 这个 包下的 这个 方法里面的所有方法(这里面带着所有的参数)-->
<aop:pointcut expression="execution(* com.letben.dao.UserDao.*(..))" id="point" /> <!-- 通知类型+插入方法+切入点 =连接点 -->
<aop:before method="beforeSaveUser" pointcut-ref="point" />
<!--
上面这句话有两种书写方式:
<aop:before method="beforeSaveUser" pointcut="execution(* com.letben.dao.UserDao.*(..))">
要么直接 写切入点,要么采用引入的方式 写 切入点
-->
<aop:after method="afterSaveUser" pointcut-ref="point"/>
<aop:around method="aroundSaveUser" pointcut-ref="point"></aop:around> </aop:aspect>
</aop:config>
</beans>

这样就完成了动态代理的xml形式的书写。

注解方式:

注解编程里面,配置就比较简单。

jar包还是那些。

beans.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 启动注解编程 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="userDao" class="com.letben.dao.impl.UserDaoImpl"></bean> <!-- 因为 当前注解类 并不需要 给谁使用,所以 直接注册 并不需要 别的什么逻辑 -->
<bean class="com.letben.aspect.AspectForUserDao"/> </beans>

切面类:

package com.letben.aspect;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 声明这是一个 注解类
* @author Administrator
*/
@Aspect
public class AspectForUserDao {
Logger logger = Logger.getLogger(AspectForUserDao.class.getName());
/**
* 前置 方法 里面需要一个 切入点
*/
@Before("execution(* com.letben.dao.UserDao.*(..))")
public void test1(){
logger.log(Level.INFO,"之前");
}
/**
* 在7.0里面 那个 value 不能加上。但是 在 6.0里面这个 参数 是需要的
* @Around(value="exection(*com.letben.dao.UserDao.*(..))")这是不正确的写法 在7.0里面
*/
@After("execution(* com.letben.dao.UserDao.*(..))")
public void test2(){
logger.log(Level.INFO,"之后");
}
/**
* 不,不是 那个 value 也不知道 是哪里,这个 注解编程 很奇怪,就是 很奇怪。我也不知道 那里 写错了,但是 就是 报了 一些 处理了20min的异常就在这个 写注解的地方
* @param point
*/
@Around(value="execution(* com.letben.dao.UserDao.*(..))")
public void test3(ProceedingJoinPoint point){
logger.log(Level.INFO,"环绕-前");
try {
point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
logger.log(Level.INFO,"环绕-后");
}
}

所以动态的代理就是 我们所说的aop也就是织入了一个逻辑。织入这个词,感觉翻译起来还是很贴切的。就是在完整的东西上加进去一些东西,让他更加完美。上面是对日志的织入。

下面有事务的织入。略有不同。

只说修改的地方了,原来的那个工程还是有点儿小。。。

对于事务这样的操作,想来应该是放到service层里面最合适。比如存取钱的操作同时完成。不应该封装到dao层理面,而应该是 服务层理面,但是其实,这样的操作也可在dao里面写,但是框架提供了这样的方式,可以让我们进行事务操作。所以我们来应用一下。

【完整样例代码:欢迎写邮箱,或者小纸条我,主要十几个包里面的十几个类,复制粘贴确实容易让人没有兴趣看下去。但是都是前面儿的一些知识点的总结。】

serviceImpl 里面添加:

/**
* 事务这样的操作都写在 业务层 而非 持久化层
*/
public void tryTransaction(){
UserPo user1 = getUserById(2);
UserPo user2 = getUserById(3);
user1.setUserAge(39);
userDao.updateUser(user1);
user2.setUserAge(Integer.parseInt("23"));
userDao.updateUser(user2);
}

在beans.xml里面新增事务工厂:

    <!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 事务管理器 管理的是哪一个 数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的代理工厂 -->
<bean id="proxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 给事务代理工厂一个 事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 告诉代理工厂 的代理权限——要管理那些接口 -->
<property name="proxyInterfaces">
<list><!-- 列表给值 底层 list和 set 是一个 类型的 -->
<value>com.letben.service.UserService</value>
</list>
</property>
<!-- 告诉代理工厂要管理的 实现类 -->
<property name="target" ref="UserService"></property>
<!-- 告诉事务管理器 要管理的规则 和方法 -->
<property name="transactionAttributes">
<props><!-- 参数给值 底层 props 和 map 是一个类型的 -->
<!-- 下面这个 key对应的是实现类里面的方法。符合通配符的使用规则* -->
<prop key="tryTransaction">PROPAGATION_REQUIRED</prop><!-- 需要传播 -->
</props>
</property>
</bean>

这样就完成了 带有工厂的新增事务的配置方式。

当然还有两种。一种是不使用事务工厂的方式,还有一种是自动代理的方式。

当然这种织入不仅有日志的添加,事务的处理,还包括权限的检查等等多个方面。所谓切面编程就是在不影响原来事情处理逻辑的基础上,添加内容,让这个流程变得更加完善合情理合逻辑。

什么是aop?的更多相关文章

  1. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  2. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  3. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  4. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  5. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  6. .Net中的AOP系列之构建一个汽车租赁应用

    返回<.Net中的AOP>系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看. 本系列的实验环境:VS ...

  7. .NET里简易实现AOP

    .NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...

  8. 在.Net中实现自己的简易AOP

    RealProxy基本代理类 RealProxy类提供代理的基本功能.这个类中有一个GetTransparentProxy方法,此方法返回当前代理实例的透明代理.这是我们AOP实现的主要依赖. 新建一 ...

  9. 使用Java原生代理实现AOP

    ### 本文由博主柒.原创,转载请注明出处 ### 完整源码下载地址 [https://github.com/MatrixSeven/JavaAOP](https://github.com/Matri ...

  10. 【开源】.Net Aop(静态织入)框架 BSF.Aop

    BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费): 实现前后Aop切面和INotifyPropertyChanged注入方式. 开源地址: ...

随机推荐

  1. WCF - Hosting WCF Service

    After creating a WCF service, the next step is to host it so that the client applications can consum ...

  2. hadoop运行流程分析源代码级

    前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...

  3. [Hadoop源码解读](二)MapReduce篇之Mapper类

    前面在讲InputFormat的时候,讲到了Mapper类是如何利用RecordReader来读取InputSplit中的K-V对的. 这一篇里,开始对Mapper.class的子类进行解读. 先回忆 ...

  4. 【转】G++ 处理 /usr/bin/ld: cannot find -lc

    原文网址:http://blog.sina.com.cn/s/blog_67bbb71101010tto.html 用g++编译C++程序时显示出:/usr/lib/ld: cannot find - ...

  5. 利用OpenXml生成Word2007文档

    一.OpenXml简介 利用C#生成Word文档并非一定要利用OpenXml技术,至少可以使用微软提供的Office相关组件来编程,不过对于Office2007(确切的说是Word.Excel和Pow ...

  6. VS2010中的调试技巧

    作者: scottgu 这是我的博客中关于VS 2010和.NET 4发布系列的第二十六篇文章. 今天的博文将介绍Visual Studio中的一些实用调试技巧.这是受我朋友Scott Cate (他 ...

  7. lua与C/C++交互

    Lua设计小巧很容易与C/C++进行交互,下面我们具体讲解C/C++中如何调用lua,而lua中又如何调用C代码. 首先lua和C交互的一个重要的数据结构lua_State,它是进行数据交换的堆栈,按 ...

  8. shell常用命令总结

    统计文件行数 wc -l filename grep -c "" filename sed -n '$=' filename awk 'END{print NR}' filenam ...

  9. Tutorial: Getting Started with SignalR (C#) -摘自网络

    Overview This tutorial introduces SignalR development by showing how to build a simple browser-based ...

  10. HDU5643-King's Game

    BestCoder上的题,直接贴网站上的题目和题解了.很棒的题. 问题描述为了铭记历史,国王准备在阅兵的间隙玩约瑟夫游戏.它召来了 n(1≤n≤5000) 个士兵,逆时针围成一个圈,依次标号 1,2, ...