反射
反射是Java語言的重要特性烤惊,它允許程序運(yùn)行時(shí)進(jìn)行自我檢查魏颓,也允許對內(nèi)部的成員
進(jìn)行操作忘瓦,能夠?qū)崿F(xiàn)在運(yùn)行時(shí)對類進(jìn)行裝載格仲,使程序運(yùn)行時(shí)更加靈活辜荠,但是也有注意正確使用
否則會對性能有影響。
案例1 基本的反射
父類
public class Parents {
public void function(){
System.out.println("I'm parents!");
}
}
子類
public class Children extends Parents{
@Override
public void function() {
System.out.println("I'm children!");;
}
}
測試類
import one.Parents;public class TestReflection {
@org.junit.Test
public void testFunction() throws Exception{
Class cls = Class.forName("one.Children");// 包名.類名
Parents parents = (Parents)cls.newInstance();
parents.function();
}
}
這時(shí)候會調(diào)用子類的function方法輸出
I'm children!
我們可以寫一個(gè)父類的工廠方法抓狭,傳入子類的名稱,自動調(diào)用子類的方法造烁,一些RPC框架就是基于這種動態(tài)代理的思想
這里我們增加一個(gè)實(shí)現(xiàn)類Student
public class Student extends Parents{
@Override
public void function() {
System.out.println("I'm children!");;
}
}
增加一個(gè)工廠否过,模擬注冊中心,調(diào)用子類的方法
public class Factory {
private static Map<String, String> map = new HashMap<String, String>();
static{
map.clear();
// 模擬注冊中心惭蟋,存放實(shí)現(xiàn)類
map.put("one.Children", "one.Children");
map.put("one.Student", "one.Student");
}
public static void main(String[] args) {
run("one.Student");
}
private static
void run(String clzName){
if(null==clzName || clzName.length()==0 || "".equals(clzName)){
System.out.println("參數(shù)不能為空");
return;
}
if(!map.containsKey(clzName)){
System.out.println("參數(shù)不合法");
return;
}
try{
Class cls = Class.forName(map.get(clzName));
Parents parents = (Parents)cls.newInstance();
System.out.println("執(zhí)行方法"+clzName);
parents.function();
}catch (Exception e){
e.printStackTrace();
}
}
}
運(yùn)行Factory會出現(xiàn)
執(zhí)行方法Student
I'm children!
案例2 動態(tài)代理實(shí)現(xiàn)反射
父類接口
public interface Parents {
public void function();
}
子類實(shí)現(xiàn)
public class Children implements Parents{
@Override
public void function() {
System.out.println("I'm children!");
}
}
基本的代理類苗桂,繼承InvocationHandler接口,實(shí)現(xiàn)invoke方法告组。
public class Intermediary implements InvocationHandler {
private Object post;
public Intermediary(Object post) {
this.post = post;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(post, args);
System.out.println("代理類:打印日志");
return invoke;
}
}
優(yōu)雅的代理類煤伟,直接返回對象。采用內(nèi)部類的方式木缝,將反射的方法當(dāng)作參數(shù)傳遞給InvocationHandler
public class GetObject {
/**
* 反射+動態(tài)代理的方式調(diào)用Parents里面的方法
* @return
*/
public static void runObject(final Parents post){
// 調(diào)用方法時(shí)的處理器便锨,本質(zhì)都是在調(diào)用invoke()方法
InvocationHandler h = new InvocationHandler() {
/**
* 調(diào)用方法的處理內(nèi)容放在invoke里面
* @param proxy 代理對象
* @param method 調(diào)用的方法
* @param args 傳遞的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(post, args);
System.out.println("代理類:打印日志");
return invoke;
}
};
// 參數(shù):產(chǎn)生這個(gè)代理類的classLoader, 實(shí)現(xiàn)了這個(gè)代理類的接口,h
Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
System.out.println(o.getClass().getName());
System.out.println(o.getClass().getInterfaces()[0]);
Parents parents = (Parents) o;
parents.function();
return;
}
}
測試下上述兩個(gè)類的效果
public class TestIntermediary {
public static void main(String[] args) {
// 基本的代理類
Parents child = new Children();
Intermediary intermediary = new Intermediary(child);
Parents proxy = (Parents) Proxy.newProxyInstance(child.getClass().getClassLoader(), child.getClass().getInterfaces(), intermediary);
proxy.function();
// 優(yōu)雅的代理工具類
GetObject.runObject(new Children());
}
}
通過代理類可以在代理類中包裝一些方法進(jìn)去我碟,以下是運(yùn)行結(jié)果
I'm children!
代理類:打印日志
com.sun.proxy.$Proxy0
interface two.Parents
I'm children!
代理類:打印日志
案例3 通過代理類設(shè)置方法攔截器
自定義一個(gè)服務(wù)
public class MyService {
public String run(String something){
// int i = 1/0;
return "服務(wù)正在運(yùn)行...." + something;
}
}
定義一個(gè)其他服務(wù)
public class OtherService {
public String runOther(String something){
// int i = 1/0;
return "服務(wù)正在運(yùn)行...." + something;
}
}
寫一個(gè)服務(wù)攔截器放案,對不是“run”的方法進(jìn)行攔截
public class ServiceInterceptor implements MethodInterceptor {
private static final String INTERCEPTOR = "run";
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object result = null;
if(!INTERCEPTOR.equals(methodInvocation.getMethod().getName())){
System.out.println("不能執(zhí)行該方法" + methodInvocation.getMethod().getName());
return null;
}
try {
System.out.println("方法執(zhí)行之前:" + methodInvocation.getMethod().toString());
result = methodInvocation.proceed();
System.out.println("方法正常運(yùn)行結(jié)果:" + result);
System.out.println("方法執(zhí)行之后:" + methodInvocation.getMethod().toString());
return result;
} catch (Exception e) {
System.out.println("方法出現(xiàn)異常:" + e.toString());
System.out.println("方法運(yùn)行Exception結(jié)果:" + result);
return result;
}
}
}
public class TestMethodInterceptor {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new MyService());
proxyFactory.addAdvice(new ServiceInterceptor());
Object proxy = proxyFactory.getProxy();
MyService myService = (MyService) proxy;
myService.run("通過代理工廠設(shè)置代理對象,攔截代理方法");
proxyFactory.setTarget(new OtherService());
proxy = proxyFactory.getProxy();
OtherService otherService = (OtherService) proxy;
otherService.runOther("通過代理工廠設(shè)置代理對象矫俺,攔截代理方法");
}
}
可以看到otherService.runOther方法被攔截了
方法執(zhí)行之前:public java.lang.String three.MyService.run(java.lang.String)
方法正常運(yùn)行結(jié)果:服務(wù)正在運(yùn)行....通過代理工廠設(shè)置代理對象,攔截代理方法
方法執(zhí)行之后:public java.lang.String three.MyService.run(java.lang.String)
不能執(zhí)行該方法runOther
案例4 抽象服務(wù)的接口
定義一個(gè)父類實(shí)現(xiàn)接口打印
public class BaseService {
/**
*
* @param something
* @return
*/
String run(String something){
return null;
}
void printLog(){
System.out.println("統(tǒng)一打印日志");
}
}
ServiceOne實(shí)現(xiàn)上述接口
public class ServiceOne extends BaseService {
@Override
String run(String something) {
return "ServiceOne "+ something;
}
}
ServiceTwo實(shí)現(xiàn)上述接口
public class ServiceTwo extends BaseService{
@Override
String run(String something) {
return "ServiceTwo "+ something;
}
}
借助Spring攔截器据忘,實(shí)現(xiàn)反射
public class ServiceInterceptor implements MethodInterceptor {
private static final String INTERCEPTOR = "printLog";
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object result = null;
try {
if(INTERCEPTOR.equals(methodInvocation.getMethod().getName())){
//日志直接打印
result = methodInvocation.proceed();
return result;
}
System.out.println("方法執(zhí)行之前:" + methodInvocation.getMethod().toString());
result = methodInvocation.proceed();
System.out.println("方法正常運(yùn)行結(jié)果:" + result);
System.out.println("方法執(zhí)行之后:" + methodInvocation.getMethod().toString());
return result;
} catch (Exception e) {
System.out.println("方法出現(xiàn)異常:" + e.toString());
System.out.println("方法運(yùn)行Exception結(jié)果:" + result);
return result;
}
}
}
定義測試類扣泊,調(diào)用ServiceOne和ServiceTwo, 把二者的公共方法printLog直接放在test里面調(diào)用,反射不會對其執(zhí)行環(huán)繞方法稿湿。
public class TestMethodInterceptor {
public static void main(String[] args) {
ServiceOne serviceOne = (ServiceOne) test(new ServiceOne());
serviceOne.run("通過代理工廠設(shè)置代理對象,攔截代理方法");
ServiceTwo serviceTwo = (ServiceTwo) test(new ServiceTwo());
serviceTwo.run("通過代理工廠設(shè)置代理對象押赊,攔截代理方法");
}
public static BaseService test(BaseService baseService){
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(baseService);
proxyFactory.addAdvice(new ServiceInterceptor());
Object proxy = proxyFactory.getProxy();
BaseService baseProxy = (BaseService) proxy;
baseProxy.printLog();
return baseProxy;
}
}
查看執(zhí)行結(jié)果
統(tǒng)一打印日志
方法執(zhí)行之前:java.lang.String four.ServiceOne.run(java.lang.String)
方法正常運(yùn)行結(jié)果:ServiceOne 通過代理工廠設(shè)置代理對象饺藤,攔截代理方法
方法執(zhí)行之后:java.lang.String four.ServiceOne.run(java.lang.String)
統(tǒng)一打印日志
方法執(zhí)行之前:java.lang.String four.ServiceTwo.run(java.lang.String)
方法正常運(yùn)行結(jié)果:ServiceTwo 通過代理工廠設(shè)置代理對象,攔截代理方法
方法執(zhí)行之后:java.lang.String four.ServiceTwo.run(java.lang.String)
案例5 使用FastClass完成反射考杉,執(zhí)行類中的所有方法
父接口與原來保持一致
public interface Parents {
String hello(String name);
}
子類實(shí)現(xiàn)接口
public class Children implements Parents {
@Override
public String hello(String name) {
return "I'm " + name;
}
}
實(shí)現(xiàn)基本的代理類
public class Intermediary implements InvocationHandler {
private Object post;
public Intermediary(Object post) {
this.post = post;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(post, args);
System.out.println("代理類:打印日志");
return invoke;
}
}
調(diào)用FastClass實(shí)現(xiàn)反射策精,循環(huán)遍歷類中的方法執(zhí)行
public class GetObject {
/**
* 反射+動態(tài)代理的方式調(diào)用Parents里面的方法
* @return
*/
public static Object runObject(final Parents post, String methodName, Object[] parameters){
try {
Class<?> clazz = Class.forName(post.getClass().getName());
//獲取本類的所有方法,存放入數(shù)組
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if(methodName.equals(method.getName())){
System.out.println("方法名:"+method.getName());
//獲取本方法所有參數(shù)類型崇棠,存入數(shù)組
Class<?>[] getTypeParameters = method.getParameterTypes();
if(getTypeParameters.length==0){
System.out.println("此方法無參數(shù)");
}
for (Class<?> class1 : getTypeParameters) {
String parameterName = class1.getName();
System.out.println("參數(shù)類型:"+parameterName);
}
FastClass fastClazz = FastClass.create(Parents.class);
int methodIndex = fastClazz.getIndex(method.getName(), getTypeParameters);
return fastClazz.invoke(methodIndex, post, parameters);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
執(zhí)行代理類
public class TestIntermediary {
public static void main(String[] args) {
// 基本的代理類
Parents child = new Children();
Intermediary intermediary = new Intermediary(child);
Parents proxy = (Parents) Proxy.newProxyInstance(child.getClass().getClassLoader(), child.getClass().getInterfaces(), intermediary);
System.out.println(proxy.hello("shgx"));
// 優(yōu)雅的代理工具類
System.out.println(GetObject.runObject(new Children(), "hello", new String[]{"shgx"}));
}
}
執(zhí)行結(jié)果如下:
代理類:打印日志
I'm shgx
方法名:hello
參數(shù)類型:java.lang.String
I'm shgx
案例6 使用PropertyUtil和BeanUtil拷貝參數(shù)
父類接口
public interface Parents {
void function();
}
子類-外部類
public class OuterClass implements Parents{
private Long id;
private String name;
private Integer sex;
private Double age;
private Date birthDay;
// 省略get set toString方法
@Override
public void function() {
System.out.println("OuterClass{" +
"id=" + id +
", name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
", birthDay=" + birthDay +
'}');
}
}
子類-內(nèi)部類
public class InnerClass implements Parents {
private Long id;
private String name;
private Integer sex;
private Double age;
private Date birthDay;
// 新增, 與上面的類形成對比
private String address;
// 省略get set toString方法
@Override
public void function() {
System.out.println("反射實(shí)現(xiàn) InnerClass{" +
"id=" + getId() +
", name='" + getName() + '\'' +
", sex=" + getSex() +
", age=" + getAge() +
", birthDay=" + getBirthDay() +
", address='" + address + '\'' +
'}');
}
}
優(yōu)雅的代理類實(shí)現(xiàn)反射
public class GetObject {
/**
* 反射+動態(tài)代理的方式調(diào)用Parents里面的方法
* @return
*/
public static void runObject(final Parents post){
// 調(diào)用方法時(shí)的處理器咽袜,本質(zhì)都是在調(diào)用invoke()方法
InvocationHandler h = new InvocationHandler() {
/**
* 調(diào)用方法的處理內(nèi)容放在invoke里面
* @param proxy 代理對象
* @param method 調(diào)用的方法
* @param args 傳遞的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(post, args);
return invoke;
}
};
// 參數(shù):產(chǎn)生這個(gè)代理類的classLoader, 實(shí)現(xiàn)了這個(gè)代理類的接口,h
Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
Parents parents = (Parents) o;
parents.function();
}
}
實(shí)現(xiàn)對象之間的參數(shù)拷貝
public class CopyUtil {
@SuppressWarnings("unchecked")
public static void getClassByBeanUtil(InnerClass dest, OuterClass source) {
try {
BeanUtils.copyProperties(dest, source);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("BeanUtils result:" + dest);
}
public static void getClassByPropertyUtil(InnerClass dest, OuterClass source) {
try {
PropertyUtils.copyProperties(dest, source);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println("PropertyUtils result:" + dest);
}
}
public class TestIntermediary {
public static void main(String[] args) {
// 基本的代理類
OuterClass outerClass = new OuterClass(1L, "shgx", 1, 23.5, new Date());
// BeanUtil 完成對象拷貝
InnerClass innerClassOne = new InnerClass();
CopyUtil.getClassByBeanUtil(innerClassOne, outerClass);
GetObject.runObject(innerClassOne);
// PropertyUtil 完成對象拷貝
InnerClass innerClassTwo = new InnerClass();
CopyUtil.getClassByPropertyUtil(innerClassTwo, outerClass);
GetObject.runObject(innerClassTwo);
// 置空
outerClass.setId(null);
outerClass.setName(null);
outerClass.setAge(null);
outerClass.setSex(null);
outerClass.setBirthDay(null);
// BeanUtil
CopyUtil.getClassByBeanUtil(innerClassOne, outerClass);
GetObject.runObject(innerClassOne);
// PropertyUtil
CopyUtil.getClassByPropertyUtil(innerClassTwo, outerClass);
GetObject.runObject(innerClassTwo);
}
}
執(zhí)行結(jié)果
BeanUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射實(shí)現(xiàn) InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
PropertyUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射實(shí)現(xiàn) InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
BeanUtils result:InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
反射實(shí)現(xiàn) InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
PropertyUtils result:InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
反射實(shí)現(xiàn) InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
上述方法可以在不同系統(tǒng)之間完成參數(shù)傳輸枕稀,實(shí)現(xiàn)RPC調(diào)用
置空之后在執(zhí)行發(fā)現(xiàn)對象拷貝完成也是null询刹,不會賦予默認(rèn)值
多余的參數(shù)會賦予null值,不會拋出異常
案例7 使用注解和反射實(shí)現(xiàn)日志打印
使用注解的方式配置日志
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
/** 日志類型 */
LogTypeEnum logType() default LogTypeEnum.SERVICE_LOG;
/** 業(yè)務(wù)名 */
String bizName() default "";
/** 自定義日志打印*/
Class<? extends LogInfo> customerLogType() default LogInfo.class;
/** 是否打印日志*/
boolean recordMonitorData() default false;
}
不在展開描述萎坷,見項(xiàng)目空間