03-java反射(二)

2022-07-18

03-java反射(二)

java反射(二)

一. 利用反射操作简单java类

1.1 定义实体类

  • company类

    package entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class Company implements Serializable {
    
        private String name;
        private String address;
        private Date creatDate;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public Date getCreatDate() {
            return creatDate;
        }
    
        public void setCreatDate(Date creatDate) {
            this.creatDate = creatDate;
        }
    }
    
    
  • Dept类

    package entity;
    
    import java.io.Serializable;
    
    public class Dept implements Serializable {
        private Integer deptno;
        private String dname;
        private String loc;
        private Company company = new Company();
    
        public Integer getDeptno() {
            return deptno;
        }
    
        public void setDeptno(Integer deptno) {
            this.deptno = deptno;
        }
    
        public String getDname() {
            return dname;
        }
    
        public void setDname(String dname) {
            this.dname = dname;
        }
    
        public String getLoc() {
            return loc;
        }
    
        public void setLoc(String loc) {
            this.loc = loc;
        }
    
        public Company getCompany() {
            return company;
        }
    
        public void setCompany(Company company) {
            this.company = company;
        }
    }
    
    
  • Emp类

    package entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class Emp implements Serializable {
        private Integer empno;
        private Double sal;
        private Date hiredate;
        private String ename;
        private String [] msg;
        private Integer [] iid;
        private Dept dept = new Dept();
    
        public Integer getEmpno() {
            return empno;
        }
    
        public void setEmpno(Integer empno) {
            this.empno = empno;
        }
    
        public Double getSal() {
            return sal;
        }
    
        public void setSal(Double sal) {
            this.sal = sal;
        }
    
        public Date getHiredate() {
            return hiredate;
        }
    
        public void setHiredate(Date hiredate) {
            this.hiredate = hiredate;
        }
    
        public String getEname() {
            return ename;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public String[] getMsg() {
            return msg;
        }
    
        public void setMsg(String[] msg) {
            this.msg = msg;
        }
    
        public Integer[] getIid() {
            return iid;
        }
    
        public void setIid(Integer[] iid) {
            this.iid = iid;
        }
    
        public Dept getDept() {
            return dept;
        }
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    }
    
    

1.2 编写工具类

  • BeanOperator

  • package util;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    
    /**
     * 本类利用反射设置给定的字符串对应的属性
     */
    public class BeanOperate {
    
        private Object obj;         //操作对象
        private String property;    //操作属性
        private Object value;       //数据内容
        private Field field;        //要操作的属性
        private String fieldName;   //属性的名字
        private Object currentObj;  //操作属性的对象
    
        public Field getField() {
            return field;
        }
    
        public Object getCurrentObj() {
            return currentObj;
        }
    
        public BeanOperate(Object obj, String property, Object value) {
            this.obj = obj;
            this.property = property;
            this.value = value;
            this.handleString();
            this.setFileValue();
        }
    
        /**
         * 处理属性字符串,将属性对象和普通属性分开
         */
        private void handleString() {
            String[] result = this.property.split("\\.");
            this.currentObj = this.obj;
            try {
                for (int i = 1; i < result.length; i++) {
                    Class<?> currentClass = this.currentObj.getClass();
                    this.field = currentClass.getDeclaredField(result[i]);
                    this.fieldName = result[i];
                    if (i < result.length - 1) {
                        Method getField = currentClass.getDeclaredMethod("get" + StringUtils.initcap(result[i]));
                        this.currentObj = getField.invoke(this.currentObj);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 设置属性的值
         */
        private void setFileValue(){
            Class<?> currentClass = this.currentObj.getClass();
            try {
                Method setField = currentClass.getDeclaredMethod("set" + StringUtils.initcap(this.fieldName), this.field.getType());
                String valType = value.getClass().getSimpleName();                                  //获取内容类型
                String fieldType = this.field.getType().getSimpleName();                            //获取属性类型字符串
                this.field.setAccessible(true);
                if("string".equalsIgnoreCase(valType)){                                             //如果内容是string类型
                    String val = (String) this.value;
                    if ("string".equalsIgnoreCase(fieldType)){                                      //如果是String类型
                        setField.invoke(this.currentObj, this.value);
                    }else if("int".equalsIgnoreCase(fieldType) || "Integer".equalsIgnoreCase(fieldType)){
                        setField.invoke(this.currentObj, Integer.parseInt(val));
                    }else if ("double".equalsIgnoreCase(fieldType)){
                        setField.invoke(this.currentObj, Double.parseDouble(val));
                    }else if ("date".equalsIgnoreCase(fieldType)){
                        setField.invoke(this.currentObj, new SimpleDateFormat("yyyy-MM-dd").parse(val));
                    }
                }else if("String[]".equalsIgnoreCase(valType)){
                    String [] val = (String[]) this.value;
                    if ("string[]".equalsIgnoreCase(fieldType)){
                        this.field.set(this.currentObj, val);
                    }else if("int[]".equalsIgnoreCase(fieldType) || "Integer[]".equalsIgnoreCase(fieldType)){
                        Integer [] v = new Integer[val.length];
                        for (int i = 0; i < val.length; i++) {
                            v[i] = Integer.parseInt(val[i]);
                        }
                        this.field.set(this.currentObj, v);
                    }else if("double[]".equalsIgnoreCase(fieldType)){
                        Double [] d = new Double[val.length];
                        for (int i = 0; i < val.length; i++) {
                            d[i] = Double.parseDouble(val[i]);
                        }
                        this.field.set(this.currentObj, d);
                    }
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }
    
    
  • StringUtils类

    package util;
    
    /**
     * 字符串操作工具类,用于字符串的处理
     */
    public class StringUtils {
        public static String initcap(String str){
            if (str == null){
                return null;
            }
            return str.substring(0, 1).toUpperCase() + str.substring(1);
        }
    }
    
    

1.3 编写测试类

  • package test1;
    import entity.Emp;
    import util.BeanOperate;
    import java.util.Arrays;
    
    public class TestSimple {
        private static Emp emp = new Emp();
        public static void main(String[] args) throws Exception{
            String attributeName = "emp.iid";
            String value [] = {"1", "2"};
            BeanOperate beanOperate = new BeanOperate(emp, attributeName, value);
            System.out.println(Arrays.toString(emp.getIid()));
        }
    }
    
    

二. ClassLoader

Class类中的方法

public ClassLoader getClassLoader()
  • 观察Classloader

    package test;
    
    public class TestClassloader {
        public static void main(String[] args) {
            System.out.println(new TestClassloader().getClass().getClassLoader());
            System.out.println(new TestClassloader().getClass().getClassLoader().getParent());
        }
    }
    
    

    结果如下

    sun.misc.Launcher$AppClassLoader@18b4aac2		系统加载器
    sun.misc.Launcher$ExtClassLoader@1b6d3586		扩展加载器
    

    ClassLoader是一个抽象类

    public abstract class ClassLoader extends Object
    

    因此必须有子类继承,重写

    public Class<?> loadClass(String name) throws ClassNotFoundException
    
  • 自定义类加载器

    • 实现类的加载器结构

      package test;
      
      class MyLoader extends ClassLoader{
      
          @Override
          public Class<?> loadClass(String name) throws ClassNotFoundException {
              return super.loadClass(name);
          }
      }
      
      public class TestClassloader {
          public static void main(String[] args) throws Exception{
              MyLoader myLoader = new MyLoader();
              Class<?> cls = myLoader.loadClass("java.util.Date");
              System.out.println(cls.newInstance());
      
          }
      }
      
    • 利用文件实现类的加载

      package test1;
      
      import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
      
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.InputStream;
      import java.net.URL;
      import java.net.URLConnection;
      
      class MyLoader extends ClassLoader{
      
          public Class<?> getClass(String name) throws Exception {
              byte[] data = this.loadClassNet(name);
              return super.defineClass(name, data, 0, data.length);
          }
      
      
          public byte [] loadClassFile(String name) throws Exception{
              String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
              String filePath = "F:\\test" + File.separator + fileName;
              File file = new File(filePath);
              System.out.println(filePath);
              InputStream inputStream = new FileInputStream(file);
              ByteOutputStream byteOutputStream = new ByteOutputStream();
              int len = 1;
              byte [] data = new byte[1024];
              while ((len = inputStream.read(data)) != -1){
                  byteOutputStream.write(data, 0, len);
              }
              inputStream.close();
              byteOutputStream.close();
              return byteOutputStream.toByteArray();
          }
      
          public byte [] loadClassNet(String name) throws Exception{
              String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
              URL url = new URL("http://mahaonan.com:8080/" + fileName);
              URLConnection urlConnection = url.openConnection();
              InputStream inputStream = urlConnection.getInputStream();
              ByteOutputStream byteOutputStream = new ByteOutputStream();
              int len = 1;
              byte [] data = new byte[1024];
              while ((len = inputStream.read(data)) != -1){
                  byteOutputStream.write(data, 0, len);
              }
              inputStream.close();
              byteOutputStream.close();
              return byteOutputStream.toByteArray();
          }
      }
      
      public class TestClassloader {
          public static void main(String[] args) throws Exception{
              MyLoader myLoader = new MyLoader();
              Class<?> cls = myLoader.getClass("test1.Book");
              System.out.println(cls.newInstance());
      
          }
      }
      
      
      

      利用defineClass(name, data, 0, data.length)方法, 从class文件流中加载类

      可以直接从服务器上读取.class文件加载,有妙用

三. 代理设计模式

3.1 标准代理的应用环境

  • service接口

    package service;
    
    public interface IDeptService {
    
        boolean insert();
    }
    
    
  • 代理类

    package service.proxy;
    
    import service.IDeptService;
    
    public class IDeptServiceProxy implements IDeptService {
    
        private IDeptService iDeptService;
    
        public IDeptServiceProxy(IDeptService iDeptService){
            this.iDeptService = iDeptService;
        }
    
        public void prepare(){
            System.out.println("打开数据库");
        }
    
        public void destroy(){
            System.out.println("关闭数据库");
        }
    
        @Override
        public boolean insert() {
            this.prepare();
            boolean flag = this.iDeptService.insert();
            this.destroy();
            return flag;
        }
    }
    
  • 真正主题实现类

    package service.impl;
    
    import service.IDeptService;
    
    public class IDeptServiceImpl implements IDeptService {
    
        @Override
        public boolean insert() {
            System.out.println("插入数据库");
            return true;
        }
    }
    
    
  • 工厂类

    package service.factory;
    
    import service.IDeptService;
    import service.impl.IDeptServiceImpl;
    import service.proxy.IDeptServiceProxy;
    
    public class IDeptServiceFactory {
    
        public static IDeptService getIDeptServiceInstance(){
            return new IDeptServiceProxy(new IDeptServiceImpl());
        }
    }
    
    
  • 客户端类

    package test;
    
    import service.IDeptService;
    import service.factory.IDeptServiceFactory;
    
    public class TestIDpetService {
        public static void main(String[] args) {
            IDeptService iDeptService = IDeptServiceFactory.getIDeptServiceInstance();
            iDeptService.insert();
        }
    }
    
  • 分析

    代理类必须为一个操作主题而存在
    
    代理的目的就是为了给真正的实现类增加一些额外的功能,这个有点类似于python的装饰器,增加额外的功能.为了让客户端直接得到需要的结果而不必考虑中间其他的过程,因此需要配合工厂模式来使用
    
    代理设计模式和工厂设计模式一起使用
    

3.2 动态代理模式

代理模式可以有效的抽取出核心业务和辅助业务
必须有一个类来实现Invocationhandler接口,里面有一个方法
Object invoke(Object proxy,Method method,Object[] args) throws Throwable

说明动态代理设计也属于反射

Object proxy 表示要代理的对象

Method method 表示要操作的方法

Object[] args 调用时传递的参数

代理类对象的产生可以通过java.lang.reflect.Proxy类

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
  • 实现动态代理类

    • 代理类
    package service.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 动态代理类
     */
    public class ServiceProxy implements InvocationHandler {
        private Object target = null;       //保存真实操作对象
    
        /**
         * 返回动态代理类的对象, 这样用户才可以使用代理类对象调用真实操作
         * @param object 包含有真实业务实现对象
         * @return       代理对象
         */
        public Object bind(Object object){
            this.target = oPbject;
            Object obj = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
            return obj;
        }
    
        public void prepare(){
            System.out.println("打开数据库");
        }
    
        public void destroy(){
            System.out.println("关闭数据库");
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            this.prepare();
            Object obj = method.invoke(this.target, args);
            this.destroy();
            return obj;
        }
    }
    
    
    • 工厂类
    package service.factory;
    
    import service.IDeptService;
    import service.impl.IDeptServiceImpl;
    import service.proxy.ServiceProxy;
    
    public class IDeptServiceFactory {
    
        public static IDeptService getIDeptServiceInstance(){
            return (IDeptService) new ServiceProxy().bind(new IDeptServiceImpl());
        }
    }
    
    
    • 客户端类
    package test;
    
    import service.IDeptService;
    import service.factory.IDeptServiceFactory;
    
    public class TestIDpetService {
        public static void main(String[] args) {
            IDeptService iDeptService = IDeptServiceFactory.getIDeptServiceInstance();
            iDeptService.insert();
        }
    }
    
    
  • 动态代理设计解析

    1. 动态代理类ServiceProxy实现了InvocationHandler接口, 重写了invoke方法
    2. 代理类需要绑定真实业务实现类,因此在该类中设计bind()方法,用来结合实现类创建代理对象
    3. 利用Proxy类中的newProxyInstance方法,创建了代理对象
    4. 由于利用反射invoke()需要实现类本身,因此设计属性tartget来保存真实对象
    5. 客户端在调用insert()方法时,调用者是代理对象,因此会进入代理类中的invoke()方法
    6. 在该方法中,调用代理类拓展的功能,和method.invoke()来实现方法的调用
  • 代理模式理解

    代理模式就是在现有方法的基础上拓展一些功能,为了做到通用性,利用反射来实现类的加载和方法的调用

    代理类必须保存真正实现类,这样才能完成方法的调用

3.3 CGLIB实现动态代理设计

传统动态代理设计模式有一个最大的缺陷,那么就是必须围绕接口进行.如下:

return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);

object.getClass().getInterfaces()

所有的动态代理设计模式不可能离开接口

不管是否使用cglib开发包,那么对于代理操作实际上本质是不变的

  • Proxy产生代理对象 -> net.sf.cglib.proxy.Enhancer
  • InvocationHandler -> net.sf.cglib.proxy.MethodInterceptor
  • Method负责真实操作的调用 -> net.sf.cglib.proxy.MethodProxy
  • 简单测试

    package test;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    class Book{
        public void get(){
            System.out.println("今天得到了一本书!");
        }
    }
    
    class BookProxy implements MethodInterceptor {
    
        private Object target;
    
        public BookProxy(Object target) {
            this.target = target;
        }
    
        public void pay(){
            System.out.println("给了收银员50元!");
        }
    
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            this.pay();
            return method.invoke(this.target, objects);
        }
    }
    
    public class TestCGLib {
        public static void main(String[] args) {
            Book book = new Book();         //真实对象
            Enhancer enhancer = new Enhancer();     //代理工具类对象
            enhancer.setSuperclass(Book.class);     //设置一个假定的父类
            enhancer.setCallback(new BookProxy(book));
            Book proxy = (Book) enhancer.create();      //创建代理对象
            proxy.get();
        }
    }
    
    

    本质其实一样,还是利用代理对象拓展一些额外的功能

    cglib最大的好处就是不需要接口,只需要真实的对象就可以了.

    操作流程还是一样的,代理类需要保存真实对象,需要利用反射调用真实对象的方法,只不过套了一层代理添加的方法

    感觉和python的装饰器越来越像了


标题:03-java反射(二)
作者:mahaonan
地址:https://mahaonan.fun/articles/2022/07/18/1658147074754.html