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");这里打个断点,看看得到的是什么。