Day27-基础加强(注解Annotation、动态代理)

来源:互联网 发布:中国兵器工业 知乎 编辑:程序博客网 时间:2024/05/18 02:18

注解Annotation

注解概述:
annotation:是一种代码级别的说明。和类、接口平级;
(注释是给人看的,注解是给计算机(虚拟机)看的)

注解作用:

*      执行编译时期的检查,例如Override*      分析代码(主要用途:替代配置文件)

JDK中的三类注解
1)@Override 标识重写父类方法,描述方法的重写
2)@SuppressWarnings(“all”)压制警告
3)@Deprecated 标识方法过时,例如Date.toLocalString();


自定义注解:

eclipse里面可以直接new 一个,和类、接口是平级关系
@interface 注解名 { }
可以看出注解的本质就是一个接口

插一句:
找到annotation编译出来class文件,shift +右键,打开命令行,javap指令,把class扔进去,就可以看到下图信息:可以看出annotation的本质上就是一个接口
这里写图片描述
注解本质上就是一个接口,接口中可以定义变量(常量)和方法(抽象),注解中的方法叫注解属性

注解属性:
1)基本数据类型;
2)String类型
3)枚举类型
4)注解类型
5)Class类型
6)以上类型的一位数组类型

注意:一旦注解有了属性,在使用的时候就一定要带上注解的属性。

注解属性:
格式:
定义注解属性的时候:(不写修饰符的时候默认是public abstract修饰,且只能是这个)

@Target(value = {ElementType.METHOD  })public @interface AnnotationTest01 {      int i() default 1;      String s();}

定义注解属性的时候可以指定默认值:
属性类型 属性名() default 默认值;

@Target(value = {ElementType.METHOD  })public @interface AnnotationTest01 {      int i() default 1;}

使用注解属性的时候
@注解名(属性名=值,属性名2=值2)
eg:@MyAnnotation3(i = 0,s="23")

特殊情况:
1)若属性类型的一维数组的时候,当数组的值只有一个的时候可以有以下下两种写法
两种写法:
@MyAnnotation4(ss = { “a” })
@MyAnnotation4(ss = “a”)

2)如果属性名字为value的时候,且只有一个属性的时候
赋值的时候value可以省略

3)一个类上,注解不能够重复使用,但是可以用不同的注解


元注解:

  定义在注解上的注解

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD })
public @interface AnnotationTest01 {
int i() default 1;
}
这里写图片描述
四个元注解:后两个重要
1)Target:定义该注解可以使用在什么上面,值为:ElementType的枚举值:

* ​ METHOD:方法* ​ TYPE:类 接口* ​ FIELD:字段* ​ CONSTRUCTOR:构造方法声明

如果自定义的注解上没有加target元注解,都默认为该注解可以定义在任何位置上(方法、类、字段、构造函数)

2)Retention:定义该注解保留到哪个代码阶段,值为RetentionPolocy类型
.java(源码阶段)——>.class(字节码阶段)——>运行阶段

*     * ​ SOURCE:只在源码上保留    * ​ CLASS:在源码和字节码上保留    * ​ RUNTIME:在所有的阶段都保留 不会分就直接写RUNTIME即可;默认情况下是SOURCE阶段

例子:(如果忘记了具体的值怎么写,可以把鼠标移动到value上,按下ctrl点击进去查看写法)

@Target(value = {ElementType.METHOD,ElementType.TYPE  })@Retention(value = RetentionPolicy.RUNTIME)public @interface MyAnnotation03 {    int a();    String b();}

注解Api(如果要找以下函数的Api,需要搜索AnnotatedElement)
( java.lang.reflect.AnnotatedElement)
(调用一下api的可以是字节码对象中的method、field、class等等)
这里写图片描述
* - T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。
* - Annotation[] getAnnotations():得到所有的注解,包含从父类继承下来的。
* - Annotation[] getDeclaredAnnotations():得到自己身上的注解。
* - boolean isAnnotationPresent(Class

@MyTestclass Demo {​ public void fun01(){​ }}
new Demo().getClass().isAnnotationPresent(MyTest.class);//判断Demo这个类上是否有MyTest的注解

2)getAnnotation()得到指定类型的注解的引用,没有返回null

method.isAnnotationPresent(MyTest.class);//判断一个方法上是否有MyTest的注解

案例:判断指定类中的每个函数是不是被指定的Annotation修饰了,如果是,就执行

思路分析:
- 定义两个类(TestDemo,MainTest)和一个注解(@MyTest)
- 在MainTest这个类的main方法里面:
​ 获得TestDemo里面的所有方法
​ 遍历这些方法, 判断此方法上是否有@MyTes注解,
​ 如果有,就执行该方法
有个细节,要加上元注解Retention,让注解在所有阶段都存在。
使得当前注解保留到运行(任何)阶段
ManTest:

public class MainTest {      public static void main(String[] args) throws Exception {           //获得要目标类的字节码文件           Class clazz = Class.forName("com.itheima.mainTest.Test01");           //获得目标类中的所有函数           Method[] methods = clazz.getDeclaredMethods();           //遍历目标类中的所有函数           for (Method method : methods) {                 //判定遍历出来的函数是不是被指定的Annotation修饰了                 if(method.isAnnotationPresent(AnnotationTest01.class)){                      //如果该函数有AnnotationTest01注解                      method.invoke(clazz.newInstance());                       //newInstance()使用的是无参数的构造函数,这也是javaBean中为了一定需要无参数的构造函数的原因,因为框架里面都会用到无参数的构造方法。                        AnnotationTest01 annotation = method.getAnnotation(AnnotationTest01.class);                      System.out.println(annotation);                 }           }      }}

Test01

public class Test01 {      @AnnotationTest01      public void test01(){           System.out.println("test01函数执行了");      }      @AnnotationTest01      public void test02(){           System.out.println("test02函数执行了");      }      public void test03(){           System.out.println("test03函数执行了");      }}

Annotation的定义

@Retention(value = RetentionPolicy.RUNTIME)@Target(value = {ElementType.METHOD  })public @interface AnnotationTest01 {      int i() default 1;}

输出结果为:
这里写图片描述


代理!!

ProxyPattern(代理模式),23中常见的面向对象软件的设计模式之一;

代理概念:
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

​ 意义:增强一个类中的某个方法.
代理模式和装饰者模式挺像。

动态代理:
动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。
​ 动态代理与代理模式原理是一样的,只是它没有具体的代理类,直接通过反射生成了一个代理(虚拟)对象。
静态代理和动态代理的区别:
静态代理一定需要有一个具体的类存在的。
动态代理不需要一个具体的类,通过虚拟机来创建一个代理(虚拟)对象出来


JDK中的动态代理:

 Java.lang.reflect.Proxy类可以直接生成一个代理对象

Proxy.newProxyInstance(ClassLoader loader, Class

public class ProxyCarDemo {      @Test      public void test01(){           final Car myCar = new QqCar();           Car proxyCar = (Car)Proxy.newProxyInstance(myCar.getClass().getClassLoader(), myCar.getClass().getInterfaces(),                      new InvocationHandler() {                            @Override                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                                  if(method.getName().equalsIgnoreCase("run")){                                       System.out.println("代理改装后,可以跑120迈");                                       return null;                                 }                                 return method.invoke(myCar, args);                            }                      });           proxyCar.run();           proxyCar.stop();           proxyCar.addOil(93);      }      /**       * @Title: ProxyCarDemo.java       * @Description: TODO(定义一个接口)       * @author jjizh       * @date 2017年7月10日 上午10:02:19       * @version V1.0       */      interface Car {           public void run();           public void stop();           public void addOil(int num);      }      /**       * @Title: ProxyCarDemo.java       * @Description: TODO(定义一个接口的实现类qqCar)       * @author jjizh       * @date 2017年7月10日 上午10:02:58       * @version V1.0       */      class QqCar implements Car{           @Override           public void run() {                 System.out.println("QqCar可以跑60迈");           }           @Override           public void stop() {                 System.out.println("car都具有刹车功能");           }           @Override           public void addOil(int num) {                 System.out.println("QqCar需要加【"+num+"】号油");           }      }}

案例二:统一GET和POST中文乱码的处理(动态代理)
利用filter和动态代理

需求分析:
​ 在整个网站中,可能会有get请求或post请求向服务器提交参数.参数中往往有中文信息.在后台每个Servlet中都需要去处理乱码.
​ 我们想做的是:无论get请求或者是post请求提交到Servlet中.就可以直接调用getParameter方法将乱码处理好.(使用动态代理)

Filter的主要代码

public class CodeFilter implements Filter {      public void destroy() {           // TODO Auto-generated method stub      }      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {           //强制类型转换           final HttpServletRequest request = (HttpServletRequest) req;           HttpServletResponse response = (HttpServletResponse) res;           HttpServletRequest myRequest = (HttpServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(),                      new InvocationHandler() {                            @Override                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                                  if(method.getName().equalsIgnoreCase("getParameter")){                                       //判断是get提交方式还是post提交方式                                       if(request.getMethod().equalsIgnoreCase("get")){                                            String value = (String) method.invoke(request, args);                                            value = new String(value.getBytes("iso8859-1"),"utf-8");                                            return value;                                       }else if(request.getMethod().equalsIgnoreCase("post")){                                             request.setCharacterEncoding("utf-8");                                            return method.invoke(request, args);                                       }                                 }                                 return method.invoke(request, args);                            }                      });           // pass the request along the filter chain           chain.doFilter(myRequest, response);      }      public void init(FilterConfig fConfig) throws ServletException {      }}

知识点补充: 类加载器

 类加载器就是将.class文件加载到内存生成Class对象 反射的原理图:

1.2JVM中的三类加载器
- BootStrap:引导类加载器 ,负责加载JRE/lib/rt.jar ( 加载JDK中绝大部分的类)runtime
- ExtClassLoader:扩展类加载器,负责加载JRE/lib/ext/*.jar
- AppClassLoader:应用类加载器,负责加载在classpath环境变量中的所有类,我们自己编写的

1.3父类委托机制
保证了字节码只被加载一次

原创粉丝点击