• spring AOP(2)


     

           前面写过一篇关于Spring AOP方面的文章,探讨了Spring AOP底层实现的一些细节知识,这里面涉及到了JAVA反射机制,代理模式以及CGLIB库的使用。也就是说,Spring AOP底层实现就是靠动态代理(针对有接口的类)和CGLIB(针对没有实现接口的一般类),那么,有了这些知识,再辅佐对核心配置XML文件解析的能力,其实就可以实现一个简易的基于IOCAOP的小框架,大家可以自己尝试着写一下。下面呢我们就由浅入深地来看看在SpringAOP是怎么实现的。

     

           最简单的AOP实现只需要涉及3个概念:目标(Target),通知(Advice)和代理(Proxy)。目标呢,当然就是真实的需要被代理的对象,一般它会实现至少一个接口。通知呢,就是当目标的方法调用时需要调用的代码,也叫拦截器。而代理,毫无疑问就是加入了通知的目标了,它可以作为目标的替身出现。为了说明这三者的关系,我们来看一个网上有趣的小例子:一间书店开始打折促销,规则是每一名顾客只能买一本书,并且当顾客来到书店时,要说喜欢您来。顾客走的时候,还要说喜欢您再来!(麦当劳啊^_^ 顾客如果买到<hibernate in action>这本书,要抛出异常,告知他没有存货!呵呵,好啦,洪哥,我们动手吧!

     

    package com.wepull.spring.book;

     

    public class NoThisBookException extends Exception {

     

           public NoThisBookException(String msg) {

                  super(msg);

           }

     

    }

     

    package com.wepull.spring.book;

     

    public interface BuyBook {

           public void buyBook(String customer,String book) throws NoThisBookException;

    }

     

    package com.wepull.spring.book;

     

    public class MyBuyBook implements BuyBook{

     

           public void buyBook(String customer, String book)

                         throws NoThisBookException {

                  if(book.equals("<hibernate in action>"))

                  throw new NoThisBookException("对不起,没有"+book+"的存货了!");

           System.out.println(customer+",你好,你已经购买了一本"+book+"!");

                 

           }

          

    }

    看上面,我们做了一个异常类和一个简单买书的接口,并且目标MyBuyBook已经出现。OK,再来看看通知吧!

     

    Spring中主要有以下四种通知类型:

    1Around通知

    2Before通知

    3Throws通知

    4After Returning通知

     

    例子分别如下:

    package com.wepull.spring.book;

     

    import java.util.HashSet;

    import java.util.Set;

    import org.aopalliance.intercept.MethodInterceptor;

    import org.aopalliance.intercept.MethodInvocation;

     

    public class MyAroundAdvice implements MethodInterceptor {

        private Set customers=new HashSet(); //保存购过书的顾客信息

     

        public Object invoke(MethodInvocation invocation) throws Throwable {

            Object[] args= invocation.getArguments();

            if(customers.contains(args[0])){

                System.out.println("对不起,一名顾客只能买一本打折书!");

                return null;

            }

            customers.add(args[0]);

            return invocation.proceed();

        }

     

    }

    package com.wepull.spring.book;

     

    import java.lang.reflect.Method;

    import org.springframework.aop.MethodBeforeAdvice;

     

    public class MyBeforeAdvice implements MethodBeforeAdvice {

     

        public void before(Method arg0, Object[] arg1, Object arg2)

                throws Throwable {

            String customer=(String)arg1[0]; //2个参数组就是被通知的方法传入的参数,本例中即customer,book

            System.out.println("喜欢您来!"+customer+"!"); //显示欢迎信息!,在buyBook方法前调用

     

     

        }

     

    }

    package com.wepull.spring.book;

     

    import java.lang.reflect.Method;

    import org.springframework.aop.ThrowsAdvice;

     

    public class MyThrowsAdvice implements ThrowsAdvice {

     

        public void afterThrowing(Method method, Object[] args, Object target, NoThisBookException e){

            //可以定义多个方法,只要传入的参数是不同异常

            System.out.println("对不起"+args[0]+",没货了。通知仓库,赶紧加书!");

     

        }

    }

    package com.wepull.spring.book;

     

    import java.lang.reflect.Method;

    import org.springframework.aop.AfterReturningAdvice;

     

    public class MyAfterAdvice implements AfterReturningAdvice {

     

        public void afterReturning(Object arg0, Method arg1, Object[] arg2,

                Object arg3) throws Throwable {

            String customer=(String)arg2[0]; //同前置通知一样,参数组3为传入参数,具体见spring doc

            System.out.println("喜欢您再来!"+customer+"!"); //显示欢送信息!

     

     

        }

     

    }

     

    现在目标类有了,通知类也写好了,写了就要配,这是框架的基本使用原则。呵呵,下面就来看看核心配置文件src/applicationContext.xml

     

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

        <!-- 各种通知 -->

        <!-- 前置通知 -->

        <bean id="myBeforeAdvice"

            class="com.wepull.spring.book.MyBeforeAdvice">

        </bean>

        <!-- 后置通知 -->

        <bean id="myAfterAdvice"

            class="com.wepull.spring.book.MyAfterAdvice">

        </bean>

        <!-- 异常通知 -->

        <bean id="myThrowsAdvice"

            class="com.wepull.spring.book.MyThrowsAdvice">

        </bean>

        <!-- 环绕通知 -->

        <bean id="myAroundAdvice"

            class="com.wepull.spring.book.MyAroundAdvice">

        </bean>

        <!-- 目标对象 -->

        <bean id="buyBookTarget"

            class="com.wepull.spring.book.MyBuyBook">

        </bean>

        <!-- 代理对象 -->

    <!-- ProxyFactoryBean被用于创建代理 -->

        <bean id="buyBook"

            class="org.springframework.aop.framework.ProxyFactoryBean">

            <!-- 实现的接口 -->

            <property name="proxyInterfaces">

                <value>com.wepull.spring.book.BuyBook</value>

            </property>

            <!-- 应用所有 (通知)拦截器对象到所有调用 -->

            <property name="interceptorNames">

                <list>

                    <value>myBeforeAdvice</value>

                    <value>myAfterAdvice</value>

                    <value>myThrowsAdvice</value>

                    <value>myAroundAdvice</value>

                </list>

            </property>

            <!-- 代理的目标对象 -->

            <property name="target">

                <ref bean="buyBookTarget" />

            </property>

        </bean>

    </beans>

     

    注意上面的ProxyFactoryBean类是在BeanFactory中显式地创建代理对象的中心类。你给它一个要实现的接口,一个要代理的目标对象,若干个要织入的通知,它就会创建一个崭新的代理对象。所以通常配置ProxyFactoryBean,让它实现和目标对象一样的接口。最后当然是测试代码了。

     

    package com.wepull.spring.book;

     

    import org.springframework.context.ApplicationContext;

    import org.springframework.context.support.ClassPathXmlApplicationContext;

     

    public class Test {

     

        public static void main(String[] args) {

            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

            BuyBook buyBook = (BuyBook) context.getBean("buyBook");

            try {

                buyBook.buyBook("leno", "<struts in action>");

                buyBook.buyBook("leno", "<spring in action>");

                buyBook.buyBook("lenotang", "<hibernate in action>");

            } catch (NoThisBookException e) {

                e.printStackTrace();

            }

        }

    }

     

           这就是Spring中最简单的AOP应用了。怎么样,并不是很难吧。大家已经看到,如果我们的接口里面定义了很多方法,那么所有的方法都会置入这些通知,但有时候我们是希望有所选择的,怎么改进呢?还有,如果我们还有很多个这样需要置入这些通知的对象存在,那针对每一个目标都要做一个几乎一样的代理的配置,似乎也欠妥。那么,如何优化呢。这就是我们下一篇文章的主题了。呵呵,继续前进吧!

  • 相关阅读:
    [Java入门] 从键盘输入两个整数,求最小公倍数并输出
    【C#食谱】【面食】菜单3:用泛型替代ArrayList
    C# Stable Sort(稳固排序)
    【C#食谱】【面食】菜单4:List和LinkedList性能比较
    一个非常酷的应用:修改“开始”菜单的显示文本
    我的第一个5年计划
    C#语言重要常识
    决定执行力的49个细节
    .NET资源链接
    标准表达式中数据类型不匹配(Access 时间格式)
  • 原文地址:https://www.cnblogs.com/CharmingDang/p/9663769.html
Copyright © 2020-2023  润新知