Java 注解&反射&动态代理是框架封装的核心基础,必须要掌握.
注解
注解:是Java5引入的一种代码辅助工作,它的核心作用是对类、方法、变量、参数和包进行标注,通过反射来访问这些标注信息,以此运行时改变所注解对象的行为.Java中注解由内置注解和元注解组成.
注解与注释:
Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据.
普通的注释在编译后的class文件中不存在的
而注解附加的信息则根据需要可以保存到class文件中,甚至运行期加载的class对象中.
元注解介绍
- 创建注解: public @interface AnnotationName{}
- 元注解(描述注解的一种方式)
- @Retention 定义注解的生命周期:[source -> class -> runntime]
- @Documented 文档注解,会被Javadoc工具文档化
- @Inherited 是否让之类继承该注解
- @Target 描述了注解的应用范围
@Target 描述的应用范围
public enum ElementType {/** 表示可以用来修饰类、接口、注解类型或枚举类型 */TYPE,/** 可以用来修饰属性(包括枚举常量) */FIELD,/** 可以用来修饰方法 */METHOD,/** 可以用来修饰参数 */PARAMETER,/** 可以用来修饰构造器 */CONSTRUCTOR,/** 可以用来修饰局部变量 */LOCAL_VARIABLE,/** 可以用来修饰注解类型 */ANNOTATION_TYPE,/** 可以用来修饰包 */PACKAGE,/*** Type parameter declaration** @since 1.8*/TYPE_PARAMETER,/*** Use of a type** @since 1.8*/TYPE_USE}
创建一个注解: 一般使用注解的生命周期@Retention以及应用范围@Target
/*** 自定义注解 - 设置元注解*/@Retention(RetentionPolicy.RUNTIME)//元注解 定义注解的生命周期@Target({ElementType.FIELD, ElementType.TYPE,ElementType.METHOD}) //元注解 通过 { } 设置多个注解的应用范围public @interface Study {String name() default "kane";//Java基本类型String[] moves(); //如果没有定义default 默认属性 如果用到这个注解必须要定义这个属性值}@Study(moves = {"hard", "jake"},name = "jake")public class Person {private String name;@Study(moves = {"18"})private int age;@Study(moves = {"hard", "jake"},name = "method")public String getString() {return "str";}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}
注解的使用,只是做到了一个标记的作用,其他的并没有任何操作.
注解的创建方式:
- 配置元注解,由元注解来当前注解的作用范围和生命周期.
- 注解中如果需要添加信息,可以用以上方式添加.
- 注解信息支持Java的基本数据结构
反射
反射 Reflection:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射
反射的优缺点
- 通过反射可以使程序代码访问装载到JVM中的类的内部信息,获取已装载类的属性信息,获取已装载类的方法,获取已装载类的构造方法信息.
- 反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力.
- 反射会对性能造成一定的影响,同时让代码的可读性变低.
反射常用的API
| 方法名 | 返回值 | 参数描述 |
|---|---|---|
| Class.forName(String) | 获取类的元信息 | 当前类文件的具体位置 |
| 类.getClass() | 获取类的元信息 | - |
| clz.getDeclaredFields() | 获取当前类的所有属性 | - |
| setAccessible(true) | 设置当前类属性为可见 | true 或者 false |
| getMethods() | 获取类所有方法 | - |
| invoke(obj) | 通过反射执行方法 | 类的元信息 |
| getAnnotation(class) | 获取注解 | 需要获取的注解的Class |
获取类的元信息
//1 通过反射获取到class类的元信息Person person = new Person();Class<? extends Person> aClass = person.getClass();//获取到class对象 类的元信息//其他方式反射Class<?> aClass1 = Class.forName("com.jakeprim.model.Person");//spring//<bean name="", class="com.jakeprim.model.Person" />//2 通过反射获取类名 包名String name = aClass1.getName();//全类名String simpleName = aClass1.getSimpleName();//类名System.out.println("simpleName:" + simpleName);System.out.println("name:" + name);System.out.println("aClass1:" + aClass1);System.out.println("aClass:" + aClass);
获取类的属性
Field[] declaredFields = aClass.getDeclaredFields();//获取类的所有属性for (Field field : declaredFields) {System.out.println(field);}//获取指定的属性Field ageField = aClass.getDeclaredField("age");System.out.println("ageField:" + ageField);//4 获取到属性的具体值person.setName("jakeprim");person.setAge(18);for (Field declaredField : declaredFields) {declaredField.setAccessible(true);//设置属性为可见System.out.println(declaredField.get(person));}
反射中实例化
Object p = aClass.newInstance();//相当于在反射中实例化for (Field field : declaredFields) {field.setAccessible(true);if (field.getName().equals("name")) {field.set(p, "kane");} else {field.set(p, 18);}System.out.println(field.get(p));}
反射获取方法 并执行方法
Method[] methods = aClass.getMethods();//获取所有方法for (Method method : methods) {// System.out.println(method.getName());}//获取指定的方法Method method = aClass.getMethod("getString");//方法名 参数类型Object invoke = method.invoke(p);//传递:类对象 参数值 并执行方法System.out.println(invoke);
反射获取类的注解
Study study = aClass.getAnnotation(Study.class);//获取注解的内容String[] moves = study.moves();String name1 = study.name();System.out.println("moves:" + moves[0] + " name:" + name1);
反射获取方法的注解
Study methodAnnotation = method.getAnnotation(Study.class);String[] methodMoves = methodAnnotation.moves();String methodName1 = methodAnnotation.name();System.out.println("get method annotation moves:" + methodMoves[0] + " methodName1:" + methodName1);
反射获取属性的注解
for (Field field : declaredFields) {Study fieldAnnotation = field.getAnnotation(Study.class);if (fieldAnnotation == null) {continue;}System.out.println("get fields annotation moves:" + fieldAnnotation.moves()[0] + " name:" + fieldAnnotation.name());}Study ageAnnotation = ageField.getAnnotation(Study.class);System.out.println("ageAnnotation:"+ageAnnotation.moves()[0]);
注解与反射实战
通过注解加反射,实现一个对SQL语句的封装,代码如下:
SqlState 来标记实体类中的SQL的表名@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface SqlState {String value() default "";}
@Column 来标记实体了属性的SQL中的字段名
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Column {String value() default "";}
Order 实体类(setter和getter方法这里就不在显示了)
@SqlState("t_order")public class Order {@Column //默认和字段名一致private Long id;@Column("order_no") //Java驼峰命名 SQL _ 命名private String orderNo;//一旦有值 作为查询条件@Column("user_name")private String userName;@Column("user_id")private int userId;@Column("shop_id")private int shopId;}
然后通过工具类,来实现:select 字段 表名 from where 条件 自动拼接返回SQL语句 如果属性的值不为空则作为条件
public String query(Object obj) throws Exception {StringBuffer buffer = new StringBuffer();//拼接整体的查询SQL语句StringBuffer whereBuffer = new StringBuffer(" where "); //拼接查询条件SQL语句//select 字段 from 表名 where 条件buffer.append("select");//反射对象上的注解Class<?> objClass = obj.getClass();SqlState sqlState = objClass.getAnnotation(SqlState.class);//表名if (sqlState == null) {throw new RuntimeException("注解缺失");}String tableName = sqlState.value();Field[] fields = objClass.getDeclaredFields();//获取对象属性for (Field field : fields) {Column column = field.getAnnotation(Column.class);if (column == null) {continue;//不生成查询字段}if (column.value().equals("")) {//表字段名和属性名是一致的String name = field.getName();field.setAccessible(true);Object value = field.get(obj);buffer.append(" " + name + ",");if (value != null && Integer.parseInt(value.toString()) != 0) {whereBuffer.append(" and " + name + "=" + value);}} else {String name = column.value();//表字段名和属性名不一致field.setAccessible(true);Object value = field.get(obj);buffer.append(" " + name + ",");if (value != null && Integer.parseInt(value.toString()) != 0) {whereBuffer.append(" and " + name + "=" + value);}}}buffer.deleteCharAt(buffer.length() - 1);//删除最后一个逗号buffer.append(" from " + tableName);buffer.append(whereBuffer);return buffer.toString();}
测试一下:
GenerateSqlUtil generateSqlUtil = new GenerateSqlUtil();Order order = new Order();order.setOrderNo("7789");String query = generateSqlUtil.query(order);System.out.println("query:" + query);
返回结果如下: 注解加反射在很大的程度上帮助我们提高了开发效率 不用再去写繁琐的SQL语句.
query:select id, order_no, user_name, user_id, shop_id from t_order where and order_no=7789
代理模式
代理模式(Proxy)为其他对象提供一种代理,以控制对这个对象的访问.代理对象在客户端和目标对象之间起到中介的作用

静态代理
如下是生产玩具的工厂接口
public interface GameFactory {//生产所有的玩具String make();}
如下是购买图书的工厂接口
public interface BookFactory {String printed();}
如下两个类,是两款游戏,都实现了GameFactory
public class GameBoyFactory implements GameFactory {@Overridepublic String make() {return "gameBoy";}}
public class PS4Factory implements GameFactory {@Overridepublic String make() {return "ps4";}}
有人发现了商机,准备去代理游戏和图书,代理人就要实现GameFactory, BookFactory,由购买者向代理人传递购买哪个游戏或者图书
/*** 代理人* 静态代理*/public class JakeProxy implements GameFactory, BookFactory {GameFactory factory;/*** 告诉代理人 你要买的东西** @param factory*/public JakeProxy(GameFactory factory) {this.factory = factory;}@Overridepublic String make() {//售前服务dosomethingBefore();String make = factory.make();System.out.println("make:" + make);//售后服务dosomethingAfter();return null;}private void dosomethingAfter() {System.out.println("售后服务");}private void dosomethingBefore() {System.out.println("售前服务");}@Overridepublic String printed() {//越来越多的业务 会使这个代理类越来越难以维护return null;}}
有一个土豪想要去购买ps4,找到代理人告诉他我要购买ps4,如下代码调用
//想要去购买ps4PS4Factory factory = new PS4Factory();//找代理人 我要买ps4JakeProxy jakeProxy = new JakeProxy(factory);//买回来了jakeProxy.make();//在比如 土豪现在想买图书了 那么代理人就需要继承bookfactory 以后代理人扩展业务 就需要实现不同的接口
上述的代码就属于静态代理,如果以后代理人要扩展其他业务就需要在实现其他的接口,如果接口中的方法修改或者添加,那么代理人同样也需要修改和添加,非常不利于以后的扩展,最终代理人会被累死……
动态代理
代理人经过了多重思考最终成立一个代理公司:
代理游戏,直接找游戏公司,给我提供一个代理人,在比如书籍直接找厂商给我提供一个代理人,进行代理,而不用在自己负责代理了.即便厂商接口发生变化也不会影响代理公司,因为厂商提供的代理人都会知道.

动态代理代码实现如下:
/*** 动态代理 不关注具体的代理对象 只需要代理即可*/public class JakeProxy2 implements InvocationHandler {private Object target;//目标的代理对象 被代理对象public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Object getProxy() {//我只负责跟总公司联系 而不在负责具体的店铺return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {dosomethingBefore();Object invoke = method.invoke(target, args);//由System.out.println(invoke);dosomethingAfter();return invoke;}//标准化的服务定制流程private void dosomethingAfter() {System.out.println("售后服务");}private void dosomethingBefore() {System.out.println("售前服务");}}
调用如下:由代理厂商直接提供一个代理人
//动态代理 不管是增加接口还是新的代理 不会影响代理人的代码业务JakeProxy2 jakeProxy2 = new JakeProxy2();jakeProxy2.setTarget(new PS4Factory());GameFactory proxyGame = (GameFactory) jakeProxy2.getProxy();//获得一个代理proxyGame.make();
当然上述的代码 非常恶心的 如果要代理100个对象 那么就要写一百次 非常麻烦 那么如何解决这个问题呢?可以参考Spring中的源码设计 后期在进行讲解.
APT 注解处理器
- 创建类 继承: AbstractProcessor
@SupportedAnnotationTypes(“”) 只处理某个指定的注解需要添加注解的全类名
- 配置文件: resources/META-
INF.services/javax.annotation.processing.Processor - gralde 中添加该library
什么时候吊起注解处理程序
.java -> javac -> .class
javac 首先吊起注解处理程序 然后才会将.java 编译成.class文件
采集到所有的注解信息 -> Element -> 注解处理程序

@IntDef 元注解提供语法检查
自定义语法检查注解 IDE 插件实现的语法检查
@IntDef({SUNDAY,MONDAY})@Target({ElementType.FIELD,ElementType.PARAMETER})@Retention(RetentionPolicy.SOURCE)@interface WekDay{}
字节码增强技术
在字节码中写代码
.class 文件 字节码文件 -》格式 数据按照特定的方式记录与排列
public @interface InjectView{}isAnnotationPresent(注解类) //判断是否存在某个注解
查看动态代理生成的class
利用反射、注解、动态代理实现OnClick事件的自动注入
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface EventType {Class listenerType();String listenerSetter();}
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventType(listenerType = View.OnLongClickListener.class, listenerSetter = "setOnLongClickListener")public @interface LongClick {int[] value();}
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener")public @interface Click {int[] value();}
public class ClickUtils {public static void init(final Activity activity) {//首先获取 方法的注解 以及viewClass<? extends Activity> aClass = activity.getClass();Method[] declaredMethods = aClass.getDeclaredMethods();for (final Method m : declaredMethods) {if (m.isAnnotationPresent(Click.class)) {Click annotation = m.getAnnotation(Click.class);int[] value = annotation.value();for (int id : value) {final View view = activity.findViewById(id);View.OnClickListener proxyClickListener = (View.OnClickListener) Proxy.newProxyInstance(view.getClass().getClassLoader(),new Class[]{View.OnClickListener.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("onClick")) {m.invoke(activity, args);}return proxy;}});view.setOnClickListener(proxyClickListener);}}}}private static final String TAG = "ClickUtils";public static void init2(final Activity activity) {Class<? extends Activity> aClass = activity.getClass();Method[] declaredMethods = aClass.getDeclaredMethods();for (final Method declaredMethod : declaredMethods) {Annotation[] annotations = declaredMethod.getAnnotations();for (Annotation annotation : annotations) {Class<? extends Annotation> annotationType = annotation.annotationType();//判断注解上是否有事件类型注解if (annotationType.isAnnotationPresent(EventType.class)) {EventType eventType = annotationType.getAnnotation(EventType.class);String listenerSetter = eventType.listenerSetter();Class listenerType = eventType.listenerType();Log.e(TAG, "init2 -> listenerSetter:" + listenerSetter + " listenerType:" + listenerType);//不需要关注是onClick 还是 onLongClicktry {//获取注解@Click 或 @LongClick 的value() 方法Method value = annotationType.getDeclaredMethod("value");//获取方法的返回值int[] ids = (int[]) value.invoke(annotation);Log.e(TAG, "init2 -> ids:" + ids.toString());//设置允许调用private方法declaredMethod.setAccessible(true);//代理事件Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return declaredMethod.invoke(activity, args);}});// String name = listenerType.getName() + "$Proxy0";// //生成代理指定接口的Class数据// byte[] bytes = Proxy.generateProxyClass(name, new Class[]{Massage.class});// FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");// fos.write(bytes);// fos.close();//遍历view id 绑定事件for (int id : ids) {View view = activity.findViewById(id);//反射调用view的事件方法Method method = view.getClass().getMethod(listenerSetter, listenerType);//执行方法method.invoke(view, proxyInstance);}} catch (Exception e) {e.printStackTrace();}}}}}}
