博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中实现AOP的两种方式 之一: 使用JDK自带的动态代理类Proxy实现
阅读量:6368 次
发布时间:2019-06-23

本文共 10059 字,大约阅读时间需要 33 分钟。

  hot3.png

    说到AOP,一般都会首先想到Spring中的AOP,但是AOP其实是一种编程思想,而Spring只是对AOP进行实现而已。

      在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 在Spring中用到了两种代理方式:

    若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

              优点:因为有接口,所以使系统更加松耦合

              缺点:为每一个目标类创建接口

    若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

              优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。

              缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

本文主要分别通过这两种代理方式实现简单的AOP(LZ能力有限,如果哪里有问题,还请指出)

  • 一 利用JDK自带的代理类

    java从jdk1.3开始引入了动态代理。实现方式是为被代理的业务接口生成代理类,将AOP的逻辑写入到代理类中,在运行的时候动态织入AOP,使用反射执行织入的逻辑。 

开始撸代码

  • 1. 首先新建一个接口(这里以一个service接口为例)

package com.declan.aop.jdk;/** * 业务接口 *  * @author Declan */public interface IService {		void doSomething();	void doElseSomething(String str);}
  • 2. 写两个service的实现

package com.declan.aop.jdk;public class ServiceImpl_1 implements IService{	@Override	public void doSomething() {		System.out.println("ServiceImpl_1 do doSomething");	}	@Override	public void doElseSomething(String str) {		System.out.println("ServiceImpl_1 do doElseSomething");	}}
package com.declan.aop.jdk;public class ServiceImpl_2 implements IService{	@Override	public void doSomething() {		System.out.println("ServiceImpl_2 do doSomething");			}	@Override	public void doElseSomething(String str) {		System.out.println("ServiceImpl_2 do doElseSomething");	}}
  • 3. 新建一个ServiceDynamicProxy类,该类需要实现 java.lang.reflect.InvocationHandler 接口

该类是实现动态代理的关键

package com.declan.aop.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Arrays;/** * 代理类(实现JDK反射包中的InvocationHandler, 实现invoke方法) * @author Declan * */public class ServiceDynamicProxy implements InvocationHandler{	/**	 * 代理对象	 */	private Object proxied;		public ServiceDynamicProxy(Object proxied) {		this.proxied = proxied;	}		/**	 * 这个方法会自动调用, java的动态代理机制	 * @param proxy  代理对象的接口,不同于对象	 * @param method  被调用的方法	 * @param args  被调用方法的参数	 * @return	 * @throws Throwable	 */	@Override	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {		// TODO Auto-generated method stub		System.out.println("******************方法开始执行*********************");		System.out.println("被代理对象:"+proxied.getClass());		System.out.println("代理对象:"+proxy.getClass());		System.out.println("被调用的方法:"+method.getName());		System.out.println("被调用方法的参数:"+Arrays.toString(args));				Object invoke = method.invoke(proxied, args); //调用方法				System.out.println("******************方法执行结束*********************");				return invoke;	}}
  • 4. 写一个生产Service代理对象的工厂类(该类提供了多种方式来构造一个对象)

通过调用 Proxy.newProxyInstance()方法可以创建动态代理,这个方法需要一个类加载器,一个你希望该代理实现的接口(不是类或抽象类),以及InvocationHandler的一个实现类。

package com.declan.aop.jdk;import java.lang.reflect.Proxy;/** * 产生代理对象工厂 * @author Declan */public class ProxyFactory {	private static Object getServiceBase(Object obj) {		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),				new ServiceDynamicProxy(obj));	}			 /***     * 获取对象方法     * @param obj     * @return     */    @SuppressWarnings("unchecked")    public static 
T getService(Object obj){ return (T) getServiceBase(obj); } /*** * 获取对象方法 * @param className * @return */ @SuppressWarnings("unchecked") public static
T getService(String className){ Object obj=null; try { obj= getServiceBase(Class.forName(className).newInstance()); } catch (Exception e) { e.printStackTrace(); } return (T)obj; } /*** * 获取对象方法 * @param clz * @return */ @SuppressWarnings("unchecked") public static
T getService(Class clz){ Object obj=null; try { obj= getServiceBase(clz.newInstance()); } catch (Exception e) { e.printStackTrace(); } return (T)obj; }}
  • 5. 进行测试

package com.declan.aop.jdk;/** * 测试类 * @author Declan * */public class MainTest {		public static void main(String[] args) {		IService service = ProxyFactory.getService(ServiceImpl_1.class);//		IService service = ProxyFactory.getService("com.declan.aop.jdk.ServiceImpl_1");		service.doSomething();		service.doElseSomething("do else something");				IService service_2 = ProxyFactory.getService(ServiceImpl_2.class);		service_2.doSomething();		service_2.doElseSomething("do else something");	}		}

运行结果:

******************方法开始执行*********************被代理对象:class com.declan.aop.jdk.ServiceImpl_1代理对象:class com.sun.proxy.$Proxy0被调用的方法:doSomething被调用方法的参数:nullServiceImpl_1 do doSomething******************方法执行结束***************************************方法开始执行*********************被代理对象:class com.declan.aop.jdk.ServiceImpl_1代理对象:class com.sun.proxy.$Proxy0被调用的方法:doElseSomething被调用方法的参数:[do else something]ServiceImpl_1 do doElseSomething******************方法执行结束***************************************方法开始执行*********************被代理对象:class com.declan.aop.jdk.ServiceImpl_2代理对象:class com.sun.proxy.$Proxy0被调用的方法:doSomething被调用方法的参数:nullServiceImpl_2 do doSomething******************方法执行结束***************************************方法开始执行*********************被代理对象:class com.declan.aop.jdk.ServiceImpl_2代理对象:class com.sun.proxy.$Proxy0被调用的方法:doElseSomething被调用方法的参数:[do else something]ServiceImpl_2 do doElseSomething******************方法执行结束*********************

到这里动态代理就已经实现了,但是具体的AOP还是没有做,其实到了这里大家可能都已经知道怎么加AOP的接口了,下面就接着将没实现的完的全部做完。

  6. AOP接口的定义(这里我们以Service层的事物为例)

package com.declan.aop.jdk;import java.lang.reflect.Method;/** * AOP注入接口 * 这里以事物的打开和提交为例 * @author Declan */public interface IAOPMethod {	/**	 * 方法执行前开启事物	 * @param proxy	 * @param method	 * @param args	 */	void beginTransaction(Object proxy, Method method, Object[] args);		/**	 * 方法执行完提交事物	 * @param proxy	 * @param method	 * @param args	 */	void commitTransaction(Object proxy, Method method, Object[] args);}

7. 新建类 实现上面定义的AOP接口

package com.declan.aop.jdk;import java.lang.reflect.Method;/** * AOP方法的实现 * @author Declan */public class AOPMethodImpl implements IAOPMethod{	@Override	public void beginTransaction(Object proxy, Method method, Object[] args) {		System.out.println("方法:"+method.getName()+" 开启事物!");	}	@Override	public void commitTransaction(Object proxy, Method method, Object[] args) {		System.out.println("方法:"+method.getName()+" 提交事物!");	}}

8. 到这里需要对我们之前写的 ProxyFactory  和  ServiceDynamicProxy 进行修改, 就是将自己实现的AOP接口引入(具体如下)

package com.declan.aop.jdk;import java.lang.reflect.Proxy;/** * 产生代理对象工厂 *  * @author Declan */public class ProxyFactory {	private static Object getServiceBase(Object obj, IAOPMethod iaopMethod) {		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),				new ServiceDynamicProxy(obj, iaopMethod));	}	/***	 * 获取对象方法	 * 	 * @param obj	 * @return	 */	@SuppressWarnings("unchecked")	public static 
T getService(Object obj, IAOPMethod iaopMethod) { return (T) getServiceBase(obj, iaopMethod); } /*** * 获取对象方法 * * @param className * @return */ @SuppressWarnings("unchecked") public static
T getService(String className, IAOPMethod iaopMethod) { Object obj = null; try { obj = getServiceBase(Class.forName(className).newInstance(), iaopMethod); } catch (Exception e) { e.printStackTrace(); } return (T) obj; } /*** * 获取对象方法 * * @param clz * @return */ @SuppressWarnings("unchecked") public static
T getService(Class clz, IAOPMethod iaopMethod) { Object obj = null; try { obj = getServiceBase(clz.newInstance(), iaopMethod); } catch (Exception e) { e.printStackTrace(); } return (T) obj; }}
package com.declan.aop.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Arrays;/** * 代理类(实现JDK反射包中的InvocationHandler, 实现invoke方法) * @author Declan * */public class ServiceDynamicProxy implements InvocationHandler{	/**	 * 代理对象	 */	private Object proxied;		private IAOPMethod iaopMethod;		public ServiceDynamicProxy(Object proxied, IAOPMethod iaopMethod) {		this.proxied = proxied;		this.iaopMethod = iaopMethod;	}		/**	 * 这个方法会自动调用, java的动态代理机制	 * @param proxy  代理对象的接口,不同于对象	 * @param method  被调用的方法	 * @param args  被调用方法的参数	 * @return	 * @throws Throwable	 */	@Override	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {		// TODO Auto-generated method stub		System.out.println("******************方法开始执行*********************");		iaopMethod.beginTransaction(proxy, method, args);		System.out.println("被代理对象:"+proxied.getClass());		System.out.println("代理对象:"+proxy.getClass());		System.out.println("被调用的方法:"+method.getName());		System.out.println("被调用方法的参数:"+Arrays.toString(args));				Object invoke = method.invoke(proxied, args); //调用方法		iaopMethod.commitTransaction(proxy, method, args);		System.out.println("******************方法执行结束*********************");				return invoke;	}}

然后进行测试

package com.declan.aop.jdk;/** * 测试类 * @author Declan * */public class MainTest {		public static void main(String[] args) {				IService service = ProxyFactory.getService(ServiceImpl_1.class, new AOPMethodImpl());//		IService service = ProxyFactory.getService("com.declan.aop.jdk.ServiceImpl_1");		service.doSomething();		service.doElseSomething("do else something");	}		}

测试结果

******************方法开始执行*********************方法:doSomething 开启事物!被代理对象:class com.declan.aop.jdk.ServiceImpl_1代理对象:class com.sun.proxy.$Proxy0被调用的方法:doSomething被调用方法的参数:nullServiceImpl_1 do doSomething方法:doSomething 提交事物!******************方法执行结束***************************************方法开始执行*********************方法:doElseSomething 开启事物!被代理对象:class com.declan.aop.jdk.ServiceImpl_1代理对象:class com.sun.proxy.$Proxy0被调用的方法:doElseSomething被调用方法的参数:[do else something]ServiceImpl_1 do doElseSomething方法:doElseSomething 提交事物!******************方法执行结束*********************

        到这里这个使用JDK自带的动态代理实现AOP就结束了,从上面的执行结果可以看出在每个方法执行前 都会开启事物,在方法执行完后,都会提交事物,如果要添加新的AOP(例如 添加日志等),就可以再自定义新的接口进行切入,而不用修改具体具体service的方法。

        如果有人疑惑怎么实现像Spring对具体的方法进行切入,其实已经可以了。在 ServiceDynamicProxy 的 invoke方法中根据方法名进行过滤接可以了。

转载于:https://my.oschina.net/Declan/blog/1787024

你可能感兴趣的文章
NSQ:分布式的实时消息平台
查看>>
《七周七并发模型》作者Paul Butcher、阿里云研究员余锋(褚霸)——QCon北京2016前瞻...
查看>>
Elixir 1.3带来新的语言功能、API和改进后的工具
查看>>
Ruby 2.5.0概览
查看>>
Atlassian的Stash数据中心为Git提供了高可用性及可伸缩性
查看>>
Netflix发布Polly.JS,一个用于HTTP交互的开源库
查看>>
比拼生态和未来,Spark和Flink哪家强?
查看>>
Chef宣布100%开源,要走红帽模式?\n
查看>>
利用Diferencia和Java微服务进行分接比较测试
查看>>
图片相似度计算:深入理解DCT变换以及感知哈希
查看>>
Micronaut for Spring支持Spring Boot应用以Micronaut形式运行
查看>>
斯坦福开源Python库StanfordNLP,可处理53种人类语言
查看>>
GitHub采用了新的GraphQL API
查看>>
如何使用package.json文件
查看>>
Microsoft发布模块化区块链服务:Project Bletchley
查看>>
C2x将成为C语言的下一个ISO标准
查看>>
与专门团队一起持续交付
查看>>
Google提出Grasp2Vec模型:利用自监督方法学习物体表示
查看>>
使用TensorFlow的递归神经网络(LSTM)进行序列预测
查看>>
企业金融云存储建设之路
查看>>