经过代理对象访问目标对象.这样作的好处是:能够在目标对象实现的基础上,扩展目标对象的功能。
代理对象拦截真实对象的方法调用,在真实对象调用前/后实现本身的逻辑调用
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,若是需改修改,能够经过代理的方式来扩展该方法。java
动态代理的用途与装饰模式很类似,就是为了对某个对象进行加强。全部使用装饰者模式的案例均可以使用动态代理来替换。
git
/** * subject(抽象主题角色): * 真实主题与代理主题的共同接口。 */ interface Subject { void sellBook(); } /** * ReaISubject(真实主题角色): * 定义了代理角色所表明的真实对象。 */ public class RealSubject implements Subject { @Override public void sellBook() { System.out.println("出版社卖书"); } } /** * Proxy(代理主题角色): * 含有对真实主题角色的引用,代理角色一般在将客户端调用传递给真实主题对象以前或者以后执行某些操 做,而不是单纯返回真实的对象。 */ public class ProxySubject implements Subject { private RealSubject realSubject; @Override public void sellBook() { if (realSubject == null) { realSubject = new RealSubject(); } sale(); realSubject.sellBook(); give(); } public void sale() { System.out.println("打折"); } public void give() { System.out.println("送优惠券"); } } public class Main { public static void main(String[] args) { //静态代理(咱们本身静态定义的代理类) ProxySubject proxySubject = new ProxySubject(); proxySubject.sellBook(); //动态代理(经过程序动态生成代理类,该代理类不是咱们本身定义的。而是由程序自动生成) RealSubject realSubject = new RealSubject(); MyHandler myHandler = new MyHandler(); myHandler.setProxySubject(realSubject); Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), myHandler); subject.sellBook(); } } public class MyHandler implements InvocationHandler { private RealSubject realSubject; public void setProxySubject(RealSubject realSubject) { this.realSubject = realSubject; } /** * @param proxy 指代咱们所代理的那个真实对象 * @param method 指代的是咱们所要调用真实对象的某个方法的Method对象 * @param args 指代的是调用真实对象某个方法时接受的参数 * @reurn * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { sale(); proxy = method.invoke(realSubject, args); give(); return proxy; } public void sale() { System.out.println("打折"); } public void give() { System.out.println("送优惠券"); } }
hook翻译就是钩子。而「钩子」的意思,就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件同样,而且可以在钩上事件时,处理一些本身特定的事件。github
Hook 的选择点:
静态变量和单例,由于一旦建立对象,它们不容易变化,很是容易定位。
Hook 过程:
寻找 Hook 点,原则是静态变量或者单例对象,尽可能 Hook public 的对象和方法。
选择合适的代理方式,若是是接口能够用动态代理。
偷梁换柱——用代理对象替换原始对象。
Android 的 API 版本比较多,方法和类可能不同,因此要作好 API 的兼容工做编程
应用缓存
App登陆劫持服务器
登陆界面上面的用户信息都存储在EditText控件上,而后经过用户手动点击“登陆”按钮才会将上面的信息发送至服务器端去验证帐号与密码是否正确。这样就很简单了,黑客们只须要找到开发者在使用EditText控件的getText方法后进行网络验证的方法,Hook该方法,就能劫持到用户的帐户与密码了网络
hook练习jvm
正如面向对象编程是对常见问题的模块化同样,面向切面编程是对横向的同一问题进行模块化,好比在某个包下的全部类中的某一类方法中都须要解决一个类似的问题,能够经过AOP的编程方式对此进行模块化封装,统一解决
关于AOP的具体解释,能够参照维基百科。而AspectJ就是面向切面编程在Java中的一种具体实现。ide
Join point:程序中执行代码插入的点,例如方法调用时或者方法执行时。模块化
AOP编程的具体使用场景
日志记录
持久化
行为监测
数据验证
缓存
...
好比埋点,记录方法执行的时长。能够定义注解。aop能够过滤全部被"这个注解"标记的方法和构造器。而后能够能够根据他提供的方法(注解),讲咱们想要埋点的日志插入进去。
反射和注解
反射:对于任何一个对象,都可以调用它的任何一个方法和属性,包括私有的。这种动态获取的方法就叫反射。
注解:下降项目的耦合度;自动完成一些规律性的代码;自动生成java代码,减轻开发者的工做量。
@Retention:注解保留的生命周期
@Target:注解对象的做用范围。
建立一个注解遵循: public @interface 注解名 {方法参数}
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface getViewTo { int value() default -1; } public class MainActivity extends AppCompatActivity { @getViewTo(R.id.textview) private TextView mTv; @getViewTo(R.id.button) private Button mBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //经过注解生成View; getAllAnnotationView(); } /** * 解析注解,获取控件 */ private void getAllAnnotationView() { //得到成员变量 Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) { try { //判断注解 if (field.getAnnotations() != null) { //肯定注解类型 if (field.isAnnotationPresent(GetViewTo.class)) { //容许修改反射属性 field.setAccessible(true); GetViewTo getViewTo = field.getAnnotation(GetViewTo.class); //findViewById将注解的id,找到View注入成员变量中 field.set(this, findViewById(getViewTo.value())); } } } catch (Exception e) { } } } }
注解和反射效率问题
反射先new类class,而后在从类里面new对象。Class.getMethod(...)还要查找全部的方法。
而注解编译期间就完成了注解的反射工做, jvm只是读取。
JAVA反射机制是在运行状态中,对于任意一个类 (class文件),都可以知道这个类的全部属性和方法;
对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
动态获取类中信息,就是java反射 。能够理解为对类的解剖。
Person
public class Person { private int age; private String name; public Person(String name,int age) { super(); this.age = age; this.name = name; System.out.println("Person param run..."+this.name+":"+this.age); } public Person() { super(); System.out.println("person run"); } public void show(){ System.out.println(name+"...show run..."+age); } private void privateMethod(){ System.out.println(" method run "); } public void paramMethod(String str,int num){ System.out.println("paramMethod run....."+str+":"+num); } public static void staticMethod(){ System.out.println(" static method run......"); } }
要想要对字节码文件进行解剖,必需要有字节码文件对象.
如何获取字节码文件对象呢?
获取Class对象的三种方式
public class ReflectDemo { /** * @param args * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException { getClassObject_3(); } /* * 获取字节码对象的方式: * Object类中的getClass()方法的。 * 想要用这种方式,必需要明确具体的类,并建立对象。 * 麻烦 . * */ public static void getClassObject_1(){ Person p = new Person(); Class clazz = p.getClass(); Person p1 = new Person(); Class clazz1 = p1.getClass(); System.out.println(clazz==clazz1); } /* * 方式二: * 任何数据类型都具有一个静态的属性.class来获取其对应的Class对象。 * 相对简单,可是仍是要明确用到类中的静态成员。 * 仍是不够扩展。 * */ public static void getClassObject_2() { Class clazz = Person.class; Class clazz1 = Person.class; System.out.println(clazz==clazz1); } /* * 方式三: * 只要经过给定的类的 字符串名称就能够获取该类,更为扩展。 * 但是用Class类中的方法完成。 * 该方法就是forName. * 这种方式只要有名称便可,更为方便,扩展性更强。 */ public static void getClassObject_3() throws ClassNotFoundException { String className = "cn.test.bean.Person"; Class clazz = Class.forName(className); System.out.println(clazz); } }
获取Class中的构造函数
public class ReflectDemo2 { /** * @param args * @throws Exception * @throws InstantiationException * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException, InstantiationException, Exception { createNewObject_2(); } public static void createNewObject_2() throws Exception { // cn.test.bean.Person p = new cn.test.bean.Person("小强",39); /* * 当获取指定名称对应类中的所体现的对象时, * 而该对象初始化不使用空参数构造该怎么办呢? * 既然是经过指定的构造 函数进行对象的初始化, * 因此应该先获取到该构造函数。 经过字节码文件对象便可完成。 * 该方法是:getConstructor(paramterTypes); * */ String name = "cn.test.bean.Person"; //找寻该名称类文件,并加载进内存,并产生Class对象。 Class clazz = Class.forName(name); //获取到了指定的构造函数对 象。 Constructor constructor = clazz.getConstructor(String.class,int.class); //经过该构造器对象的newInstance方法进行对象的初始化。 Object obj = constructor.newInstance("小明",38); } public static void createNewObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException{ //早期:new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存, // 并建立该字节码文件对象,并接着建立该字节文件的对应的Person对象. // cn.test.bean.Person p = new cn.test.bean.Person(); //如今: String name = "cn.test.bean.Person"; //找寻该名称类文件,并加载进内存,并产生Class对象。 Class clazz = Class.forName(name); //如何产生该类的对象呢? Object obj = clazz.newInstance(); } }
获取Class中的字段
/* * 获取字节码文件中的字段。 */ public static void getFieldDemo() throws Exception { Class clazz = Class.forName("cn.test.bean.Person"); Field field = null;//clazz.getField("age");//只能获取公有的, field = clazz.getDeclaredField("age");//只获取本类,但包含私有。 //对私有字段的访问取消权限检查。暴力访问。 field.setAccessible(true); Object obj = clazz.newInstance(); field.set(obj, 89); Object o = field.get(obj); System.out.println(o); // cn.test.bean.Person p = new cn.test.bean.Person(); // p.age = 30; }
获取Class中的方法
public static void getMethodDemo_3() throws Exception { Class clazz = Class.forName("cn.test.bean.Person"); Method method = clazz.getMethod("paramMethod", String.class,int.class); Object obj = clazz.newInstance(); method.invoke(obj, "小强",89); } public static void getMethodDemo_2() throws Exception { Class clazz = Class.forName("cn.test.bean.Person"); Method method = clazz.getMethod("show", null);//获取空参数通常方法。 // Object obj = clazz.newInstance(); Constructor constructor = clazz.getConstructor(String.class,int.class); Object obj = constructor.newInstance("小明",37); method.invoke(obj, null); } /* * 获取指定Class中的全部公共函数。 */ public static void getMethodDemo() throws Exception { Class clazz = Class.forName("cn.test.bean.Person"); Method[] methods = clazz.getMethods();//获取的都是公有的方法。 methods = clazz.getDeclaredMethods();//只获取本类中全部方法,包含私有。 for(Method method : methods){ System.out.println(method); } }
如何得到泛型类的真实类型
经过Class类上的 getGenericSuperclass() 或者 getGenericInterfaces() 获取父类或者接口的类型,而后经过ParameterizedType.getActualTypeArguments()
public class RealType<T>{ private Class<T> clazz; // 使用反射技术获得T的真实类型 public Class getRealType(){ // 获取当前new的对象的泛型的父类类型 ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 获取第一个类型参数的真实类型 this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; return clazz; } }