博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 之 AOP
阅读量:6294 次
发布时间:2019-06-22

本文共 6401 字,大约阅读时间需要 21 分钟。

面向方面的编程,即 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         else22         {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         else21         {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 Throwable21     {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 
2
3
4

  其中第一行是让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的方式来处理。

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

BeforeBeforesave() is called for null执行时间:1The value is nullAfterAfter执行时间:1The value is nullBeforeBeforesave() is called for null执行时间:3001The value is nullAfterAfter执行时间:3002The value is nullBeforeThe name is Zhang SanBefore执行时间:26The value is nullAfterAfter执行时间:27The value is nullBeforeBefore执行时间:0The value is nullAfterAfter执行时间:1The value is null

  使用Spring配置文件来配置AOP

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

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

1 
2
3
4
5
6
7
8
9
10
11

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

转载于:https://www.cnblogs.com/doudouxiaoye/p/5693383.html

你可能感兴趣的文章
政府安全资讯精选 2017年第十六期 工信部发布关于规范互联网信息服务使用域名的通知;俄罗斯拟建立备用DNS;Google打击安卓应用在未经同意情况下收集个人信...
查看>>
简单易懂的谈谈 javascript 中的继承
查看>>
iOS汇编基础(四)指针和macho文件
查看>>
Laravel 技巧锦集
查看>>
Android 使用 ViewPager+RecyclerView+SmartRefreshLayout 实现顶部图片下拉视差效果
查看>>
Flutter之基础Widget
查看>>
写给0-3岁产品经理的12封信(第08篇)——产品运营能力
查看>>
ArcGIS Engine 符号自动化配置工具实现
查看>>
小程序 · 跳转带参数写法,兼容url的出错
查看>>
flutter error
查看>>
Flask框架从入门到精通之模型数据库配置(十一)
查看>>
10年重新出发
查看>>
2019年-年终总结
查看>>
聊聊elasticsearch的RoutingService
查看>>
让人抓头的Java并发(一) 轻松认识多线程
查看>>
从源码剖析useState的执行过程
查看>>
地包天如何矫正?
查看>>
中间件
查看>>
Android SharedPreferences
查看>>
css面试题
查看>>