java AOP原理以及实例用法总结
aop :
面向切面编程
在程序设计中,我们需要满足高耦合低内聚,所以编程需满足六大原则,一个法则.
aop面向切面编程正是为了满足这些原则的一种编程思想.
一.装饰者模式:
当我们需要给对象增加功能时,为了满足单一职责原则,可利用装饰者模式编程,创建一个类用来装饰原来的类,这个类写需要在原来的功能上增加的功能.
比如:一个类里面有一个增加图书的功能,
@service public class booksericeimpl implements bookserice { @override public void addone(bokbean bokbean) { system.out.println("执行逻辑:插入一本书"); } @override public void deletone(long bookid) { system.out.println("执行逻辑:删除一本书"); } }
我们需要在这个基础上新增打印日志的功能,
public class booklogserviceimpl implements bookserice { private bookserice bookserice; public booklogserviceimpl(bookserice bookserice) { this.bookserice = bookserice; } @override public void addone(bokbean bokbean) { system.out.println("准备新增一本书"); this.bookserice.addone(bokbean); system.out.println("新增一本书完成"); } @override public void deletone(long bookid) { system.out.println("准备删除一本书"); this.bookserice.deletone(323l); system.out.println("删除一本书完成"); } }
下面我们调用这个增强过后的的对象
public void test1(){ //aop :面向切面编程 //使用装饰者模式设计对象 bookserice bookserice = new booksericeimpl(); //把原来功能的对象通过构造方传给新增功能的类,并把新增功能类的对象赋给原来对象 //这里新增功能类和原来的类都是实现了同一个接口. bookserice = new booklogserviceimpl(bookserice); //调用新增功能类的方法,在这个方法里让构造方法传过去的对象调用原来的功能 bookserice.addone(new bokbean()); }
这样我们就在不改变原来代码的基础上新增了功能,并且也满足单一职责的原则,降低了代码的耦合性.
但是如果接口里面有很多方法,如果每个方法都需要增加日志功能,这样就会出现很多重复代码,并且装饰者模式不能同时为多个没有关系的类同时增强
所以java引入动态代理技术来增加功能.
二.动态代理
在java里动态代理有两个实现方式:
①针对有接口的类的代理,使用jdk中反射包下的动态代理
②针对没有接口的类的代理,使用第三方的jar包enhancer
如果一个类既没有接口,又是final,那么不能进行增强
1.第一种实现:
基于接口的动态代理,使用java内部反射包增强
这种方式创建对象是目标对象的兄弟对象.
同样上面是实现了接口的两个功能的类:
@service public class booksericeimpl implements bookserice { @override public void addone(bokbean bokbean) { system.out.println("执行逻辑:插入一本书"); } @override public void deletone(long bookid) { system.out.println("执行逻辑:删除一本书"); } }
调用通过对象调用上面两个方法:
public void test2(){ //创建需要代理的对象 bookserice bookserice = new booksericeimpl(); //根据对象的类获取类加载器 classloader classloader = bookserice.getclass().getclassloader(); //获取被代理对象说实现的所有接口 class<?>[] interfaces = bookserice.getclass().getinterfaces(); //新建代理对象,里面参数需要(类加载器,一个对象所实现的接口,invocationhandler接口类的对象) bookserice = (bookserice) proxy.newproxyinstance(classloader, interfaces, new loghandler(bookserice)); bookserice.addone(new bokbean()); bookserice.deletone(232l); }
在创建代理对象的时候需要一个invocationhandler接口类的对象,下面创建一个该类的实现类
public class loghandler implements invocationhandler { //通过构造方法接受一个没有被代理的原来的对象 //通过下面的方法名的反射找到这个对象对应方法 private object target; public loghandler(object target) { this.target = target; } //当代理对象调用原方法的时候,就会调用这个invoke方法 @override public object invoke(object proxy, method method, object[] args) throws throwable { string classname = target.getclass().getname(); string methodname = method.getname(); system.out.println(classname+"."+methodname+"方法开始执行"); //这里实际是method类通过方法名反射调用了原方法(addone) object value = method.invoke(target, args); system.out.println(classname+"."+methodname+"方法执行完毕"); return value; } }
这样实现了动态代理之后,不管实现的接口里有多少个方法,你只需调用该方法,就会增强该方法,而不需要针对每个方法写一遍增强功能,
并且这个增强类loghandler类和原来的实现类booksericeimpl类并没有耦合性,这就是说不管你是什么接口类的实现类,只需要对该类的对象进行代理即可,就能对该类的方法添加上这个新增的功能
总的来说,这种动态代理实现方式就是利用反射技术,找到调用的方法名,针对这个方法进行增强.
如果当不需要对某方法增加功能时,就不用不带.
2.第二种实现:
基于类的动态代理,使用cglib框架.
这种方式创建的代理对象是目标对象的子类对象
第二种方式是利用第三方jar包来实现,下载cglib包:
利用jar包中的enhancer类创建增强对象.
创建增强对象需要根据原对象的类名创建类增强器,还需要根据原对象的类型创建子类代理对象
属性通过增强对象set方法赋值,上一种方式是通过调用方法proxy.newproxyinstance传参.
public void test3(){ //创建需要代理增强的对象 bookserice bookserice = new booksericeimpl(); enhancer enhancer = new enhancer(); //用增强器对象创建类增强器 enhancer.setclassloader(bookserice.getclass().getclassloader()); //因为创建的代理对象是目标对象的子类,所以这里填的就是目标对象的类 enhancer.setsuperclass(bookserice.getclass()); //创建代理对象,这里需要的参数是callback接口的对象,所以需要创建一个接口的实现类. enhancer.setcallback(new timemethodinterceptor(bookserice)); //把代理对象赋给原对象 bookserice = (bookserice) enhancer.create(); bookserice.addone(new bokbean()); bookserice.deletone(1l); }
创建callback接口的实现类,也就是功能增强部分,
这一部分跟第一种方式的实现是一样的,都是通过反射在添加功能过程中调用原方法.
//callback接口没有实现方法,所以这里实现的是他的子接口 public class timemethodinterceptor implements methodinterceptor { private object target; public timemethodinterceptor(object target) { this.target = target; } @override public object intercept(object o, method method, object[] objects, methodproxy methodproxy) throws throwable { //记录当前系统时间 //这个时间是从1970年1月1日 0点0分到现在的毫秒数 long start = system.currenttimemillis(); object value = method.invoke(target, objects); long time = system.currenttimemillis() - start; system.out.println("当前时长"+time+"毫秒"); return null; } }
总结:
两种方法的区别:
第一种是用jdk内部方法创建代理对象,由于创建过程中需要一个对象的接口,所以只能针对有接口类的对象进行代理.
第二种是利用第三方jar包中的增强器(enhancer)创建代理对象,通过set方法给需要的属性赋值.由于没有接口实现,所以创建的是对象的子类代理对象.