咱们可能都用过 Spring AOP ,底层的实现原理是怎样的呢?

反射常用于编写工具,企业级开发要用到的 Mybatis、Spring 等框架,底层的实现都用到了反射。能用好反射,就能提高我们编码的核心能力。

反射机制

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

作用:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

常用的类:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

Class 类

Class 类的实例表示正在运行的 Java 应用程序中的类和接口,Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机及通过调用类加载器中的 defineClass 方法自动构造的。

  • 一个类在 JVM 中只会有一个 Class 实例
  • 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过 Class 可以完整地得到一个类中的完整结构

获取 Class 对象

获取 Class 对象有4种方式,前三种比较常用。

首先创建一个类用于测试

package com.jikedaquan.reflection;

public class User {

	private int id;
private String username;
private String password; public User() {
} public User(int id, String username, String password) {
super();
this.id = id;
this.username = username;
this.password = password;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public void show() {
System.out.println("Hello");
} @Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
}
}

编写测试

package com.jikedaquan.reflection;

public class GetClass {

	public static void main(String[] args) {
//方法1
try {
Class clz1=Class.forName("com.jikedaquan.reflection.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("找不到指定类");
}
//方法2
Class clz2=User.class;
//方法3
User user=new User();
Class clz3=user.getClass(); //方法4 类的加载器
try {
Class clz4=GetClass.class.getClassLoader().loadClass("com.jikedaquan.reflection.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("找不到指定类");
}
}
}

方法1语法:Class Class对象 = Class.forName(包名+类名);

方法2语法:Class Class对象 = 类名.class;

方法3语法:Class Class对象 = 对象.getClass();

getClass() 方法是从 Object 类中继承过来的

获取类的结构

Class 类常用方法

方法名称 说明
Annotation[] getAnnotations() 返回此元素上存在的所有注解
Constructor getConstructor(Class<?>... parameterTypes) 获取指定参数的构造函数
Constructor<?>[] getConstructors() 返回包含的公有构造方法
Constructor<?>[] getDeclaredConstructors() 返回所有构造方法
Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 根据方法名和参数获取方法对象

API 中可以看到有两种获取结构的方式:getDeclaredXxx()和getXxx();getDeclaredXxx()可以获取所有包括私有的

获取类的结构

package com.jikedaquan.reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; public class GetClassStruct { public static void main(String[] args) {
try {
Class clz=Class.forName("com.jikedaquan.reflection.User");
System.out.println("===========构造===========");
//获取构造方法
Constructor[] cons=clz.getDeclaredConstructors();
for (Constructor constructor : cons) {
System.out.println(constructor);
}
//获取字段
System.out.println("===========字段===========");
Field[] fields=clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//获取方法
System.out.println("===========方法===========");
Method[] methods=clz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
//获取父类
System.out.println("===========父类===========");
Class supperClass=clz.getSuperclass();
System.out.println(supperClass.getName());
//获取实现的接口
System.out.println("===========接口===========");
Class[] interfaces=clz.getInterfaces();
for (Class interf : interfaces) {
System.out.println(interf);
}
//获取注解
System.out.println("===========注解===========");
Annotation[] annotations=clz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

调用类的指定方法、属性

获取构造方法并实例化对象

注意:jdk1.9弃用此方式实例化对象

Object obj=clz.newInstance();

通过反射获取有参或无参构造后方可实例化化对象

package com.jikedaquan.reflection;

import java.lang.reflect.Constructor;

public class CallConstructor {

	public static void main(String[] args) {
//获取User 的 Class
Class<User> clz=User.class; //获取无参构造方法并实例化
try {
//getConstructor()方法不传参即无参
Constructor<User> constructor=clz.getConstructor();
User user=constructor.newInstance();
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
//获取有参构造方法并实例化
try {
Constructor<User> constructor=clz.getConstructor(int.class,String.class,String.class);
User user=constructor.newInstance(18,"张三","abc123");
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}

获取指定构造方法时,第二个参数为动态参数,不填写即获取无参构造方法,填写指定个数和指定类型.class可获取对应方式的构造方法。

调用类中的方法

package com.jikedaquan.reflection;

import java.lang.reflect.Method;

public class CallMethod {

	public static void main(String[] args) {
//获取User 的 Class
Class<User> clz=User.class;
//获取无参方法 show
try {
Method method=clz.getMethod("show");
//执行clz中的方法
method.invoke(clz.getConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
//获取一个参数为String的方法
try {
Method method=clz.getMethod("setUsername", String.class);
//反射实例化对象
User user=clz.getConstructor().newInstance();
//执行这个对象的方法
method.invoke(user, "反射");
//测试结果
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}

如果有多个参数,获取方法:getMethod("方法名称",参数1.class,参数2.class,参数3.class)

多个参数执行时:method.invoke(对象,参数1,参数2,参数3);

动态代理

动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要创建目标类的代理对象。

原理:

使用一个代理将对象包装起来,然后用该代理对象取代原对象,任何对原始对象的调用都要通过dialing,代理对象决定是否以及何时将方法调用转到原始对象上。

生活中海外代购其实就用到了代理,你可能不方便出国,但是代购可以,最终帮你完成购买行为。

以代购为例子完成静态代理

package com.jikedaquan.reflection;

//购买接口(约定)
interface Buy{
void buyProduct();
}
//被代理的
class Customer implements Buy{ @Override
public void buyProduct() {
System.out.println("购买商品");
}
}
//代理
class ProxyBuy implements Buy{
private Customer customer; public ProxyBuy(Customer customer) {
this.customer=customer;
} @Override
public void buyProduct() {
System.out.println("代理:出国");
//被代理的对象的行为
customer.buyProduct();
System.out.println("代理:回国");
}
} public class TestStaticProxy { public static void main(String[] args) {
Customer customer=new Customer();
ProxyBuy proxyBuy=new ProxyBuy(customer);
proxyBuy.buyProduct();
}
}

那么动态代理意味着不能只代理 Customer 类的行为,还可以代理其他类的行为

package com.jikedaquan.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; //工厂接口
interface Factory{
void product();
}
//电脑工厂
class ComputerFactory implements Factory{ @Override
public void product() {
System.out.println("生产电脑");
}
}
//动态代理处理器
class MyInvocationHandler implements InvocationHandler{
//要被代理的对象
private Object proxyObj;
//产生代理对象
public Object bind(Object proxyObj) {
this.proxyObj=proxyObj;
return Proxy.newProxyInstance(
proxyObj.getClass().getClassLoader(),
proxyObj.getClass().getInterfaces(),
this
);
}
//代理对象实际执行的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理:收收费");
Object result=method.invoke(proxyObj, args);
System.out.println("代理:代理完成");
return result;
} } public class TestDynamicProxy { public static void main(String[] args) {
//创建代理对象生产器
MyInvocationHandler invocationHandler=new MyInvocationHandler(); //创建要被代理的对象
ComputerFactory computerFactory=new ComputerFactory();
//生产代理对象
Object factoryProxy=invocationHandler.bind(computerFactory);
Factory factory=(Factory) factoryProxy;
factory.product(); //创建另一个要被代理的对象(上个示例静态代理的对象和接口)
Customer customer=new Customer();
//生产代理对象
Object buyProxy=invocationHandler.bind(customer);
Buy buy=(Buy) buyProxy;
buy.buyProduct();
}
}

在 main 方法中,创建了一个 MyInvocationHandler 对象,通过 bind 方法可以传入任意要被代理的对象,实现了动态。

重点来了,拿好小本子笔记!!!!!

实现动态代理的步骤

1.创建要被代理的类的接口

2.创建要被代理的类实现类

3.创建代理对象处理器(MyInvocationHandler),实现 InvocationHandler 接口

4.编写生产代理对象的方法,方法内调用 Proxy.newInstance() 方法,返回代理对象

5.重写 InvocationHandler 的 invoke 方法

6.测试:创建代理对象生产器,生产代理对象

最新文章

  1. [综]前景检测GMM
  2. PHP 判断数组里的值是否有存在
  3. MySQL表类型
  4. System.exit()方法的作用
  5. Android(java)学习笔记131:Intent启动别的Activity
  6. Java多线程---------同步与死锁:synchronized;等待与唤醒:wait、notify、notifyAll;生命周期
  7. 产生文件命令touch,echo,cat&lt;&lt;EOF&gt;test,less,more,tail,head
  8. HDU_2056——相交矩形的面积
  9. Balanced Binary Tree(Java代码没有结束,是什么原因???)
  10. jquery中,使用append增加元素时,该元素的绑定监听事件失效
  11. WPF:动态显示或隐藏Listview的某一列
  12. 从送外卖到建站售主机还有共享自行车说起-2017年8月江西IDC排行榜与发展报告
  13. android 自定义Viewpager实现无限循环
  14. dispatch_barrier_async--屏障是一个同步点
  15. 《Windows核心编程》读书笔记 上
  16. Visual C++中最常用的类与API函数
  17. 20172308 实验四《Java面向对象程序设计 》实验报告
  18. BZOJ2118: 墨墨的等式(同余类BFS)(数学转为图论题)
  19. RabbitMQ---8、连接断开处理-断线重连
  20. 【已解决】Android ADT中增大AVD内存后无法启动:emulator failed to allocate memory

热门文章

  1. docker中安装Jenkins
  2. asp.net——Ajax与ashx异步请求的简单案例
  3. 基于 ZKEACMS 的云建站 / 自助建站解决方案
  4. 微软 eshop 数据存储之sqlserver
  5. 中文 bootstrapValidator
  6. Linux系统忘记管理员密码(CentOS、RHEL、Ubuntu)
  7. 【09】循序渐进学 docker:docker swarm
  8. Web App三年将取代原生App?
  9. 【随记】WPF中xaml输入中文乱码问题解决
  10. POJ3076 Sudoku