摘要:本文主要講解了反射的基礎語法、反射在動態代理中的應用,動態代理主要講解了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);