• Spring 之 AOP


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

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

  • 相关阅读:
    CentOS8 安装 Java JDK
    小程序问题汇总
    CSS实现侧边栏固定宽度,内容栏自适应
    垂直居中总结
    移动端Web App自适应布局探索
    学习指南
    插件集
    移动端滑动事件
    网站收藏
    js void运用
  • 原文地址:https://www.cnblogs.com/doudouxiaoye/p/5693383.html
Copyright © 2020-2023  润新知