zoukankan      html  css  js  c++  java
  • 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实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯VIP基础练习 矩形面积交
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    核心思想:想清楚自己创业的目的(如果你没有自信提供一种更好的产品或服务,那就别做了,比如IM 电商 搜索)
    在Linux中如何利用backtrace信息解决问题
  • 原文地址:https://www.cnblogs.com/CharmingDang/p/9663769.html
Copyright © 2011-2022 走看看