您的位置:首页 > 编程语言 > Java开发

Spring 之 AOP

2016-07-21 22:24 330 查看
面向方面的编程,即 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>


  测试方法和输出结果同上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: