Java反射及代理模式

反射

反射是Java语言的重要特性,它允许程序运行时进行自我检查,也允许对内部的成员
进行操作,能够实现在运行时对类进行装载,使程序运行时更加灵活,但是也有注意正确使用
否则会对性能有影响。

案例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();
    }
}

这时候会调用子类的function方法输出

I'm children!

我们可以写一个父类的工厂方法,传入子类的名称,自动调用子类的方法,一些RPC框架就是基于这种动态代理的思想

这里我们增加一个实现类Student

public class Student extends Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");;
    }
}

增加一个工厂,模拟注册中心,调用子类的方法

public class Factory {
    private static Map map = new HashMap();

    static{
        map.clear();
        // 模拟注册中心,存放实现类
        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("参数不能为空");
            return;
        }
        if(!map.containsKey(clzName)){
            System.out.println("参数不合法");
            return;
        }
        try{
            Class cls = Class.forName(map.get(clzName));
            Parents parents = (Parents)cls.newInstance();
            System.out.println("执行方法"+clzName);
            parents.function();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运行Factory会出现

执行方法Student
I'm children!

案例2 动态代理实现反射

父类接口

public interface Parents {
    public void function();
}

子类实现

public class Children implements Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");
    }
}

基本的代理类,继承InvocationHandler接口,实现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;
    }
}

优雅的代理类,直接返回对象。采用内部类的方式,将反射的方法当作参数传递给InvocationHandler

public class GetObject {
    /**
     * 反射+动态代理的方式调用Parents里面的方法
     * @return
     */
    public static void runObject(final Parents post){
        // 调用方法时的处理器,本质都是在调用invoke()方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * 调用方法的处理内容放在invoke里面
             * @param proxy 代理对象
             * @param method 调用的方法
             * @param args 传递的参数
             * @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;
            }
        };
        // 参数:产生这个代理类的classLoader, 实现了这个代理类的接口,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;
    }
}

测试下上述两个类的效果

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();

        // 优雅的代理工具类
        GetObject.runObject(new Children());
    }
}

通过代理类可以在代理类中包装一些方法进去,以下是运行结果

I'm children!
代理类:打印日志
com.sun.proxy.$Proxy0
interface two.Parents
I'm children!
代理类:打印日志

案例3 通过代理类设置方法拦截器

自定义一个服务

public class MyService {
    public String run(String something){
        // int i = 1/0;
        return "服务正在运行...." + something;
    }
}

定义一个其他服务

public class OtherService {
    public String runOther(String something){
        // int i = 1/0;
        return "服务正在运行...." + something;
    }
}

写一个服务拦截器,对不是“run”的方法进行拦截

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("不能执行该方法" + methodInvocation.getMethod().getName());
            return null;
        }
        try {
            System.out.println("方法执行之前:" + methodInvocation.getMethod().toString());

            result = methodInvocation.proceed();
            System.out.println("方法正常运行结果:" + result);

            System.out.println("方法执行之后:" + methodInvocation.getMethod().toString());
            return result;

        } catch (Exception e) {
            System.out.println("方法出现异常:" + e.toString());
            System.out.println("方法运行Exception结果:" + 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("通过代理工厂设置代理对象,拦截代理方法");

        proxyFactory.setTarget(new OtherService());
        proxy = proxyFactory.getProxy();
        OtherService otherService = (OtherService) proxy;
        otherService.runOther("通过代理工厂设置代理对象,拦截代理方法");
    }
}

可以看到otherService.runOther方法被拦截了

方法执行之前:public java.lang.String three.MyService.run(java.lang.String)
方法正常运行结果:服务正在运行....通过代理工厂设置代理对象,拦截代理方法
方法执行之后:public java.lang.String three.MyService.run(java.lang.String)
不能执行该方法runOther

案例4 抽象服务的接口

定义一个父类实现接口打印

public class BaseService {
    /**
     *
     * @param something
     * @return
     */
    String run(String something){
        return null;
    }
    void printLog(){
        System.out.println("统一打印日志");
    }
}

ServiceOne实现上述接口

public class ServiceOne extends BaseService {
    @Override
    String run(String something) {
        return "ServiceOne "+ something;
    }
}

ServiceTwo实现上述接口

public class ServiceTwo extends BaseService{

    @Override
    String run(String something) {
        return "ServiceTwo "+ something;
    }
}

借助Spring拦截器,实现反射

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("方法执行之前:" + methodInvocation.getMethod().toString());

            result = methodInvocation.proceed();
            System.out.println("方法正常运行结果:" + result);

            System.out.println("方法执行之后:" + methodInvocation.getMethod().toString());
            return result;

        } catch (Exception e) {
            System.out.println("方法出现异常:" + e.toString());
            System.out.println("方法运行Exception结果:" + result);
            return result;
        }
    }
}

定义测试类,调用ServiceOne和ServiceTwo, 把二者的公共方法printLog直接放在test里面调用,反射不会对其执行环绕方法。

public class TestMethodInterceptor {
    public static void main(String[] args) {
        ServiceOne serviceOne = (ServiceOne) test(new ServiceOne());
        serviceOne.run("通过代理工厂设置代理对象,拦截代理方法");

        ServiceTwo serviceTwo = (ServiceTwo) test(new ServiceTwo());
        serviceTwo.run("通过代理工厂设置代理对象,拦截代理方法");
    }

    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;
    }
}

查看执行结果

统一打印日志
方法执行之前:java.lang.String four.ServiceOne.run(java.lang.String)
方法正常运行结果:ServiceOne 通过代理工厂设置代理对象,拦截代理方法
方法执行之后:java.lang.String four.ServiceOne.run(java.lang.String)
统一打印日志
方法执行之前:java.lang.String four.ServiceTwo.run(java.lang.String)
方法正常运行结果:ServiceTwo 通过代理工厂设置代理对象,拦截代理方法
方法执行之后:java.lang.String four.ServiceTwo.run(java.lang.String)

案例5 使用FastClass完成反射,执行类中的所有方法

父接口与原来保持一致

public interface Parents {
    String hello(String name);
}

子类实现接口

public class Children implements Parents {

    @Override
    public String hello(String name) {
        return "I'm " + name;
    }
}

实现基本的代理类

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;
    }
}

调用FastClass实现反射,循环遍历类中的方法执行

public class GetObject {

    /**
     * 反射+动态代理的方式调用Parents里面的方法
     * @return
     */
    public static Object runObject(final Parents post, String methodName, Object[] parameters){
        try {
            Class clazz = Class.forName(post.getClass().getName());
            //获取本类的所有方法,存放入数组
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if(methodName.equals(method.getName())){
                    System.out.println("方法名:"+method.getName());
                    //获取本方法所有参数类型,存入数组
                    Class[] getTypeParameters = method.getParameterTypes();
                    if(getTypeParameters.length==0){
                        System.out.println("此方法无参数");
                    }
                    for (Class class1 : getTypeParameters) {
                        String parameterName = class1.getName();
                        System.out.println("参数类型:"+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;
    }
}

执行代理类

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"));

        // 优雅的代理工具类
        System.out.println(GetObject.runObject(new Children(), "hello", new String[]{"shgx"}));
    }
}

执行结果如下:

代理类:打印日志
I'm shgx
方法名:hello
参数类型:java.lang.String
I'm shgx

案例6 使用PropertyUtil和BeanUtil拷贝参数

父类接口

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 +
                '}');
    }
}

子类-内部类

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("反射实现 InnerClass{" +
                "id=" + getId() +
                ", name='" + getName() + '\'' +
                ", sex=" + getSex() +
                ", age=" + getAge() +
                ", birthDay=" + getBirthDay() +
                ", address='" + address + '\'' +
                '}');
    }
}

优雅的代理类实现反射

public class GetObject {

    /**
     * 反射+动态代理的方式调用Parents里面的方法
     * @return
     */
    public static void runObject(final Parents post){
        // 调用方法时的处理器,本质都是在调用invoke()方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * 调用方法的处理内容放在invoke里面
             * @param proxy 代理对象
             * @param method 调用的方法
             * @param args 传递的参数
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(post, args);
                return invoke;
            }
        };
        // 参数:产生这个代理类的classLoader, 实现了这个代理类的接口,h
        Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
        Parents parents = (Parents) o;
        parents.function();
    }
}

实现对象之间的参数拷贝

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);

    }
}

执行结果

BeanUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射实现 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'}
反射实现 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'}
反射实现 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'}
反射实现 InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}

上述方法可以在不同系统之间完成参数传输,实现RPC调用

置空之后在执行发现对象拷贝完成也是null,不会赋予默认值

多余的参数会赋予null值,不会抛出异常

案例7 使用注解和反射实现日志打印

使用注解的方式配置日志

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
    /** 日志类型 */
    LogTypeEnum logType() default LogTypeEnum.SERVICE_LOG;

    /** 业务名 */
    String bizName() default "";

    /** 自定义日志打印*/
    Class customerLogType() default LogInfo.class;

    /** 是否打印日志*/
    boolean recordMonitorData() default false;
}

不在展开描述,见项目空间

源码参考

项目代码

你可能感兴趣的:(Java反射及代理模式)