面向方面的编程,即 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. XSS漏洞攻击原理与解决办法

    转自:http://www.frostsky.com/2011/10/xss-hack/ 对于的用户输入中出现XSS漏洞的问题,主要是由于开发人员对XSS了解不足,安全的意识不够造成的.现在让我们来普 ...

  2. intellij系列ide配置

    显示行号 搜索line number 在Editor,General,Appearance里面,勾选show line numbers 修改自体 sudo apt-get install fonts- ...

  3. (转)TreeSet简单介绍与使用方法

    TreeSet简介 TreeSet是JAVA中集合的一种,TreeSet 是一个有序的集合,它的作用是提供有序的Set集合.它继承于AbstractSet抽象类,实现了NavigableSet< ...

  4. umount时目标忙解决办法

    标签(空格分隔): ceph ceph运维 osd 在删除osd后umount时,始终无法umonut,可以通过fuser查看设备被哪个进程占用,之后杀死进程,就可以顺利umount了. [root@ ...

  5. Lucene 中的Tokenizer, TokenFilter学习

      lucene中的TokenStream,TokenFilter之间关系   TokenStream是一个能够在被调用后产生语汇单元序列的类,其中有两个类型:Tokenizer和TokenFilte ...

  6. MyBatis框架简介

    1.下载地址:下载地址:https://github.com/mybatis/mybatis-3/releases 2.MyBatis是什么? MyBatis 本是apache的一个开源项目iBati ...

  7. VueRouter

    使用VueRouter的前提: 1, 必须导入vue-router.js文件    2, 要有VueRouter()实例    3, 要把VueRouter实例挂载到Vue实例中 4, 路由的入口   ...

  8. uva-11111-栈

    注意输入和输出的结果 -9 -7 -2 2 -3 -2 -1 1 2 3 7 9 -9 -7 -2 2 -3 -1 -2 2 1 3 7 9-9 -7 -2 2 -3 -1 -2 3 2 1 7 9- ...

  9. python 简单爬虫获取气象数据发送气象定时报-预报预警信息及时推送及阿里云短信群发接口

    !/usr/bin/python #encoding=utf-8 #Author:Ruiy #//////////////////////////////////////////////////// ...

  10. leetcode917

    class Solution { public: string reverseOnlyLetters(string S) { int len = S.length(); queue<char&g ...