SpringAOP之学习总结

来源:互联网 发布:ipad扩展屏幕windows 编辑:程序博客网 时间:2024/05/16 12:18

下面是我学习时遇到的几个例子。

第一个:这是一个简单的IOC的例子:

用户买书的接口类:

package com.focus.aop;

public interface BuyBook {

    public void buyBook(String customer,String book);

}

用户买书的接口实现类:

package com.focus.aop;

public class BuyBookImpl implements BuyBook{

    public void buyBook(String customer,String book) {

        System.out.println(customer+"你好!你成功购了一本"+book+"!");

    }

}

用户买书的测试类

package com.focus.aop;

import org.springframework.context.ApplicationContext;

public class Test {

    public static void main(String args[]) throws Exception{

        ApplicationContext factory = new FileSystemXmlApplicationContext("beans-b.xml");

        BuyBook b = (BuyBook)factory.getBean("buyBook");

        b.buyBook("小东", "《楚留香》");

    }

}

Beans配置文件beans-b.xml

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

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="buyBook" class="com.focus.aop.BuyBookImpl"/>

</beans>

好了,此时运行结果应该是:

小东你好!你成功购了一本《楚留香》!

如果不是这个结果的话应该是你某些jar包没加进来或者文件位置等没放好。反正我这是运行正确了。

开始分析这个例子,我一直有个问题就是从网上找的例子几乎都是先有一个接口,然后有个实现类,然后一般用那个实现类。这是为什么呢?直接定义一个类用不就完了嘛?

我对这个问题的理解是:这应该就是应用Spring的目的,如果直接定义一个类用的话我们完全没必要用Spring。试想如果有有很多类实现了那个接口,我们想用哪个类就直接把那个类换到xml配置文件中去,甚至不用改代码就应用了其他类的方法。(这似乎就是传说中的工厂模式,不过我对设计模式也没多少了解,姑且这么认为吧呵呵!)

运行成功的话,开始添加我们的AOP第一个例子。

AOP暂且可以认为就是可以将某个方法拦截,可以获得这个方法的相关参数和返回值。并且可以在这个方法运行前运行后运行中添加自己想要的功能。激动了吧?下面看例子:

在买书前加入新功能(欢迎光临!xxx):

增加买书前添加功能的类MyBeforeAdvice:

package com.focus.aop;

import org.springframework.aop.MethodBeforeAdvice;

public class MyBeforeAdvice implements MethodBeforeAdvice{

    public void before(Method arg0, Object[] arg1, Object target) throws Throwable {

     String customer = (String)arg1[0];

        System.out.println("欢迎光临!"+customer+"!");

    }

}

修改配置文件beans-b.xml

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

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="buyBook" class="com.focus.aop.BuyBookImpl"/>

    <bean id="myBeforeAdvice" class="com.focus.aop.MyBeforeAdvice"/>

<bean id="newBuyBook" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="proxyInterfaces" value="com.focus.aop.BuyBook"/>

        <property name="interceptorNames">

            <list>

                  <value>myBeforeAdvice</value>

            </list>

        </property>

        <property name="target" ref="buyBook"/>

    </bean>

</beans>

修改测试类:

将BuyBook b = (BuyBook)factory.getBean("buyBook");

改为BuyBook b = (BuyBook)factory.getBean("newBuyBook");

好了,然后点运行看是不是在那个方法之前有变化。运行结果应该为:

欢迎光临!小东!

小东你好!你成功购了一本《楚留香》!

!知识点:用这种方法的xml配置文件的话。我们要手动将代理类org.springframework.aop.framework.ProxyFactoryBean 加载进来。并且要将生成类实例的代码BuyBook b = (BuyBook)factory.getBean("newBuyBook");中getBean()的参数改为新增加代理类的那个beanid。有点意思了吧?

另外新增的那个bean下面的几个property是干什么的?我也不懂,不过你按着这个位置和单词应该也有些理解把?下面是我的理解:

第一个property

name="proxyInterfaces" value="com.focus.aop.BuyBook"

应该是配置为哪个接口加个代理。此例中就是为BuyBook接口加代理。(应该也可以直接为某个类加代理。Name是不是proxyClass我就不知道了呵呵。)

第二个property就是添加我们想要的运行前后增加的各种方法。

第三个property应该就是传说中的目标对象就是在哪个类方法的前后增加方法。

然后想要在方法执行后加效果:再写个买书后添加功能的类MyAfterAdvice:

package com.focus.aop;

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 {

System.out.println("Good Bye"+arg2[0]);

}

}

并修改配置文件beans-b.xml:

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

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="buyBook" class="com.focus.aop.BuyBookImpl"/>

    <bean id="myBeforeAdvice" class="com.focus.aop.MyBeforeAdvice"/>

<bean id="myAfterAdvice" class="com.focus.aop.MyAfterAdvice"  />

<bean id="newBuyBook" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="proxyInterfaces" value="com.focus.aop.BuyBook"/>

        <property name="interceptorNames">

            <list>

                  <value>myBeforeAdvice</value>

  <value>myAfterAdvice</value>

            </list>

        </property>

        <property name="target" ref="buyBook"/>

    </bean>

</beans>

然后运行结果应该为:

欢迎光临!小东!

小东你好!你成功购了一本《楚留香》!

Good Bye小东

神奇吧!估计做完这几步,再分析分析我们添加方法前后的类MyBeforeAdviceMyAfterAdvice以及里面的方法和修改的xml文件你应该有点感觉了吧。

你应该发现我们添加的类都是继承自某个接口,这些接口就是传说中的aop核心子接口:

他们分别是: 

(前置通知) MethodBeforeAdvice ----methodA函数调用前执行用户定义的方法 

(后置通知) AfterReturningAdvice ----- methodA函数调后执行用户定义的方法 
(环绕通知)MethodInterceptor -------彻底变更MethodA函数为用户定义的方法 
(异常通知)ThrowsAdvice------methodA函数调用出现异常执行用户定义的方法 

也叫前置增强,后置增强、、、、、、

当然,我们需要更深的了解一下这几个接口定义的方法中的的参数才能更好的对目标方法进行拦截。

同理:把目标方法的运行过程给改了大家应该知道怎么做了吧。最好自己先摸索一下。当然我还是给出步骤来吧。首先还是添加通知类MyMethodInterceptor  :

package com.focus.aop;

import java.util.*;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

public class MyMethodInterceptor implements MethodInterceptor{

    private Set customers = new HashSet();

    @Override

    public Object invoke(MethodInvocation invocation) throws Throwable {

        String customer = (String)invocation.getArguments()[0];

        Object result = null;

        if(customers.contains(customer)){

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

        } else{

             result = invocation.proceed();

        }

        customers.add(customer);

        return result;

    }

}

修改配置文件beans-b.xml

添加一个bean<bean id="myMethodInterceptor" class="com.focus.aop.MyMethodInterceptor"  />

然后在list中添加<value>myMethodInterceptor</value>。接着测试类的主方法中再添加一句:  

b.buyBook("小东", "《楚留香》2");

运行结果是:

欢迎光临!小东!

小东你好!你成功购了一本《楚留香》!

Good Bye小东

欢迎光临!小东!

注意,一名顾客只能买一本打折书!

Good Bye小东

此时是不是感觉好像会用AOP了啊!不要激动,这只是个开始。

(上面的例子出自http://honda418.iteye.com/blog/339468 ,谢谢原作者。)

上面说的方法中配置文件是我们自己引入代理类。那么怎么能让配置文件自己引进来不用我们动手呢?你应该见过一些配置文件中有aop标签吧!没错,就是这个东西。继续找我们的小例子。

还是先来一个接口UserManager:
package cn.java.aop;

public interface UserManager {

 public void addUser(String username, String password);

 public void deleteUser(int id);

 public void modifyUser(int id, String username, String password);

 public String findUserById(int id);

}

再来个实现类UserManagerImpl:

package cn.java.aop;

public class UserManagerImpl implements UserManager {

public void addUser(String username, String password) {

  System.out.println("-------UserManagerImpl.addUser()----------");

 }

 public void deleteUser(int id) {

  System.out.println("-------UserManagerImpl.deleteUser()----------");

 }

 public String findUserById(int id) {

  System.out.println("-------UserManagerImpl.findUserById()----------");

  return null;

 }

 public void modifyUser(int id, String username, String password) {

  System.out.println("-------UserManagerImpl.modifyUser()----------");

 }

}

再添加一个测试类Test

package cn.java.aop;

import org.springframework.beans.factory.BeanFactory;

public class Test {

 public static void main(String[] args) {

  BeanFactory factory = new ClassPathXmlApplicationContext("beans-c.xml");

  UserManager userManager = (UserManager)factory.getBean("userManager");

  userManager.addUser("张三", "123");

  userManager.deleteUser(1);

 }

}

再增加一个前置通知吧!

package cn.java.aop;

import org.aspectj.lang.JoinPoint;

public class SecurityHandler {

private void doBefore(JoinPoint joinPoint) {

System.out.println("----------checkSecurity()--------------");

}

(是不是发现没实现那些个接口啊!不急、稍后说。。。)

好了然后就是配置文件:beans-c.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"

      xmlns:aop="http://www.springframework.org/schema/aop"

      xsi:schemaLocation="

  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<bean id="securityHandler" class="cn.java.aop.SecurityHandler"/>

 <bean id="userManager" class="cn.java.aop.UserManagerImpl"/>

</beans>

运行,结果应该为:

-------UserManagerImpl.addUser()----------

-------UserManagerImpl.deleteUser()----------

这是没添加前置通知时的结果,然后我们通过修改配置文件将前置通知的方法加进来:

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

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

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

      xmlns:aop="http://www.springframework.org/schema/aop"

      xsi:schemaLocation="

  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<bean id="securityHandler" class="cn.java.aop.SecurityHandler"/>

<bean id="userManager" class="cn.java.aop.UserManagerImpl"/>

<aop:config>

<aop:pointcut id="allAddMethod" expression="execution(* cn.java.aop.UserManager.*(..))"/>

<aop:aspect id="security" ref="securityHandler">

    <aop:before method="doBefore" pointcut-ref="allAddMethod"/>

</aop:aspect>

</aop:config>

</beans>

再运行结果是这样的吧:

----------checkSecurity()---------------

-------UserManagerImpl.addUser()----------

----------checkSecurity()---------------

-------UserManagerImpl.deleteUser()----------

(这个例子引自http://blog.sina.com.cn/s/blog_633532140100gjyu.html 虽然作者也是转载的不过还是说明一下!)

可见在每个方法运行前都有输出一句----------checkSecurity()---------------

效果是达到了,可是感觉配置文件里的东西有些不明白。不明白就对了,刚看我也不明白,那就分析呗。 首先第一行:

<bean id="securityHandler" class="cn.java.aop.SecurityHandler"/>

这个bean是我们要添加通知的那个类。好像叫做注入切面。不用管这个,反正就是我们要添加方法的那个类。这种方法下就声明一个类就行了。而且不用每次都实现接口,需要添加什么方法直接在这个类中添加就行了。可以在配置文件中指名哪个方法是添加在前面哪方法是添加在后面的。继续分析代码:

<bean id="userManager" class="cn.java.aop.UserManagerImpl"/>

这个类就是包含我们目标方法那个类。就是拦截里面的方法并肆意虐待、、、、、、

然后就是最关键的aop了:

<aop:config>

<aop:pointcut id="allAddMethod" expression="execution(* cn.java.aop.UserManager.*(..))"/>

<aop:aspect id="security" ref="securityHandler">

    <aop:before method="doBefore" pointcut-ref="allAddMethod"/>

</aop:aspect>

</aop:config>

里面第一行的<aop:pointcut……标签是传说中的切入点,可以认为是一个筛子,筛选出符合条件的目标方法。Expression的值类似与一个正则表达式。就是指cn.java.aop包里面的UserManger类的所有方法,最前面有个*我也不知道啥意思,最后面括号有俩点我也不知道啥意思,先按规矩来把。有空了再研究。

然后<aop:aspect……这个标签就是配置文件里面指名哪些方法在目标方法前,哪些方法在目标方法后的。那么ref="securityHandler"这个属性大家应该理解了吧。

<aop:before method="doBefore" pointcut-ref="allAddMethod"/>

这一句中pointcut-ref="allAddMethod"就是声明用与哪个筛子筛出来的方法。method="doBefore"是我们要添加方法的类SecurityHandler中的方法。

同理如果我们想在目标方法的执行后再来一个后置通知。我们就可以在SecurityHandler类中写一个方法例如名字叫做doAfter();并且在

<aop:aspect id="security" ref="securityHandler">

     <aop:before method="doBefore" pointcut-ref="allAddMethod"/>

</aop:aspect>

里面添加上就可以了

<aop:aspect id="security" ref="securityHandler">

     <aop:before method="doBefore" pointcut-ref="allAddMethod"/>

<aop:after method="doAfter" pointcut-ref="allAddMethod"/>

</aop:aspect>

同理如果想添加环绕通知那么在SecurityHandler中添加环绕方法,并在xml配置文件中添加<aop:around method="……" pointcut-ref="allAddMethod"/>就可以了

另外注意如果是环绕方法的话是区分目标方法前和后的,有前后才叫环绕嘛。

举一个例子:

public Object doAround(ProceedingJoinPoint jp)throws Throwable{

System.out.println("环绕前:");

Object obj = jp.proceed();

//就这里。环绕前后的中间必须调用proceed();方法来将前后分开。其实这句话就是将目标函数执行。并返回Object类型的返回值。

System.out.println("环绕后:");

return obj;

}

好了,第二种配置文件的配置方法也说完了是不是感觉比第一种简单多了。不用实现接口

也不用定义那么多类。而且你是否发现,自己定义的方法好像没有参数,怎么来拿到我们拦截的目标方法的参数,返回值等相关信息呢。那就是上面 doAround例子中的参数:ProceedingJoinPoint  和JoinPoint  类的对象可作为任何通知的参数,关于这两个类里面的方法,你可以查api也可以google不过我还是给个连接吧:http://mqiqe.iteye.com/blog/1341362 。同样谢谢作者。

好了!终于写完了。累死了。。。

另外再给大家推荐一篇文章,感觉不错:http://blog.csdn.net/hanxiaoshuang321123/article/details/7479065

原创粉丝点击