摘要:本文主要讲解了反射的基础语法、反射在动态代理中的应用,动态代理主要讲解了JDK动态代理和Cglib的动态代理。两者的区别是JDK是面向接口的编程,Cglib是面向方法的编程,都有各自的应用场景。
1、什么反射?
Java程序在运行时,可以获取类的相关信息,可以动态调用对象的方法机制。
类比:类是所有对象的抽象,类对象class是对所有类的抽象。(个人观点)
2、反射的使用
反射应用的前提是获取类所对应的类对象,一个类能且只能产生一个类对象。假设在包com.smart.reflect下存在一个Student类,获取类对象的三个方法
(1) Class studentClass= Class.forName("com.smart.reflect.Student"); 推荐使用
(2) Student student = new Student (); Class studentClass = student.getClass();
(3) Class studentClass = Student.class;
拿到类对象之后,就可以获取类及其父类的相关信息,属性,方法(构造方法和普通方法),来看下主要信息
(1)Class 描述类本身
(1.1)获取类的修饰符:studentClass.getModifies()
(1.2)获取类的名字:studentClass.getSimpleName();studentClass.getName();
(1.3) 获取父类的信息:Class superClass = studentClass,getSuperClass()
(1.4)获取接口信息:Class[] interface = studentClass.getInterfaces();
创建对象
Object object = studentClass.newInstance(),调用无参构造函数;studentClass.newInstance()
(2)Package 包信息
获取包的相关信息:Package p = studentClass.getPackage();
(3)Field 属性信息
(3.1)获取 属性:Field field = studentClass.getField(属性名); Field field = studentClass.getDeclareField(属性名);
(3.2)获取属性的类型:Class fclass = field.getType();
(3.3) 为属性赋值:field.set(对象,值)
(3.4)取属性的值:field.get(对象)
(3.5) 获取所有公有的属性: Field[] field = studentClass.getFields()
(3.6) 获取所有的属性: Field[] field = studentClass.getDeclaredFields()
注意:访问私有属性 field.setAccessible(true);
(3.7) 获取所有内部类:Class[] innerClass = studentClass.getClasses()
(4)Method 方法信息
(4.1)获取 方法:Method method = studentClass.getMethod(方法名,类型); Method method = studentClass.getDeclareMethod(方法名,类型);
(4.2)获取方法的信息:返回值类型,方法名,参数列的类型,异常类型
(4.3) 获取所有公有的方法: Method[] method= studentClass.getMethods()
(4.4) 获取所有的方法: Method[] method = studentClass.getDeclaredMethods()
(4.5)有参数:method.invoke(对象,参数); 无参数:method.invoke(对象);
注意:访问私有属性 method.setAccessible(true);
(5)Constructor 用来描述类中的构造方法
(6)Annotation 描述类中的注解
3、反射的应用
3.1 动态代理
3.1.1.静态代理之Helloworld
(1)创建目标接口
interface Service{
void sayHello();
}
(2)实现目标接口
class RealService implements Service{
@Override
public void sayHello() {
System.out.println("hello, world!");
}
}
(3)创建代理类,实现了Service接口
class StaticProxy implements Service{
private Service realService;
public StaticProxy(Service service){
this.realService = service;
}
@Override
public void sayHello() {
realService.sayHello();
}
}
代理类中注入了Service接口的对象,实例化时只需要注入Service的实现类就好。
(4)创建测试程序
3.1.2.动态代理-JDK
(1)创建目标接口
interface Service{
void sayHello();
}
(2)实现目标接口
class RealService implements Service{
@Override
public void sayHello() {
System.out.println("hello, world!");
}
}
(3)创建代理类
public class DynamicProxyHanlder implements InvocationHandler {
private Object obj;
public DynamicProxyHanlder(Object realObj){
this.obj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(obj, args);
return null;
}
}
创建动态代理,主要是实现InvocationHandler接口,重写了invoke方法,其中obj表示要真实代理的类。
(4)创建测试程序
public class Client {
public static void main(String[] args) {
Service realService = new ServiceImpl();
InvocationHandler proxyHandler = new DynamicProxyHanlder(realService);
Service proxyService =
(Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class<?>[]{Service.class},
proxyHandler);
proxyService.sayHello();
}
}
代理类传入的参数是:被代理接口的类加载器,类类型的数组,代理类。
3.1.3.动态代理-Cglib
(1)创建目标方法类
class Service{
public void sayHello(){
System.out.println("hello,world");
}
}
(2)创建代理类
class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
Object result = proxy.invokeSuper(object, args);
return result;
}
}
实现的是MethodInterceptorie接口中的intercept的方法
(3)创建测试程序
public class Main {
private static <T> T getProxy(Class<T> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new SimpleInterceptor());
return (T)enhancer.create();
}
public static void main(String[] args) {
Service proxyService = getProxy(Service.class);
proxyService.sayHello();
}
}
总结:静态代理和动态代理-JDK是面向接口编程的,而动态代理-cglib是面向方法的编程。
动态代理涉及到类加载器的加载,具体的文章请参考:https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html。
4、类加载器和反射的关系
类加载器将字节码文件加载如JVM。
5、反射实例 (简化版的SpringIOC的实现)
5.1创建Person类:
/**
*
* @author smart 2019/3/23
*/
public class Person {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
private Integer age;
}
5.2 创建简化版的Spring处理类
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
*
* @author smart 2019/3/23
*/
public class MySpring {
public Object getBean(String packagePath){
Object result = null;
Scanner scanner = new Scanner(System.in);
try{
//创建类
Class<?> classObject = Class.forName(packagePath);
result = classObject.newInstance();
//获取类的属性
Field[] fields = classObject.getDeclaredFields();
for (Field field: fields) {
//获取属性名
String name = field.getName();
String firstLetter = name.substring(0,1).toUpperCase();
String otherLetter = name.substring(1);
StringBuilder propertiesMethod = new StringBuilder("set");
propertiesMethod.append(firstLetter).append(otherLetter);
Class fieldClass = field.getType();
Method method = classObject.getMethod(propertiesMethod.toString(),fieldClass);
//参数可以从文件中读取或者从注解中读取
System.out.println("请输入参数");
String param = scanner.nextLine();
Constructor con = fieldClass.getConstructor(String.class);
method.invoke(result,con.newInstance(param));
}
}catch (Exception e){
e.printStackTrace();
}
return result ;
}
}
5.3测试及结果
/**
*
* @author smart 2019/3/23
*/
public class MainTest {
public static void main(String[] args) {
MySpring mySpring = new MySpring();
Person person = (Person) mySpring.getBean("Person");
System.out.println(person);
}
}
测试结果:
请输入参数
12222
请输入参数
12
Person{name='12222', age=12}
5.4 利用注解的方式注入值
(1)自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author smart 2019/3/23
*/
@Target(value = {ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
String value();
}
(2)修改Person类
/**
*
* @author smart 2019/3/23
*/
public class Person {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@MyAnnoation("smart")
private String name;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@MyAnnoation("12")
private Integer age;
}
(3)修改MySpring类
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
*
* @author smart 2019/3/23
*/
public class MySpring {
public Object getBean(String packagePath){
Object result = null;
Scanner scanner = new Scanner(System.in);
try{
//创建类
Class<?> classObject = Class.forName(packagePath);
result = classObject.newInstance();
//获取类的属性
Field[] fields = classObject.getDeclaredFields();
for (Field field: fields) {
//获取属性名
String name = field.getName();
String firstLetter = name.substring(0,1).toUpperCase();
String otherLetter = name.substring(1);
StringBuilder propertiesMethod = new StringBuilder("set");
propertiesMethod.append(firstLetter).append(otherLetter);
Class fieldClass = field.getType();
Method method = classObject.getMethod(propertiesMethod.toString(),fieldClass);
//参数可以从文件中读取或者从注解中读取
Annotation annotation = field.getAnnotation(MyAnnoation.class);
Constructor con = fieldClass.getConstructor(String.class);
method.invoke(result,con.newInstance(((MyAnnoation) annotation).value()));
}
}catch (Exception e){
e.printStackTrace();
}
return result ;
}
}
(4)测试结果
Person{name='smart', age=12}
备注:调用注解的里的value方法,可以通过如下的代码:‘
’Annotation annotation = field.getAnnotation(MyAnnoation.class);
Class annotationClass = annotation.getClass();
Method method = annotationClass.getMethod("value");
String value = method.invoke(annotationClass);