首页 > 编程 > Java > 正文

使用Java动态代理技术实现AOP

2019-11-06 07:07:29
字体:
来源:转载
供稿:网友

实现动态代理

根据网络上很多资料,实现一个业务接口的动态代理只需要三步: - 定义业务接口 - 定义实现业务接口的业务类 - 根据PRoxy类创建任何接口的代理类

第一:定义业务接口

AnimalInterface.java

package proxy.imp;/** * 动态代理的业务接口定义 * * @ClassName: AnimalInterface * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午3:52:15 * */public interface AnimalInterface { // 设置名字 void setName(String name); // 获取名字 String getName(); // 叫声 void say(); // 获取栖性 void getProperty(); // 设置栖性 void setProperty(String Property);}

第二:定义一个实现业务接口的具体类,也叫委托类,等下被代理的类

DogImp.java

package proxy;import proxy.annon.Seven;import proxy.imp.AnimalInterface;/** * 实现接口的具体业务类 * * @ClassName: DogImp * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午3:53:12 * */public class DogImp implements AnimalInterface { @Seven(value = "Lumia") private String name; private String Property; public DogImp() { } @Override public void setName(String name) { this.name = name; } @Override public String getName() { return this.name; } @Override public void say() { System.out.println("小狗:汪汪汪汪....."); } @Override @Seven(Property = "水陆两栖战士") public void setProperty(String Property) { this.Property = Property; } @Override public void getProperty() { System.out.println(this.name + "= " + this.Property); }}

可以看到我这里使用了注解给具体业务类属性赋值的技术,所以就引入了注解的定义和解析;详情可看下面

第三步:先要实现一个调用处理器,然后Proxy类动态生成代理类。

实现调用处理器

AOPHandle.java

package proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import proxy.imp.AOPMethod;/* * 调用处理器,每一个代理类都必须有一个关联的调用处理器,当代理类上的一个方法被调用都会被分发到这个调用处理器上 * 的invoke方法上面; InvocationHandler接口被实现可以作为代理类的调用处理器功能 * @ClassName: AOPHandle * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午3:55:29 * */public class AOPHandle implements InvocationHandler { // 保存AOP切入的接口引用 private AOPMethod method; /** * 被代理的对象 */ private Object o; /** * * 创建一个新的实例 AOPHandle. * * @param o * 委托类实例引用 * @param method */ public AOPHandle(Object o, AOPMethod method) { this.o = o; this.method = method; } /** * 这个方法会自动调用,Java动态代理机制 会传入下面是个参数 * * @param Object * proxy 代理对象的接口,不同于对象 * @param Method * method 被调用方法业务接口 * @param Object[] * args 方法参数 不能使用invoke时使用proxy作为反射参数时,因为代理对象的接口,不同于对象 * 这种代理机制是面向接口,而不是面向类的 **/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object ret = null; if (this.method != null) { // 修改的地方在这里哦 this.method.before(proxy, method, args); // 反射调用方法 ret = method.invoke(o, args); // 修改的地方在这里哦 this.method.after(proxy, method, args); } else {//无AOP的路径 System.out.println("invocation handler before"); ret = method.invoke(o, args); System.out.println("invocation hander after"); } return ret; }}

代理类动态生成:这个是生成动态代理类的核心

AnimalFactory.java

package proxy;import java.lang.reflect.Proxy;import proxy.annon.AnnoInjection;import proxy.imp.AOPMethod;/** * 根据 传进来的委托实例引用创建并返回代理类引用 * * @ClassName: AnimalFactory * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午4:13:37 * */public class AnimalFactory { /*** * 获取对象方法 * * @param obj * @return */ private static Object getAnimalBase(Object obj, AOPMethod method) { // 获取代理对象 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new AOPHandle(AnnoInjection.getBean(obj), method)); } /*** * 获取对象方法 * * @param obj * @return */ @SuppressWarnings("unchecked") public static <T> T getAnimal(Object obj, AOPMethod aopMethod) { return (T) getAnimalBase(obj, aopMethod); } /*** * 获取对象方法 * * @param className * @return */ @SuppressWarnings("unchecked") public static <T> T getAnimal(String className, AOPMethod method) { Object obj = null; try { obj = getAnimalBase(Class.forName(className).newInstance(), method); } catch (Exception e) { e.printStackTrace(); } return (T) obj; } /*** * 获取对象方法 * * @param clz * @return */ @SuppressWarnings("unchecked") public static <T> T getAnimal(Class<?> clz, AOPMethod method) { Object obj = null; try { obj = getAnimalBase(clz.newInstance(), method); } catch (Exception e) { e.printStackTrace(); } return (T) obj; }}

然后就可以进行测试了 先进行注解的测试,看我在代码中注解的值到底有没有赋值到类实例中;

注解测试

package proxy;import proxy.annon.AnnoInjection;public class TestInjection { /*** * 创建一个实例然后,通过注入逻辑自动将注解的内容赋值给实例属性 */ public static void main(String[] args) throws InterruptedException { DogImp dogImp = (DogImp) AnnoInjection.getBean(new DogImp());//等下还会介绍其中的处理逻辑 Thread.sleep(100); System.out.println(dogImp.getName()); dogImp.getProperty(); }}

输出结果:可以看到输出结果我没有对实例属性进行任何的赋值操作,但是最后两句输出已经可以看出属性已经有值了。前两句是注入逻辑中输出的log,可以看到进行了一次属性注入,一次方法注入。

注入 name 属性 Lumia注入 setProperty 方法注解 水陆两栖战士LumiaLumia= 水陆两栖战士

代理类测试

上面我们是建立了一个简单的基于接口的动态代理技术框架,动态代理技术主要有委托接口,以及委托类,调用处理器,代理类动态生成这些技术组成;下面给出代理类的测试案例。

package proxy;import java.lang.reflect.Method;import proxy.imp.AOPMethod;import proxy.imp.AnimalInterface;public class AOPTest { public static void main(String[] args) { /** * 返回的dog为代理实例 */ AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, null);//通过这个函数调用返回DogImp类的代理类; //注意下这里为什么第二个参数为null,这个就是AOP切入的位置 dog.say();// 实体的一个行为 String name1 = "我的名字是= " + dog.getName();// 通过这个可以看到可以将注解注入到属性中 System.out.println(name1); dog.setName("二狗子"); String name2 = "我的名字是" + dog.getName(); System.out.println(name2); dog.getProperty(); }}

输出结果:看到注解注入成功,然后在注解处理器中走得是无AOP的路径。

注入 name 属性 Lumia注入 setProperty 方法注解 水陆两栖战士invocation handler before小狗:汪汪汪汪.....invocation hander afterinvocation handler beforeinvocation hander after我的名字是= Lumiainvocation handler beforeinvocation hander afterinvocation handler beforeinvocation hander after我的名字是二狗子invocation handler before二狗子= 水陆两栖战士invocation hander after

自定义注解并用于给属性赋值

定义注解

Seven.java文件

package proxy.annon;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 自定义的注解 * * @ClassName: Seven * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午4:20:44 * */@Retention(RetentionPolicy.RUNTIME) // 表示这个注解可以生存到运行期@Target({ ElementType.FIELD, ElementType.METHOD }) // 指定注解的使用范围public @interface Seven { // 设定注解的方法,注解方法没有方法体,可以设置默认值 public String value() default "小黑"; public String Property() default "无属性";}

注解注入

注解自动注入是在程序定义一个空实例后,根据注解进行自动注入。根据刚才自定义的注解可以这个注解只能用于字段,函数中,所有自动注入逻辑主要是遍历一个变量的所有字段和函数,然后查看是否具有指定的Seven注解,然后读取我们在源码中设置的值并通过反射机制属性对应的setXXX方法完成初始化。

package proxy.annon;import java.lang.reflect.Field;import java.lang.reflect.Method;/* * 注解注入,将一个业务类实例的注解自动赋值给类属性 * @ClassName: AnnoInjection * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午4:22:52 * */public class AnnoInjection { public static Object getBean(Object obj) { try { // 获得类属性 Field f[] = obj.getClass().getDeclaredFields(); // 遍历属性,查找所有的属性注解 for (Field ff : f) { // 获得属性上的注解 Seven s = ff.getAnnotation(Seven.class);// 返回ff属性的Seven类型的注解 if (s != null) { System.err.println("注入 " + ff.getName() + " 属性" + "/t/t" + s.value()); // 反射调用public set方法,如果访问级别为private,那么可以直接使用属性的set(obj, // value); obj.getClass() .getMethod("set" + ff.getName().substring(0, 1).toUpperCase() + ff.getName().substring(1), // 组配函数名称出来 new Class<?>[] { String.class }) .invoke(obj, s.value());// 通过反射调用属性对应的setXXX函数将注解的值赋值给类属性 } } // 获得所有方法,查找方法注解 Method m[] = obj.getClass().getDeclaredMethods(); for (Method mm : m) { // 获得方法注解 Seven s = mm.getAnnotation(Seven.class); if (s != null) { System.err.println("注入 " + mm.getName() + " 方法注解" + "/t" + s.Property()); mm.invoke(obj, s.Property());// 通过方法注入注解的值 } } } catch (Exception e) { e.printStackTrace(); } return obj; }}

定义一个AOP接口用于AOP切面编程

上面只是使用了动态代理技术和注解技术,下面通过定义一个AOP接口,将AOP接口的能力添加到代理类中

AOP接口

package proxy.imp;import java.lang.reflect.Method;/** * 这是一个AOP的一个切面;在每个接口方法中可以做一些类似于日志处理的功能 * * @ClassName: AOPMethod * @Description: TODO(这里用一句话描述这个类的作用) * @author PengRong * @date 2017年3月5日 下午4:18:20 * */public interface AOPMethod { // 实例方法执行前执行的方法,比如执行方法前,记录类状态,写入log.监控xx变量,,, void after(Object proxy, Method method, Object[] args); // 实例方法执行后执行的方法 void before(Object proxy, Method method, Object[] args);}

通过在生产代理类的代码中将实现AOP接口的实例传递进去。

package proxy;import java.lang.reflect.Method;import proxy.imp.AOPMethod;import proxy.imp.AnimalInterface;public class AOPTest { public static void main(String[] args) { /** * new AOPMethod() { // 这里写方法执行前的AOP切入方法 * * @Override public void before(Object proxy, Method method, Object[] * args) { if (method.getName().equals("getProperty")) { * System.err.println("成功拦截" + method.getName() + "方法,启动"); } * } * * // 这里系方法执行后的AOP切入方法 * @Override public void after(Object proxy, Method method, Object[] * args) { if (method.getName().equals("getProperty")) * System.err.println("成功拦截" + method.getName() + "方法,结束"); * * } } */ /** * 返回的dog为代理实例 */ AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() { // 这里写方法执行前的AOP切入方法 @Override public void before(Object proxy, Method method, Object[] args) { if (method.getName().equals("getProperty")) { System.err.println("成功拦截" + method.getName() + "方法,启动"); } } // 这里系方法执行后的AOP切入方法 @Override public void after(Object proxy, Method method, Object[] args) { if (method.getName().equals("getProperty")) System.err.println("成功拦截" + method.getName() + "方法,结束"); } });// 返回一个代理类 dog.say();// 实体的一个行为 String name1 = "我的名字是= " + dog.getName();// 通过这个可以看到可以将注解注入到属性中 System.out.println(name1); dog.setName("二狗子"); String name2 = "我的名字是" + dog.getName(); System.out.println(name2); dog.getProperty(); }}

执行结果:通过实现AOP接口实例传递进去,那么将可以在实际业务方法执行前进行很多其他操作,比如统计,监控,日志功能。

注入 name 属性 Lumia注入 setProperty 方法注解 水陆两栖战士成功拦截getProperty方法,启动成功拦截getProperty方法,结束小狗:汪汪汪汪.....我的名字是= Lumia我的名字是二狗子二狗子= 水陆两栖战士

综上:动态代理类技术是实现AOP的技术基础。 源代码 使用Java原生代理并实现注解自动注入

使用Java原生代理实现AOP


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表