Java反射技术--SpringIOC原理和SpringAop原理

本文介绍了Java反射机制,包括动态编译、反射API的使用,以及它在Android插件化和Hook技术中的作用。接着讲解了Spring的IOC容器实现,模拟了一个简单的IOC容器,解析XML配置文件并使用反射实现依赖注入。然后阐述了Spring AOP基于代理模式和Java反射的实现原理,包括JDK动态代理和CGLib。最后,通过租房的例子展示了JDK动态代理的工作方式。
摘要由CSDN通过智能技术生成


前言
相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有个开源框架jOOR相信很多人都用过,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架,当然这里所讲的反射技术,是学习Android插件化技术、Hook技术等必不可少的!

Java的反射

动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。

反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。

在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。

实现Java反射机制的类都位于java.lang.reflect包中:

  1. Class类:代表一个类
  2. Field类:代表类的成员变量(类的属性)
  3. Method类:代表类的方法
  4. Constructor类:代表类的构造方法
  5. Array类:提供了动态创建数组,以及访问数组的元素的静态方法

一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。

在这里插入图片描述
推荐一个讲Java反射的博主-----点击

实现极简IOC容器(SpringIOC原理)

模拟ioc容器,了解ioc容器原理

定义接口

package com.zhongruan.context;

public interface ApplicationContext {
    public Object getBean(String beanId);
}

定义实现类

package com.zhongruan.context;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplication implements ApplicationContext{
    private Map iocContainer = new HashMap();

    public ClassPathXmlApplication() {
        String filePath = this.getClass().getResource("/applicationContext.xml").getPath();
        try {
            filePath = new URLDecoder().decode(filePath, "utf-8");
            SAXReader reader = new SAXReader();
            Document document = reader.read(new File(filePath));
            List<Node> beans = document.getRootElement().selectNodes("bean");//读取xml中所有Bean配置
            for (Node node: beans){
                Element ele = (Element) node;
                String id = ele.attributeValue("id");//获取Bean的id属性
                String className = ele.attributeValue("class");//获取Bean的class属性
                //类对象(jvm虚拟机载入字节码文件时自动生成)
                //类对象是对类的描述,其中中包含了类的所有属性对象,行为对象
                //className = com.cjl.entity.Apple可以这样写 就是配置bean时的类的全限定名
                Class<?> aClass = Class.forName(className);//创建类对象
                Object obj = aClass.newInstance();//创建类的对象
                List<Node> properties = ele.selectNodes("property");//读取xml中一个Bean配置中所有property标签
                for (Node p: properties){//遍历property标签
                    Element property = (Element)p;
                    String propName = property.attributeValue("name");//获取property标签的name属性
                    String propValue = property.attributeValue("value");//获取property标签的value属性

                    System.out.println(propValue);
					//获取方法的名字
                    String setMethodName = "set" + propName.substring(0, 1).toUpperCase()+propName.substring(1);
                    System.out.println(setMethodName);
                    //创建方法对象
                    Method setMethod = aClass.getMethod(setMethodName, String.class);
                    //通过setter方法实现注入
                    //obj类对象 propValue方法参数
                    setMethod.invoke(obj, propValue);
                }
                iocContainer.put(id, obj);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String beanId) {
        return iocContainer.get(beanId);
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="apple" class="com.zhongruan.entity.Apple">
        <property name="title" value="金帅"></property>
        <property name="color" value="黄色"></property>
        <property name="origin" value="山东"></property>
    </bean>
</beans>

测试类

package com.zhongruan;

import com.zhongruan.context.ApplicationContext;
import com.zhongruan.context.ClassPathXmlApplication;
import com.zhongruan.entity.Apple;

public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplication();
        Apple sweetApple = (Apple) context.getBean("apple");
        System.out.println(sweetApple);
    }
}

SpringAop原理

SpringAop==代理模式+Java反射技术

Spring AOP实现原理
  • Spring基于代理模式实现功能动态扩展,包含两种形式:
    • 目标类拥有接口,通过JDK动态代理实现功能扩展
    • 目标类没有接口,通过CGLib组件实现功能扩展(CGLib组件会自动为目标对象和代理对象生成一个接口,让他们去实现)
代理模式
  • 代理模式通过代理对象对原对象的实现功能扩展

在这里插入图片描述
这里我们可以看出来,代理模式的代理类(目标对象的类) 委托类(代理对象的类)必须实现同一个接口。

静态代理proxy

  • 定义接口

    public interface UserService {
        public void createUser();
    }
    
  • 定义实现类

    public class UserServiceImpl implements UserService {
        public void createUser() {
            System.out.println("执行创建用户业务逻辑");
        }
    }
    
  • 测试类

    public class Application {
        public static void main(String[] args) {
            UserService userService = new UserServiceImpl();
            userService.createUser();
        }
    }
    
  • 添加代理类

    //代理类
    public class UserServiceProxy implements UserService {
        //持有委托类对象
        private UserService userService;
    
        public UserServiceProxy(UserService userService) {
            this.userService = userService;
        }
    
        public void createUser() {
            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            userService.createUser();
        }
    }
    
  • 改写测试类

    public class Application {
        public static void main(String[] args) {
            UserService userService = new UserServiceProxy(new UserServiceImpl());//将目标对象传递给代理对象,代理对象对目标对象的createUser()方法进行增强
            userService.createUser();
        }
    }
    
  • 再定义一个代理类

    //代理类
    public class UserServiceProxyPost implements UserService{
        //委托类对象
        private UserService service;
    
        public UserServiceProxyPost(UserService service) {
            this.service = service;
        }
    
        public void createUser() {
            service.createUser();
            System.out.println("后置扩展功能");
        }
    }
    
  • 改写测试类

    public class Application {
        public static void main(String[] args) {
            UserService userService = new UserServiceProxyPost(new UserServiceProxy(new UserServiceImpl()));
            userService.createUser();
        }
    }
    

动态代理

  • 动态代理就是可以任意的控制任意对象的执行过程,意思就是说这个对象的执行过程可以由客户端灵活的指定,可能这样说还是不太明确,通俗说就是
  • 本来应该自己做的事情,因为没有某种原因不能直接做,只能请别人代理做。被请的人就是代理。
    动态代理的表现形式即同一个代理对象可以代理多个被代理对象,不再像之前的静态代理仅约束于一个被代理对象。

简单来说动态代理就是利用Java反射技术让一个代理对象能代理多个
需要被代理的对象进行代理处理

JDK动态代理
  • 目标类拥有接口,通过JDK动态代理实现功能扩展

以租房子为例简绍动态代理

1.RentOutHouse(租房接口)

package com.cjl.rent;

public interface RentOutHouse {
    public void rent();
}

2.HouseOwer类(房东类)

package com.cjl.rent;

public class HouseOwer implements RentOutHouse {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

3.ProxyPerson(代理类1–中介)

package com.cjl.rent;

public class ProxyPerson implements RentOutHouse{

    private RentOutHouse rentOutHouse;

    public ProxyPerson(RentOutHouse rentOutHouse) {
        this.rentOutHouse = rentOutHouse;
    }

    @Override
    public void rent() {
        System.out.println("签订合同");
        System.out.println("带客户去看房子");
        System.out.println("管理房东的钥匙");

        //目标方法
        rentOutHouse.rent();
    }
}

4.SecondProxyPerson(代理类2–第二个中介对第一个中介进行代理)

package com.cjl.rent;

public class SecondProxyPerson implements RentOutHouse {
    private RentOutHouse rentOutHouse;

    public SecondProxyPerson(RentOutHouse rentOutHouse) {
        this.rentOutHouse = rentOutHouse;
    }

    @Override
    public void rent() {
        System.out.println("改造房子");
        rentOutHouse.rent();
    }
}

5.ProxyInvocationHandler类 --实现动态代理的类

package com.cjl.auto;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
//LogHandler类,功能是负责控制任意被代理对象中任意方法的执行过程。简单说具体到本类中功能就是记录方法运行前后的日志
public class ProxyInvocationHandler implements InvocationHandler {

    //目标对象
    private Object target;

    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    //proxy 代理对象
    //method 目标方法对象
    //args 目标方法实参
    //Object 返回值,目标方法的返回结果
    //throwable 目标方法抛出的异常
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("目标方法:"+method.getName()+"开始执行的时间:"+new Date());
        //执行目标方法
        Object ret = method.invoke(target, args);
        return ret;
    }
}

6.TestProxy类–测试类

import com.cjl.auto.ProxyInvocationHandler;
import com.cjl.rent.HouseOwer;
import com.cjl.rent.ProxyPerson;
import com.cjl.rent.RentOutHouse;
import com.cjl.rent.SecondProxyPerson;
import com.cjl.service.ProxyUserServiceImpl;
import com.cjl.service.UserService;
import com.cjl.service.UserServiceImpl;
import org.junit.Test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestProxy {

//静态代理
    @Test
    public void testProxy(){
        //委托类的对象
        UserService userService = new UserServiceImpl();

        //代理类对象
        UserService proxyUserService = new ProxyUserServiceImpl(userService);
        proxyUserService.createUser();
    }
//对代理对象进行代理
    @Test
    public void testSecondProxyHouse(){
        SecondProxyPerson secondProxyPerson = new SecondProxyPerson(new ProxyPerson(new HouseOwer()));
        secondProxyPerson.rent();
    }


//测试反射技术
    @Test
    public void testClassObj() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        UserServiceImpl userService = new UserServiceImpl();
        //得到类对象
        Class classUserServiceImpl = Class.forName("com.cjl.service.UserServiceImpl");
        Method createUser = classUserServiceImpl.getMethod("createUser");
        createUser.invoke(userService);//执行对象createUser方法
    }
    
//动态代理
    @Test
    public void testAutoProxy(){
        UserService userService = new UserServiceImpl();
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(userService);

        //创建userService的代理对象
        UserService proxyUserService = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), proxyInvocationHandler);
        proxyUserService.createUser();

        RentOutHouse rentOutHouse = new HouseOwer();
        ProxyInvocationHandler proxyInvocationHandlerHouse = new ProxyInvocationHandler(rentOutHouse);
        // newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)方法用于为指定 类装载器、一组接口及 调用处理器生成动态代理类实例 
        RentOutHouse proxyPerson = (RentOutHouse) Proxy.newProxyInstance(rentOutHouse.getClass().getClassLoader(), rentOutHouse.getClass().getInterfaces(), proxyInvocationHandlerHouse);
        proxyPerson.rent();
    }
}

在这里插入图片描述

7.JDK动态代理-Proxy.newProxyInstance()方法

  • java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口目标接口的类加载器以及InvocationHandler便可为目标接口生成代理类及代理对象。
// 方法 1: 该方法用于获取指定代理对象所关联的InvocationHandler
static InvocationHandler getInvocationHandler(Object proxy) 

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces) 

// 方法 3:该方法用于判断指定类是否是一个动态代理类
static boolean isProxyClass(Class cl) 

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

1.JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的;

2.但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

一个简绍反射类Proxy的博主https://www.cnblogs.com/wobuchifanqie/p/9991342.html

CGLib组件
  • 目标类没有接口,通过CGLib组件实现功能扩展

  • CGLib是运行时字节码增强技术

  • Spring AOP扩展无接口类使用CGLib

  • AOP会运行时生成目标继承类字节码的方式进行行为扩展在这里插入图片描述
    CGLib组件可以自动的为代理类和委托类生成一个接口(Java反射技术)

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值