标题:Spring AOP
只看楼主
huwangvs
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:34
帖 子:764
专家分:0
注 册:2007-2-4
结帖率:0
 问题点数:0 回复次数:2 
Spring AOP
在论坛混了一年多,没写过一篇文章 本人较懒,也在学习当中,不敢乱发贴。以免害人。不过老不发贴对不起论坛,毕竟在论坛里捞了不少知识。就写一点学习心得吧。仅供参考。
不知这算不算原创。挂名吧!

我想大家都很少写Log的吧。我至今好像就没用过。-_-!!不过相信写过代码的一定写过这样的代码!
if(username.trim().equals("...") && password.trim().equals("...")){
    ...
}


当你开发一个项目的时候,你会发现这段代码不断的在重复着。我明明只需要一个打印的,你非要加上好几行的检验代码。会不会很累呢?
我们可不可以把安全代码抽出来?Head First设计模式第一章最后讲的就是,把变化的东西和不变的东西分离出来。
先看没分离的原始代码。

public interface ILogin{
    public void sayHello(String name);
}
对于业务类来说,基本上都是有接口的。

public class Login implements ILogin{
    public void sayHello(String name){
        if(name.equals("Ivan")){//这里可能是从数据库查询再检验,省略。。。
            System.out.println("Hello", + name);
        }
    }
}
如果有很多这种检验的话,那重复代码就很多了。
下面就开始对这段代码分离。Login类业务代码很简单,就是打印。那么Login就只管业务就行了。
public class Login implements ILogin{
    public void sayHello(String name){
        System.out.println("Hello", + name);
    }
}
那么安全代码到哪里去检验呢?那就要使用代理模式了。代理实现很简单。只要和被代理类实现同样的接口就可以了。代理类里面再保存一个目标对象的引用就OK了。
public class LoginProxy implements ILogin{
    private ILogin login;
    public LoginProxy(ILogin login){
        this.login = login;
    }
    public void sayHello(String name){
        if(name.equals("Ivan")){
            login.sayHello(name);
        }
    }
}
在调用的时候就不是直接去new Login了,而是new ILogin.
public class Test{
    public static void main(String[] args){
        ILogin login = new LoginProxy(new Login());
        login.sayHello("Ivan");
    }
}
这样就实现了分离。很明显,代码好像比之前的多了很多。。。。但是如果你有的Login需要检验,有的不需要检验,那修改起来是不是很方便?如果以后要去掉检验或加上检验是不是也很方便?只需要改main里的代码就行了。
当然了,弊端是显而易见的。一个目标接口需要一个代理类,如果项目比较大,有很多接口,那么一个检验类就要好几个,也不方便。于是就有了动态代理。(上面的叫静态代理)
动态代理实现也很简单。只需要继承java.lang.reflect.InvocationHandler接口就可以了。
public DanamicLoginProxy implements InvocationHandler{
    private Object target ; //相当于静态代理的private ILogin login;

    public Object bindTarget(Object target){ //相当于静态代理的构造方法
        this.target = target;
        return Proxy.new ProxyInstance(target.getClass().getClassLoader(),target.getCalss().getInterfaces(),this);
    }

//覆盖的方法,他在目标方法被调用之前去调用,从名称上就能知道各个参数的作用了。proxy代理,method是目标对象要执行的方法,args就是方法里的参数,这里只有一个参数,就去args[0]
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        Object res = null;
        try{
        if(args[0].equals("Ivan")){
            res = method.invoke(target,args);
        }    
        }catch(Exception e){
            
        }
        return res;
    }
}
调用时这样调
public class Test{
    public static void main(String[] args){
        ILogin login = (ILogin)new DanamicLoginProxy().bindTarget(new Login());
        login.sayHello("Ivan");
    }
}

说了这么多的代理模式干吗呢?因为Spring的AOP就是动态代理实现的。没接触Spring的时候,还以为AOP是多深奥的东西,一看,就是个动态代理。。。。学术之美,在于让你一头雾水。。。。其实就这么回事。
在写Spring之前需要知道三个名词(至少)。。。AOP名词还不少,自己查下资料。
Aspect,PointCut,Advice.
Aspect顾名思义,切面。就是把非业务的,但是在业务代码里面会被用到的东西,写成独立的可重用的类。这里就是代理类。
Advice就是在代理类里面,对横切的那些东东的具体实现。就像动态代理类里的invoke方法。
PointCut就是在哪里去执行Adbvice。你不会已刀切的把类里的所有方法都执行检查吧?
那开始配Spring,接口和目标类都不需要改动!
检查也只需要写到一个普通类里即可。
public class Validate{
    public void validateBeforeMethod(JoinPoint point){
        Object o = point.getArgs();
        if(!o[0].equals("Ivan"){
            throws new SecurityException("检验不通过!");
        }
    }
}
这里与上面的不同就是不是测试通过执行什么,而是测试不通过就抛出异常,抛出异常后,代码就不会继续进行了。
在spring配置文件里面配置一下。
<bean id="login" class="包名.Login"/>
<bean id="secu" class="包名.Validate"/>

<aop:config>
    <aop:aspect id="security" ref="secu">
    <aop:before pointcut="execution(* *(..))" method="validateBeforeMethod"/>
    </aop:aspect>
</aop:config>
对于bean标签在使用Ioc里就用到了(看看就知道意思了。id不能重复),<aop:config>是对aop的配置。
aop:aspect配置aspect是secu,pointcut配置在哪些方法前执行method。execution(* *(..))是Spring里的匹配字符串,* *(..)表示匹配任意返回值,任意方法名,任意参数的方法。
然后调用就行了。
public class Test{
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("配置文件名称");
        ILogin login = (ILogin)context.getBean("login");
        login.sayHello("Ivan");
    }
}
可以在ILogin login = (ILogin)context.getBean("login");这里打个断点,看看得到的是什么。
搜索更多相关主题的帖子: Spring AOP 
2008-10-07 16:02
huwangvs
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:34
帖 子:764
专家分:0
注 册:2007-2-4
得分:0 
debug可以看出。ApplicationContext会读取xml配置文件。
将xml文件里的配置映射为一个线程安全的Map,context.getBean("")就是到这个map里面去取值。
取到的值是个代理,而且是jdk的动态代理。

对于Spring2.*增加了对annotation的支持,是配置更加简单,但是annotation是写在类文件里的,等于是硬编码了。所以对于经常要变化的配置建议使用xml,基本不变的配置用annotation比较方便。对与于刚才的类加几个annotation就行了。
@Aspect
public class Validate{

    @Pointcut("executation(* *(..))")
    private void run(){};

    @Before("run()")
    public void validateBeforeMethod(JoinPoint point){
        Object o = point.getArgs();
        if(!o[0].equals("Ivan"){
            throws new SecurityException("检验不通过!");
        }
    }
}
看名字和xml里的匹配就知道各自是干吗的了。这里面多了一个方法run还是private的,作用是什么?这个相当于xml里面pointcut的id,标示而已,所以用private,public也可以,不过没用的。还可以把这些private方法抽出来,放到一个类里面便于管理。
对xml里面的修改,aop:config可以全部注掉,加一个<aop:aspectj-autoproxy/>就行了,这一行标记是打开annotation功能的。记得加两个包,是在spring/lib/aspectj下的两个包。
Spring AOP的主要内容应该就这么多了,动态代理是Spring AOP的实现,还有其他的实现,最完善的应该是Aspectj吧。没有看过。
其实也不难吧。和oo思想一样,重要的还是思想。oo是抽类,aop则是抽切面,应该是对oo的一个补充。

[[it] 本帖最后由 huwangvs 于 2008-10-8 10:38 编辑 [/it]]
2008-10-08 08:59
Ethip
Rank: 5Rank: 5
等 级:贵宾
威 望:15
帖 子:771
专家分:0
注 册:2008-1-18
得分:0 
支持啊 哈哈
2008-10-09 09:33



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-236800-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.262322 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved