UDN-企业互联网技术人气社区

板块导航

浏览  : 939
回复  : 0

[讨论交流] 使用Java原生代理实现注入

[复制链接]
呵呵燕的头像 楼主
发表于 2016-12-2 13:11:13 | 显示全部楼层 |阅读模式

  上一篇,咱们说了.使用Java原生代理实现AOP的简单例子,然么就不得不说一下Annotation这个东西了.注解(Annotation)是什么.吓得小柒君赶紧百度下:

  注解基础知识点

  定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

  作用分类:

  ①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】

  ② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】

  ③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

  and so on….如果你对注解(Annotation)还没有了解请左移百度百科:AnnotationJava 注解

  啊,,是不是很眼熟,这个不就是经常看到的那个@Override神马的么…原来这个东西就叫做注解啊.

  —–引至百度:“我个人使用的理解,annotation是一种在类、类型、属性、参数、局部变量、方法、构造方法、包、annotation本身等上面的一个附属品(ElementType这个枚举中有阐述),他依赖于这些元素而存在,他本身并没有任何作用,annotation的作用是根据其附属在这些对象上,根据外部程序解析引起了他的作用。”例如编译时的,其实编译阶段就在运行:java Compiler,他就会检查这些元素,例如:@SuppressWarnings、@Override、@Deprecated等等;

  的确是,但是想想Spring的IOC依赖注入,控制反转@xxx(xxx=xxx)然后Bean里就被赋值了,是不是觉得好帅,哈,是不是也觉得好神奇?

  是不是我创建一个注解,然后把他作用到类上就能赋值了?赶紧试一试…

  首先,创建一个注解,用关键字@interface来声明,这是一个注解类.@Target来声明注解目标,

  @Retention用来说明该注解类的生命周期.

  1.   package proxy.annon;

  2.   import java.lang.annotation.ElementType;

  3.   import java.lang.annotation.Retention;

  4.   import java.lang.annotation.RetentionPolicy;

  5.   import java.lang.annotation.Target;

  6.   @Retention(RetentionPolicy.RUNTIME)

  7.   @Target({ElementType.FIELD,ElementType.METHOD})

  8.   public @interface Seven {

  9.   public String value() default "小黑";

  10.   public String Property() default "无属性";

  11.   }
复制代码


  然后还用咱们昨天的写的JavaBean,加上注解后的样子就是:

  1.   package proxy;

  2.   import proxy.annon.Seven;

  3.   import proxy.imp.AnimalInterface;

  4.   public class DogImp implements AnimalInterface {

  5.   @Seven(value = "Lumia")

  6.   private String name;

  7.   private String Property;

  8.   public DogImp() {

  9.   }

  10.   @Override

  11.   public void setName(String name) {

  12.   this.name = name;

  13.   }

  14.   @Override

  15.   public String getName() {

  16.   return this.name;

  17.   }

  18.   @Override

  19.   public void say() {

  20.   System.out.println("小狗:汪汪汪汪.....");

  21.   }

  22.   @Override

  23.   @Seven(Property = "水陆两栖战士")

  24.   public void setProperty(String Property) {

  25.   this.Property = Property;

  26.   }

  27.   @Override

  28.   public void getProperty() {

  29.   System.out.println(this.name + this.Property);

  30.   }

  31.   }
复制代码


  迫不及待运行下:

  1.   public class Test {

  2.   public static void main(String[] args) {

  3.   DogImp dogImp = new DogImp();

  4.   System.out.println(dogImp.getName());

  5.   dogImp.getProperty();

  6.   }

  7.   }
复制代码


  输出:

  1.   null

  2.   nullnull
复制代码


  额.好伤心,表示并没有什么效果,那@的注入到底是怎么实现的呢….

  转了一圈,还得依赖咱们的反射大军哈哈…..

  赶紧改造下昨天的Demo,让其能够注解..咱们一起来…

  注解已经创建好了,怎么让咱们的注解产生效果呢,赶紧动手写个AnnoInjection类.

  用来实现注解的内容的注入..

  但是要注意,让注如属性的时候,一定要有对用的get/set方法,如果访问级别为private则可以直接使用

  属性的set(obj, value),如果为public,则需要自己获取方法,然后调用方法的invoke

  1.   package proxy.annon;

  2.   import java.lang.reflect.Field;

  3.   import java.lang.reflect.Method;

  4.   public class AnnoInjection {

  5.   public static Object getBean(Object obj) {

  6.   try {

  7.   // 获得类属性

  8.   Field f[] = obj.getClass().getDeclaredFields();

  9.   // 遍历属性

  10.   for (Field ff : f) {

  11.   // 获得属性上的注解

  12.   Seven s = ff.getAnnotation(Seven.class);

  13.   if (s != null) {

  14.   System.err.println("注入" + ff.getName() + "属性" + "tt" + s.value());

  15.   // 反射调用public set方法,如果为访问级别private,那么可以直接使用属性的set(obj,

  16.   // value);

  17.   obj.getClass()

  18.   .getMethod("set" + ff.getName().substring(0, 1).toUpperCase() + ff.getName().substring(1),

  19.   new Class[] { String.class })

  20.   .invoke(obj, s.value());

  21.   }

  22.   }

  23.   // 获得所有方法

  24.   Method m[] = obj.getClass().getDeclaredMethods();

  25.   for (Method mm : m) {

  26.   // 获得方法注解

  27.   Seven s = mm.getAnnotation(Seven.class);

  28.   if (s != null) {

  29.   System.err.println("注入" + mm.getName() + "方法" + "t" + s.Property());

  30.   mm.invoke(obj, s.Property());

  31.   }

  32.   }

  33.   } catch (Exception e) {

  34.   e.printStackTrace();

  35.   }

  36.   return obj;

  37.   }

  38.   }
复制代码


  这样呢,就实现了属性的方法的注入..在哪里调用呢…….

  哦,,查看之前的AnimalFactory代码,会发现在getAnimalBase里有

  1.   Proxy.newProxyInstance(obj.getClass().getClassLoader(),

  2.   obj.getClass().getInterfaces(),

  3.   new AOPHandle(obj,method))
复制代码


  很明显,咱们的动态代理new AOPHandle(obj,method)时传入了反射生成obj.然后实现代理拦截直接,

  咱们来先处理这个obj.所以咱们简单修改下这个就好了

  修改后的样子:

  1.   /***

  2.   * 获取对象方法

  3.   * @param obj

  4.   * @return

  5.   */

  6.   private static Object getAnimalBase(Object obj,AOPMethod method){

  7.   //获取代理对象

  8.   return Proxy.newProxyInstance(obj.getClass().getClassLoader(),

  9.   obj.getClass().getInterfaces(),

  10.   new AOPHandle(AnnoInjection.getBean(obj),method));

  11.   }
复制代码


  soga….看起来完美了,,赶紧跑起来试一试..

  不对..之前的方法拦截太罗嗦,咱们只拦截getProperty方法..好吧

  要不然输出太恶心了….

  修改后的AOPTest….

  1.   package proxy;

  2.   import java.lang.reflect.Method;

  3.   import org.junit.runner.RunWith;

  4.   import org.junit.runners.BlockJUnit4ClassRunner;

  5.   import proxy.imp.AOPMethod;

  6.   import proxy.imp.AnimalInterface;

  7.   @RunWith(BlockJUnit4ClassRunner.class)

  8.   public class AOPTest {

  9.   public static void main(String[] args) {

  10.   AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() {

  11.   // 这里写方法执行前的AOP切入方法

  12.   public void before(Object proxy, Method method, Object[] args) {

  13.   if (method.getName().equals("getProperty")) {

  14.   System.err.println("成功拦截" + method.getName() + "方法,启动");

  15.   }

  16.   }

  17.   // 这里系方法执行后的AOP切入方法

  18.   public void after(Object proxy, Method method, Object[] args) {

  19.   if (method.getName().equals("getProperty"))

  20.   System.err.println("成功拦截" + method.getName() + "方法,结束");

  21.   }

  22.   });

  23.   dog.say();

  24.   String name1 = "我的名字是" + dog.getName();

  25.   System.out.println(name1);

  26.   dog.setName("二狗子");

  27.   String name2 = "我的名字是" + dog.getName();

  28.   System.out.println(name2);

  29.   dog.getProperty();

  30.   }

  31.   }
复制代码


  然后给我run起来….

  beauty,成功出来了呢。。。

  输出顺序可能不一致,这是因为输出流缓存

6.png


  输出顺序可能不一致,这是因为输出流缓存

  在每句的输出语句后面flush就好了。

原文作者:Accelerator 来源:开发者头条

相关帖子

您需要登录后才可以回帖