Java-反射2


1.反射

反射的练习

  • public void setProperty(Object obj, String propertyName, Object value){} 方法将 反射获取值 封装成一个方法,然后再通过调用方法通过传参的方式调用方法;

注意事项:

  • 因为方法中的参数,不知道是不是私有的,所以调用 getDeclaredField() 方法。并设置暴力访问;
public class Tool {
    public void setProperty(Object obj, String propertyName, Object value){
        //通过反射实现
        //通过反射获取对象的字节码文件对象
        Class<?> c = obj.getClass();
        //通过字节码文件对象,获取对应的成员变量形成一个对象
        try {
            Field declaredField = c.getDeclaredField(propertyName);
            //暴力访问
            declaredField.setAccessible(true);
            declaredField.set(obj,value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

调用方法

public class ToolText {
    public static void main(String[] args) {
        Person  p = new Person();
        // p.name = "阿涛"; // 这里因为 name 是私有属性,不能直接获取

        //可以通过自己写的工具来进行获取
        Tool tool = new Tool();
        tool.setProperty(p, "name", "阿涛");
        tool.setProperty(p, "age", 18);
        tool.setProperty(p, "address", "安徽安庆");
        tool.setProperty(p, "id", "1001");
        System.out.println(p);

    }
}

动态代理

  • 动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情。

通俗而言:

代理就是被代理者没有能力或者不愿意去做某件事情,需要找个人代替自己完成这件事,动态代理业务功能(方法)进行代理的

关键步骤:

  • 1.必须要有接口,实现类实现接口(代理通常是基于接口实现的)
  • 2.创建一个实现类对象,该对象为业务对象,紧接着为业务对象做一个代理对象

代码来袭:

以前的方法:

业务代码(接口)
public interface UserDao {
   void add();
   void delete();
   void update();
   void select();
}

实现类(实现接口)

public class UserDaoImpl implements UserDao{
   @Override
   public void add() {
      // System.out.println("权限校验");
       System.out.println("增加数据");
      // System.out.println("日志记录");
   }

   @Override
   public void delete() {
     //  System.out.println("权限校验");
       System.out.println("删除数据");
      // System.out.println("日志记录");
   }

   @Override
   public void update() {
      // System.out.println("权限校验");
       System.out.println("更新数据");
      // System.out.println("日志记录");
   }

   @Override
   public void select() {
     //  System.out.println("权限校验");
       System.out.println("查询数据");
     //  System.out.println("日志记录");
   }
}

调用实现类方法

public class UserDemo {
   public static void main(String[] args) {
       UserDaoImpl userDao = new UserDaoImpl();
       userDao.add();
       userDao.delete();
       userDao.update();
       userDao.select();
       System.out.println("=================================");
       //真实的开发环境不应该就输出一句话就完事了
       //更多的是实际操作
       //通常在操作数据库前后都有额外的步骤,在操作之前一般都会有一个权限校验
       //不是任何人都可以操作的

       //操作完成后也会留下日志记录
       //通过我们简单地分析并改动后,显然实现的我们想要的功能,但是也仅限于这个案例
       //如果我们在其他这样的案例中社交到的权限检验和日志记录
       //还是以这样的方式进行修改,会显得非常麻烦
   }
}

出现问题

  • 业务对象的每个方法在调用前后都需要进行权限校验和日志记录。存在大量重复代码.

改变:动态代理

动态代理代码

  • 此代码必须实现
    InvocationHandler 接口,然后重写其中的invoke 方法

代理者的代码为

public class InvocationHandlerImpl implements InvocationHandler {
    private Object proxy1; //目标对象,将来要给谁做代理,就将谁传进来
    //创建一个无参构造,将这个对象传进来
    InvocationHandlerImpl(Object proxy1){
        this.proxy1 = proxy1;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验");
        Object o = method.invoke(proxy1, args);
        System.out.println("日志记录");
        return o;
    }
}

被代理者的代码:

package com.bigdat.java.day29;
import java.lang.reflect.Proxy;
/*
    动态代理
 */
public class Text {
    public static void main(String[] args) {
        UserDaoImpl userDao = new UserDaoImpl();

        // 注意点:向下转型必须是转型为其接口
        UserDao p = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
                userDao.getClass().getInterfaces(),
                new InvocationHandlerImpl(userDao));
        p.add();
        p.delete();
        p.select();
        p.update();
        System.out.println("=========================================================================");
        TeacherDaoImpl teacherDao = new TeacherDaoImpl();
        TeacherDao o = (TeacherDao) Proxy.newProxyInstance(teacherDao.getClass().getClassLoader(),
                teacherDao.getClass().getInterfaces(),
                new InvocationHandlerImpl(teacherDao));
        o.login();
        o.zhuce();
    }
}

一个代理可以不仅可以代理UserDao,
还可以代理老师

teacherDao 代码:

public interface TeacherDao {
    void login();
    void zhuce();
}

老师的实现类 TeacherDaoInpl

public class TeacherDaoImpl implements TeacherDao{
    @Override
    public void login() {
        System.out.println("用户登录");
    }

    @Override
    public void zhuce() {
        System.out.println("用户注册");
    }
}

代理老师的方法和代理User的方法写在一起了

反射练习:通过配置文件,反射出其中的方法;

配置文件如下:
···
className=com.bigdat.java.day29.Person
methodName=fun2
···

反射代码如下:

package com.bigdat.java.day29;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

/*
        通过配置文件运行类中的方法
        Properties

        一般情况下,配置文件有以下几种格式:基本上都是以键值对的方式存在
            格式1:
                username: xxx
                password: xxx
                xxx: xxx

            格式2:
                <name>className</name>
                <value>com.xxx.xxx.xx</value>

            格式3:
                name=xiaohu
                age=18
                password=123456
 */
public class ReflexTest1 {
    public static void main(String[] args) throws  Exception{
        //学习反射之前
//        Person person = new Person();
//        person.fun2();
        Properties prop = new Properties();
        FileReader fr = new FileReader("D://Softwore_java//projects//基础语法1//src//com//" +
                "bigdat//java//day29//configure.txt");
        prop.load(fr);
        fr.close();

        //获取数据
        String className = prop.getProperty("className");
        System.out.println(className);
        String methodName = prop.getProperty("methodName");
        System.out.println(methodName);

        //通过反射实现
        Class<?> c = Class.forName(className);
        Constructor<?> con1 = c.getConstructor();
        Object o = con1.newInstance();

        Method fun2 = c.getDeclaredMethod(methodName, String.class);
        fun2.setAccessible(true);
        fun2.invoke(o,"你好!");
    }
}

反射练习:我给你ArrayList的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?

package com.bigdat.java.day29;

import java.lang.reflect.Method;
import java.util.ArrayList;

/*
        我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
 */
public class ReflexText2 {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(100);
        list.add(1023);

        //通过反射来实现
        Class<? extends ArrayList> c = list.getClass();
        Method add = c.getMethod("add", Object.class); // 使用 Object 类型,表示所有类型的数据都能加
        add.invoke(list,"我擦");
        add.invoke(list,"这都行?");
        add.invoke(list,10.23);
    }
}

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/244406.html

(0)
上一篇 2022年4月17日 01:59
下一篇 2022年4月17日 02:00

相关推荐

发表回复

登录后才能评论