Spring 之 AOP

本文参考文章:http://www.cnblogs.com/wing011203/archive/2013/05/15/3078849.html

面向方面的编程,即 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)