面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,

它将那些影响多个类的行为封装到可重用的模块中。

通常情况下,对于AOP,我们有两种方式来实现。

  使用DynamicProxy实现AOP

  下面是一个简单的示例,首先定义业务对象:

 1 public interface UserDao {
2
3 void save();
4 }
5
6 public class UserDaoImpl implements UserDao
7 {
8 private String name;
9
10 public void save() {
11 System.out.println("save() is called for " + name);
12 }
13
14 public void setName(String name) {
15 this.name = name;
16 }
17
18 public String getName() {
19 return name;
20 }
21 }

  下面是一个实现了InvocationHandler的类:

 1 public class ProxyFactory implements InvocationHandler
2 {
3 private Object target;
4
5 public Object createUserDao(Object target)
6 {
7 this.target = target;
8 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
9 this.target.getClass().getInterfaces(), this);
10 }
11
12 public Object invoke(Object proxy, Method method, Object[] args)
13 throws Throwable {
14
15 UserDaoImpl userDao = (UserDaoImpl)target;
16 Object result = null;
17 if(userDao.getName() != null)
18 {
19 result = method.invoke(target, args);
20 }
21 else
22 {
23 System.out.println("The name is null.");
24 }
25 return result;
26 }
27 }

  接下来是测试代码:

1 private static void test1()
2 {
3 ProxyFactory pf = new ProxyFactory();
4 UserDao userDao = (UserDao)pf.createUserDao(new UserDaoImpl());
5 userDao.save();
6 }

  执行结果如下:

The name is null.

  这是因为userDao的类型是UserDao,它是一个接口,并没有定义name字段,因此name=null。

  使用Cglib实现AOP

  同样是上面的需求,我们假设并没有继承的接口,这我们可以使用cglib来实现。

  首先我们重新定义一个UserDaoImpl2,它不会实现任何接口:

 1 public class UserDaoImpl2
2 {
3 private String name;
4
5 public void save() throws InterruptedException {
6 Thread.sleep(3000);
7 System.out.println("save() is called for " + name);
8 }
9
10 public void setName(String name) {
11 this.name = name;
12 }
13
14 public String getName() {
15 return name;
16 }
17
18 public void raiseException()
19 {
20 throw new RuntimeException("This is test.");
21 }
22 }

  然后是创建CglibFactory:

 1 public class CglibFactory implements MethodInterceptor
2 {
3 private Object target;
4 public Object createUserDao(Object target)
5 {
6 this.target = target;
7 Enhancer enhancer = new Enhancer();
8 enhancer.setSuperclass(target.getClass());
9 enhancer.setCallback(this);
10 return enhancer.create();
11 }
12
13 public Object intercept(Object proxy, Method method, Object[] args,
14 MethodProxy methodProxy) throws Throwable {
15 UserDaoImpl2 userDao = (UserDaoImpl2)target;
16 if (userDao.getName() != null)
17 {
18 return method.invoke(target, args);
19 }
20 else
21 {
22 System.out.println("The name is null.");
23 }
24 return null;
25 }
26 }

  它实现了MethodInterceptor接口,其中包括intercept方法,这个方法就会通过反射的方式来触发目标方法,同时还可以添加一些其他处理。

  下面是测试方法:

 1 private static void test2() throws InterruptedException
2 {
3 CglibFactory cf = new CglibFactory();
4 UserDaoImpl2 temp = new UserDaoImpl2();
5 UserDaoImpl2 userDao = (UserDaoImpl2)cf.createUserDao(temp);
6 userDao.save();
7 temp.setName("Zhang San");
8 userDao = (UserDaoImpl2)cf.createUserDao(temp);
9 userDao.save();
10 }

  输出结果如下:

The name is null.
save() is called for Zhang San

  使用Spring实现AOP

  Spring框架集合了ProxyFactory和Cglib两种方式来实现AOP。

  我们来看一个示例,还是使用上面定义的UserDaoImpl以及UserDaoImpl2。

  首先需要定义一个interceptor:

 1 @Aspect
2 public class MyInterceptor {
3
4 @Pointcut("execution (* sample.spring.aop.*.*(..))")
5 public void anyMethod(){}
6
7 @Before("anyMethod()")
8 public void before()
9 {
10 System.out.println("Before");
11 }
12
13 @After("anyMethod()")
14 public void after()
15 {
16 System.out.println("After");
17 }
18
19 @Around("anyMethod()")
20 public void Around(ProceedingJoinPoint pjp) throws Throwable
21 {
22 long start = System.currentTimeMillis();
23 pjp.proceed();
24 long end = System.currentTimeMillis();
25 System.out.println("执行时间:" + (end - start));
26 }
27
28 @Before("anyMethod() && args(name)")
29 public void before(String name)
30 {
31 System.out.println("The name is " + name);
32 }
33
34 @AfterReturning(pointcut="anyMethod()", returning="result")
35 public void afterReturning(String result)
36 {
37 System.out.println("The value is " + result);
38 }
39
40 @AfterThrowing(pointcut="anyMethod()", throwing="e")
41 public void afterThrowing(Exception e)
42 {
43 e.printStackTrace();
44 }
45 }

  我们可以看到上面的代码中包含了一些Annotation,这些Annotation是用来实现AOP的关键。

  然后需要修改beans.xml,添加如下内容:

1 <aop:aspectj-autoproxy />
2 <bean id="userDaoImpl" class = "sample.spring.aop.UserDaoImpl"/>
3 <bean id="userDaoImpl2" class = "sample.spring.aop.UserDaoImpl2"/>
4 <bean id="myInterceptor" class="sample.spring.aop.MyInterceptor"/>

  其中第一行是让Spring打开AOP的功能,下面三行定义了三个bean,这里我们把interceptor也看做是一个bean对象。

  接下来是测试代码:

 1 private static void test3() throws InterruptedException
2 {
3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/aop/beans.xml");
4 UserDao userDao = (UserDao)ctx.getBean("userDaoImpl");
5 userDao.save();
6 UserDaoImpl2 userDao2 = (UserDaoImpl2)ctx.getBean("userDaoImpl2");
7 userDao2.save();
8 userDao2.setName("Zhang San");
9 String name = userDao2.getName();
10 // userDao2.raiseException();
11 }

  这里我们可以看到,测试方法中既使用了UserDaoImpl1(这里是UserDao接口),也是用了UserDaoImpl2。正如我们上面所言,在Spring中,如果类实现了接口,Spring会按照ProxyFactory的方式来处理;如果没有实现接口,Spring会按照Cglib的方式来处理。

  上面测试方法的输出如下:

Before
Before
save() is called for null
执行时间:1
The value is null
After
After
执行时间:1
The value is null
Before
Before
save() is called for null
执行时间:3001
The value is null
After
After
执行时间:3002
The value is null
Before
The name is Zhang San
Before
执行时间:26
The value is null
After
After
执行时间:27
The value is null
Before
Before
执行时间:0
The value is null
After
After
执行时间:1
The value is null

  使用Spring配置文件来配置AOP

  上面的示例中,我们使用Annotation来配置AOP的信息,同样我们也可以使用xml文件的方式来配置AOP。

  还是以上面定义的interceptor为基础,我们去掉里面所有的Annotation,然后在beans.xml中添加如下内容:

 1 <bean id="myInterceptor2" class="sample.spring.aop.MyInterceptor2"/>
2 <aop:config>
3 <aop:aspect id="asp" ref="myInterceptor2">
4 <aop:pointcut id="anyMethod" expression="execution (* sample.spring.aop.*.*(..))"/>
5 <aop:before pointcut-ref="anyMethod" method="before"/>
6 <aop:after pointcut-ref="anyMethod" method="after"/>
7 <aop:around pointcut-ref="anyMethod" method="around"/>
8 <aop:after-returning pointcut-ref="anyMethod" method="afterReturning" returning="result"/>
9 <aop:after-throwing pointcut-ref="anyMethod" method="afterThrowing" throwing="e"/>
10 </aop:aspect>
11 </aop:config>

  测试方法和输出结果同上。

Spring 之 AOP的更多相关文章

  1. Spring基于AOP的事务管理

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

  2. Spring实现AOP的4种方式

    了解AOP的相关术语:1.通知(Advice):通知定义了切面是什么以及何时使用.描述了切面要完成的工作和何时需要执行这个工作.2.连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“ ...

  3. spring的AOP

    最近公司项目中需要添加一个日志记录功能,就是可以清楚的看到谁在什么时间做了什么事情,因为项目已经运行很长时间,这个最初没有开来进来,所以就用spring的面向切面编程来实现这个功能.在做的时候对spr ...

  4. Spring(五)AOP简述

    一.AOP简述 AOP全称是:aspect-oriented programming,它是面向切面编号的思想核心, AOP和OOP既面向对象的编程语言,不相冲突,它们是两个相辅相成的设计模式型 AOP ...

  5. Spring中AOP原理,源码学习笔记

    一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...

  6. Spring之AOP面向切片

       一.理论基础: AOP(Aspectoriented programming)面向切片/服务的编程,在Spring中使用最多的是对事物的处理.而AOP这种思想在程序中很多地方可以使用的,比如说, ...

  7. 利用CGLib实现动态代理实现Spring的AOP

    当我们用Proxy 实现Spring的AOP的时候, 我们的代理类必须实现了委托类的接口才能实现. 而如果代理类没有实现委托类的接口怎么办? 那么我们就可以通过CGLib来实现 package cn. ...

  8. spring之aop概念和配置

    面向切面的一些概念: 简单说: 连接点就一些方法,在这些方法基础上需要额外的一些业务需求处理. 切入点就是方法所代表的功能点组合起来的功能需求. 通知就是那些额外的操作. 织入就是使用代理实现整个切入 ...

  9. Spring的AOP与代理

    spring 支持两种注入方式: setter/constructor 支持多种配置方式: xml/java5注解/java类配置 支持两种事务管理: 声明性/编程性 实际上上述方式只有一个就能保证系 ...

  10. Spring 实践 -AOP

    Spring 实践 标签: Java与设计模式 AOP引介 AOP(Aspect Oriented Programing)面向切面编程采用横向抽取机制,以取代传统的纵向继承体系的重复性代码(如性能监控 ...

随机推荐

  1. TS流解析 一

    一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video.Audio以及我们需要学习的PAT.PMT等 ...

  2. week1-绪论

    一 .作业题目 仿照三元组或复数的抽象数据类型写出有理数抽象数据类型的描述 (有理数是其分子.分母均为整数且分母不为零的分数). 有理数基本运算: 构造有理数T,元素e1,e2分别被赋以分子.分母值 ...

  3. 解决标准FPGA资源丰富却浪费的问题

    FPGA以计算速度快.资源丰富.可编程著称,之前一直应用于高速数字信号领域和ASIC验证.随着逻辑资源的丰富和编程工具的改进,FPGA在机器学习和硬件加速上得到越来越多的重视,目前数据中心已经大量采用 ...

  4. [转][ASP.net]后台页面刷新

    三种后台刷新页面的方法: // 刷新方法一 Response.AddHeader("); // 刷新方法二 Response.Write("<script language= ...

  5. python 多态、多继承、函数重写、迭代器

    用于类的函数 issubclass(cls,class_or_tuple) 判断一个类是否继承自其他的类,如果此类cls是class或tuole中的一个派生(子类)则返回True,否则返回False ...

  6. 十二.jQuery源码解析之.eq().first().last().slice()

    eq(index):将集合中的索引为index的元素提取出来. first():返回集合中的第一个元素. .last():防护集合中的最后一个元素. .slice(start[,end]):返回集合中 ...

  7. 第2课 GUI程序实例分析

    1. GUI程序开发概述 (1)现代操作系统提供原生SDK支持GUI程序开发 (2)GUI程序开发是现代操作系统上的主流技术 (3)不同操作系统上的GUI开发原理相同 (4)不同操作系统上的GUI S ...

  8. python报头解决粘包简单实现

    client------------- #!/usr/bin/env python # encoding: utf-8  # Date: 2018/6/5 import socketimport st ...

  9. cpu真实核数

    判断依据: 1.具有相同core id的cpu是同一个core的超线程. 2.具有相同physical id的cpu是同一颗cpu封装的线程或者cores. 英文版: 1.Physical id an ...

  10. 关于eclipse open call hierarchy功能的一个细节

    这个功能对应的快捷键是ctrl alt H,大家应该都很熟悉了.默认是查找这个方法的被调用堆栈.90%的人应该也是习惯这个默认的功能的,也基本无视它的另一个功能. 昨天重启eclipe之后,我的ecl ...