一、Spring AOP介绍
开发其实就是在不断的重构,抽象重复代码,然后进行封装。从最原始的模块化编程到面向对象编程,代码的封装越来越整齐清晰,但是依然存在重复的代码,而这些重复代码几乎都是与业务逻辑无关的系统逻辑代码。比如在数据操作类中的插入、更新、删除数据等方法中都存在数据库事务的处理,重要业务逻辑方法中都有日志记录的逻辑等等。每个应用系统都存在着这种系统级的重复逻辑代码,而我们并没有更好的方法去将这些代码抽象出来并进行管理。然而AOP的出现弥补了这一缺陷,AOP可以在不改变原有业务逻辑代码的情况下对原有业务进行横切拦截,处理那些重复的系统逻辑。
 
与Ioc容器一样,AOP也是Spring的核心模块之一。AOP是Aspect-Oriented Programming的简称,现在通常称为面向切面编程。我们学了OOP,面向对象编程,而AOP并非是OOP的替代技术,它只是OOP的一个有益补充。
 
需要指出的是AOP的应用场合是受限的,它一般只适合于那些具有横切逻辑的应用场合:如性能监测、访问控制、事务管理以及日志记录,它并不适合处理具体的业务逻辑,分散处理业务逻辑会使得逻辑混乱、增加维护成本。
 
 
二、如何使用Spring AOP
下面以对用户操作类UserDao的AOP拦截演示Spring AOP的使用。
1、创建Java项目,添加Spring AOP依赖支持
aopalliance-1.0.jar
commons-logging-1.1.1.jar
spring-aop-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
 
2、添加User及UserDao类
User类:
  1. public class User {
  2. private Integer id;
  3. private String name;
  4. }
UserDao类:
  1. public class UserDao {
  2. public void save(User user){
  3. System.out.println("save user....");
  4. }
  5. public void delete(int id){
  6. System.out.println("delete user....");
  7. }
  8. public void update(User user) {
  9. System.out.println("update user ....");
  10. }
  11. public User query(String name) {
  12. System.out.println("getUser ....");
  13. return new User();
  14. }
  15. }
3、添加AOP拦截处理
AOP前置通知:
  1. public class UserBeforeAdvice implements MethodBeforeAdvice {
  2. public void before(Method method, Object[] args, Object target) {
  3. System.out.println("调用方法:"+method.getName() + "()前拦截处理");
  4. }
  5. }
AOP后置通知:
  1. public class UserAfterAdvice implements AfterReturningAdvice {
  2. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
  3. System.out.println("方法:"+method.getName() + "()返回后拦截处理");
  4. }
  5. }
AOP环绕通知:
  1. public class UserAroundAdvice implements MethodInterceptor {
  2. public Object invoke(MethodInvocation invocation) throws Throwable {
  3. System.out.println("调用方法:"+invocation.getMethod().getName() + "()前拦截处理");
  4. Object o = invocation.proceed();
  5. System.out.println("调用方法:"+invocation.getMethod().getName() + "()后拦截处理");
  6. return o;
  7. }
  8. }
4、添加Spring配置文件applicationContext.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
  6. <bean id="userDaoTarget" class="com.boya.spring.dao.UserDao" />
  7. <bean id="userBeforeAdvice" class="com.boya.spring.aop.UserBeforeAdvice" />
  8. <bean id="userAfterAdvice" class="com.boya.spring.aop.UserAfterAdvice" />
  9. <bean id="userAroundAdvice" class="com.boya.spring.aop.UserAroundAdvice" />
  10. <bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean">
  11. <property name="interceptorNames">
  12. <list><value>userAroundAdvice</value></list>
  13. </property>
  14. <property name="target" ref="userDaoTarget"></property>
  15. </bean>
  16. </beans>
5、测试AOP
  1. public static void main(String[] args) {
  2. ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  3. UserDao userDao = context.getBean("userDao", UserDao.class);
  4. userDao.save(new User());
  5. }
输出结果:
调用方法:save()前拦截处理
save user....

调用方法:save()后拦截处理

 
回过头来再看刚才的示例。
1、首先,原来的业务逻辑代码不变
    不再关心重复的系统逻辑代码
2、编写AOP切面处理逻辑
    把原业务逻辑中的重复代码抽象出来,封装入切面代码中,如上面示例的三种Advice通知封装不同的系统处理逻辑。
    前置通知:实现MethodBeforeAdvice 接口,在调用业务方法前调用该接口
    后置通知:实现AfterReturningAdvice 接口,在业务方法返回后调用该接口,在该接口中可以查看返回值(但不能修改返回值)
    环绕通知:实现MethodInterceptor 接口,在该接口中invocation.proceed();这个方法会调用真实对象的方法
3、使用Spring配置文件将业务逻辑和AOP切面逻辑进行组装
    AOP代理Bean类型需要设置为org.springframework.aop.framework.ProxyFactoryBean
    必须设置代理目标(target属性设置)和通知类型(interceptorNames属性设置)
    代理目标并非必须实现接口,作为POJO被代理时,会对目标所有方法进行拦截
 
三、AOP实现原理
Spring AOP是基于Java反射和动态代理实现的。在讲解动态代理之前,我们先回顾一下代理模式。
代理模式,就是为某一对象提供一个代理,通过代理操作该对象的方法。通常情况下,真实对象和代理对象需要实现相同的接口,在代理对象中保存真实对象的引用,以此来控制操作真实对象。
 
我们以班长代理老师签到来演示代理模式。
创建签到接口:
  1. public interface SignInterface {
  2. public Object sign(String nameList);
  3. }
创建真实对象,Teacher类:
  1. public   class  Teacher  implements  SignInterface {
  2. public  Object sign(String nameList) {
  3. System. out .println( "Teacher sign..." );
  4. return   new  Object();
  5. }
  6. }
创建代理对象,Leader类:
  1. public class Leader implements SignInterface {
  2. private Teacher teacher;
  3. public Object sign(String nameList) {
  4. if (teacher == null) {
  5. teacher = new Teacher();
  6. }
  7. Object o = teacher.sign(nameList);
  8. return o;
  9. }
  10. }
测试代理:
  1. public static void main(String[] args) {
  2. SignInterface s = new Leader();
  3. s.sign("names");
  4. }
以上就是一个代理模式的例子,代理类在编译时就已经被创建了,而动态代理是在运行时动态创建代理类来实现代理模式。如下代码:
  1. public class ProxyObject implements InvocationHandler {
  2. private Object proxy_obj;
  3. ProxyObject(Object obj) {
  4. this.proxy_obj = obj;
  5. }
  6. public static Object getProxy(Object obj) {
  7. Class cls = obj.getClass();
  8. // 通过Proxy类的newProxyInstance方法来返回代理对象
  9. return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new ProxyObject(obj));
  10. }
  11. /**
  12. * 实现InvocationHandler接口的invoke
  13. */
  14. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  15. System.out.println("调用方法:" + method + "()之前拦截处理");
  16. if (args != null) {
  17. System.out.println("方法有" + args.length + "个参数");
  18. for (int i = 0; i < args.length; i++) {
  19. System.out.println(args[i]);
  20. }
  21. }
  22. // 利用反射机制动态调用真实对象的方法
  23. Object o = method.invoke(proxy_obj, args);
  24. System.out.println("调用方法:" + method + "()之后拦截处理");
  25. return o;
  26. }
  27. // 测试代码
  28. public static void main(String agr[]) {
  29. SignInterface si = (SignInterface) getProxy(new Teacher());
  30. si.sign("names");
  31. }
  32. }
以上就是使用JDK的Proxy实现的动态代理,不过JDK的动态代理实现只支持针对接口的动态代理实现。Spring AOP实现默认也是动态代理方式,不过,Spring AOP支持CGLib Proxy的实现方式,可以针对POJO进行动态代理,实现AOP拦截。
 
我们来看一下CGLib实现的一个简单AOP拦截
创建业务POJO:
  1. public class CGLibTeacher {
  2. public Object sign(String nameList) {
  3. System.out.println("Teacher sign...");
  4. return new Object();
  5. }
  6. }
创建AOP拦截:
  1. public class CGLibAop implements MethodInterceptor {
  2. public Object intercept(Object arg0, Method arg1, Object[] arg2,
  3. MethodProxy arg3) throws Throwable {
  4. System.out.println("before...");
  5. Object o = arg3.invokeSuper(arg0, arg2);
  6. System.out.println("after...");
  7. return o;
  8. }
  9. }
CGLib代理对象创建及测试:
  1. public class CGLibProxy {
  2. public static CGLibTeacher create(CGLibAop aop){
  3. Enhancer en = new Enhancer();
  4. //进行代理
  5. en.setSuperclass(CGLibTeacher.class);
  6. en.setCallback(aop);
  7. //生成代理实例
  8. return (CGLibTeacher)en.create();
  9. }
  10. public static void main(String[] args) {
  11. CGLibTeacher t = CGLibProxy.create(new CGLibAop());
  12. t.sign("names");
  13. }
  14. }
从CGLib的代理对象创建中可以看到,代理对象需要设置代理目标以及AOP拦截实现,和Spring AOP的实现非常类似。

Spring 教程(二)的更多相关文章

  1. Spring Security教程(二):自定义数据库查询

    Spring Security教程(二):自定义数据库查询   Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就 ...

  2. SpringBoot入门教程(二)CentOS部署SpringBoot项目从0到1

    在之前的博文<详解intellij idea搭建SpringBoot>介绍了idea搭建SpringBoot的详细过程, 并在<CentOS安装Tomcat>中介绍了Tomca ...

  3. Spring教程索引

    Spring教程索引 2016-11-15 1 入门 1 概述.深入浅出Spring(一)Spring概述 2 体系结构 3 环境设置 4 Hello World 实例 5 IoC 容器   IoC容 ...

  4. 13、Spring教程之全部(包括所有章节)

    Spring 教程 1.Spring概述 简介 Spring : 春天 --->给软件行业带来了春天 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架. ...

  5. CRL快速开发框架系列教程二(基于Lambda表达式查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置

    前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...

  7. C#微信公众号开发系列教程二(新手接入指南)

    http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...

  8. 无废话ExtJs 入门教程二十一[继承:Extend]

    无废话ExtJs 入门教程二十一[继承:Extend] extjs技术交流,欢迎加群(201926085) 在开发中,我们在使用视图组件时,经常要设置宽度,高度,标题等属性.而这些属性可以通过“继承” ...

  9. 无废话ExtJs 入门教程二十[数据交互:AJAX]

    无废话ExtJs 入门教程二十[数据交互:AJAX] extjs技术交流,欢迎加群(521711109) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3C ...

  10. 无废话ExtJs 入门教程二[Hello World]

    无废话ExtJs 入门教程二[Hello World] extjs技术交流,欢迎加群(201926085) 我们在学校里学习任何一门语言都是从"Hello World"开始,这里我 ...

随机推荐

  1. Redhat 显示系统版本号和内核版本号

    1./etc/issue 和 /etc/redhat-release都是系统安装时默认的发行版本信息,通常安装好系统后文件内容不会发生变化.[root@rac1 mysql]# cat /etc/is ...

  2. c# 赋值后最后一项数据部分丢失

    赋值,赋值后 原因,在添加后立即调用clear()清除数据....

  3. TWaver3D入门探索——3D拓扑图之绽放的小球花

    这样一簇绚烂丰满艳丽多姿的3D小球花,要多少代码才能完成?其实不足百行,您信吗?下面咱就看一下具体实现过程,让您分分钟学会用TWaver HTML5制作3D拓扑图. 搭建3D空间 首先为花簇的绽放建一 ...

  4. 1054: [HAOI2008]移动玩具 - BZOJ

    Description 在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动 ...

  5. [转载]HTML5 Audio/Video 标签,属性,方法,事件汇总

    <audio> 标签属性: src:音乐的URL preload:预加载 autoplay:自动播放 loop:循环播放 controls:浏览器自带的控制条 <audio id=& ...

  6. 解决win8 64位提示MSVCP71.DLL等组件缺失

    把压缩包里面的DLL解压,只需要把其实缺失DLL复制到C:\Windows\SysWOW64即可.压缩包包含MSVCP70.DLL.MSVCP71.DLL.MSVCR70.DLL.MSVCR71.DL ...

  7. jsp 获取表单值, 提交类型为multipart/form-data处理

    //tt.jsp<script type="text/javascript"> function doSubmit(){ alert("aaaaaa" ...

  8. VARCHAR2转换为CLOB碰到ORA-22858错误

    近日工作中发现有一张表的字段类型建错了,本应是BLOB类型却被别人建成了VARCHAR2(200),修改时oracle却提示“ORA-22858 invalid alteration of datat ...

  9. 宜昌IT软件资源汇总

    三峡云计算中心大厦 http://castd-ycci.com/ 三峡云计算中心http://www.sanxiacloud.com/index.htm 宜昌创业者(罗新:小卡片做成大产业 年销售额5 ...

  10. POJ 1269 Intersecting Lines(几何)

    题目链接 题意 : 给你两条线段的起点和终点,一共四个点,让你求交点坐标,如果这四个点是共线的,输出“LINE”,如果是平行的就输出“NONE”. 思路 : 照着ZN留下的模板果然好用,直接套上模板了 ...