一、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. Insist

    1.怎么自动截断文本? 如题,当数据库中的数据内容超出了要显示的长度时,如果不采取措施,会破坏页面的布局美观,所以可以采用自动截断文本,需要查看的时候再把其他的内容显示出来. 没截断的时候如下图: 再 ...

  2. 区间型动规--石子归并(Pascal)

    题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安排怎样的合并顺序,能够使 ...

  3. 【BZOJ 1087】[SCOI2005]互不侵犯King

    Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包 ...

  4. Xcode和github入门详细教程

    Xcode和github详细教程! 主要是参考了现在网上的一些资料给没整过的人一个详细的指南. (1)先在github上注册账号,自行解决! (2)在导航栏右上角new一个repository(仓库) ...

  5. What we learned in Seoul with AlphaGo

    What we learned in Seoul with AlphaGo March 16, 2016 Go isn’t just a game—it’s a living, breathing c ...

  6. PAT-乙级-1001. 害死人不偿命的(3n+1)猜想 (15)

    1001. 害死人不偿命的(3n+1)猜想 (15) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 卡拉兹(Ca ...

  7. hdu 4672 Present Day, Present Time 博弈论

    看了解题报告才知道怎么做!! 题意:有 N 堆石子和 M 个石子回收站,每回合操作的人可以选择一堆石子,从中拿出一些 放到石子回收站里(可以放进多个回收站,每个回收站可以使用无数次),但每个石子回收站 ...

  8. coco2d-js 多屏适配相关API

    setDesignResolutionSize() //设计分辨率大小及模式  setContentScaleFactor() //内容缩放因子 setSearchPaths() //资源搜索路径 g ...

  9. Spring整合freemarker发送邮件

    转载:http://blog.csdn.net/zdp072/article/details/32745335 分类: freemarker spring 2014-06-20 23:39 752人阅 ...

  10. ids & hdmi 原理

    http://www.taiwanwebinar.com/zh_TW/STATIC/SITE/dwc_hdmi_tx.pdf http://blog.csdn.net/g_salamander/art ...