java反射在spring ioc和aop中的应用

news2024/9/20 10:54:47

java反射在spring ioc和aop中的应用

反射

1.反射是什么?

程序运行时,通过类名能够获得类的属性和方法。使用方式如下

  • Class clazz= Class.ForName(“Student”)
  • Class clazz = Student.class;
  • Class clazz = student.getClass();

获取到clazz以后 就能通过clazz获取其他属性和方法。

2.反射的原理

Object 类(所有类都继承这个类) 其中有个方法 public final Class getClass() 所有的子类都继承了这个方法,返回了一个Class类;这个Class类中包含了某个类的属性、方法、和构造器等;对应的是一个加载到 JVM的中的一个.class 文件

正常方式:

引入类名称----------》通过这个类名去new一个对象----------------》取得实例化对象

反射:

取得实例化对象------------------》getClass()方法----------------------》得到完整的“包类”名称

所有被创建的类 都被放到一个Class类中

获取反射对象 只有一个地址,一个类被加载,

3.怎么使用反射?

反射在Spring IOC和AOP中的应用

反射与ioc

ioc是一种思想 叫做控制反转,也就是说把对象创建的主动权 new一个对象 从 用户手中交给了IOC容器去管理,当一个对象需要一些外部资源的时候,IOC容器帮助我们注入这些所依赖的对象!

ioc=工厂模式+反射+文件属性

根据配置文件,应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。默认是无参构造

不用反射机制的工厂模式

/**
  * 工厂模式
  */
 interface fruit{
     public abstract void eat();
 }
 
 class Apple implements fruit{
     public void eat(){
         System.out.println("Apple");
     }
 }
 
 class Orange implements fruit{
     public void eat(){
         System.out.println("Orange");
     }
 }
 // 构造工厂类
 // 也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了
 class Factory{
     public static fruit getInstance(String fruitName){
         fruit f=null;
         if("Apple".equals(fruitName)){
             f=new Apple();
         }
         if("Orange".equals(fruitName)){
             f=new Orange();
         }
         return f;
     }
 }
 
 class hello{
     public static void main(String[] a){
         fruit f=Factory.getInstance("Orange");
         f.eat();
     }
 }

利用反射机制的工厂模式

interface fruit{
     public abstract void eat();
 }
 
 class Apple implements fruit{
     public void eat(){
         System.out.println("Apple");
     }
 }
 
 class Orange implements fruit{
     public void eat(){
         System.out.println("Orange");
     }
 }
 
 class Factory{
     public static fruit getInstance(String ClassName){
         fruit f=null;
         try{
             f=(fruit)Class.forName(ClassName).newInstance();
         }catch (Exception e) {
             e.printStackTrace();
         }
         return f;
     }
 }
 
 class hello{
     public static void main(String[] a){
         fruit f=Factory.getInstance("Reflect.Apple");
         if(f!=null){
             f.eat();
         }
     }
 }

利用反射,只要传入类名,就可以获取实力类,我们怎么知道包和类名呢?通过一个配置文件

apple=xxxx.Apple

orange=xxxx.Orange

只要修改配置文件即可。

Spring IOC 容器的的顶层接口时BeanFactory,但是我们一般使用的时ApplicationContext接口,三个常用的实现类AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、ClassPathXmlApplicationContext

类型名简介
ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContextApplicationContext 的子接口,包含一些扩展方法refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力
WebApplicationContext专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中

举例:基于XML管理bean

//创建一个类
public class HelloWorld {
    public void sayHello() {
        System.out.println("helloworld");
    }

}


//resource目录下创建xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
配置HelloWorld所对应的bean,即将HelloWorld的对象交给Spring的IOC容器管理
通过bean标签配置IOC容器所管理的bean
属性:
id:设置bean的唯一标识
class:设置bean所对应类型的全类名
-->
    <bean id="helloworld" class="ioc.beanWithXml.HelloWorld"></bean>
</beans>
//创建测试类
public class testHelloworld {
    @Test
    public  void testHelloWorld(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloworld = (HelloWorld) classPathXmlApplicationContext.getBean("helloworld");
        helloworld.sayHello();
    }
}

是怎么通过xml就可以获得 helloworld对象的呢?反射的运用。

反射与AOP

1.aop是什么?

AOP是一种思想,对oop思想的一种补充,在不修改代码的前提下添加一种额外的功能

也叫面向切面编程,将一些与主要业务无关的,但有有一定通用性的功能代码,比如说 日志等,单独拎取出来。需要调用的时候调用就好了。

相关概念

  • 横切关注点:对目标对象来说的,非核心业务

  • 通知:非核心业务在切面中的调用

    • 前置通知:在被代理的目标方法执行
    • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
    • 异常通知:在被代理的目标方法异常结束后执行(死于非命
    • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
    • 环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
  • 切面:封装横切关注点的类,或者说是封装通知方法的类

  • 目标:被代理的目标对象

  • 代理:向目标对象应用通知之后创建的代理对象。

  • 连接点:标识要加入的额外功能的位置

在这里插入图片描述

  • 切入点:真正的切入代码的位置,定位横切点

AOP的总体流程:抽和插,目标对象是提前就有的,代理对象是JDK 动态代理帮助我们生成的,

从目标对象中,把非核心业务抽取出来,非核心业务就叫做横切关注点,横切关注点封装到类中,这个类就叫做切面,在切面中,每一个横切关注点都是一个方法,这个方法就叫做通知,通知有不同的类型,再定位到目标对象抽取横切关注点的位置,这个位置就叫做连接点,连接点是通过切入点定位的。

基于注解的AOP实现

  • aspectJ

    编译时织入(应用到java代码的过程)

在这里插入图片描述

注意:AOP的底层只是用到了反射,最底层的原理主要是动态代理,动态代理中包含有反射。

比如JDK动态代理

1.被代理类实现一个接口

2.创建代理对象,需要实现InvocationHandler

3.代理过程在invoke中实现。

//被代理的类
public interface Calculator {
    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);
}
//被代理类的实现
public class CalculatorImpl implements Calculator {
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;
    }

    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
}

现在有一个需求:在计算的前后增加日志

普通实现

public class CalculatorLogImpl implements Calculator {
    public int add(int i, int j) {
        System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] add 方法结束了,结果是:" + result);
        return result;
    }

    public int sub(int i, int j) {
        System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] sub 方法结束了,结果是:" + result);
        return result;
    }

    public int mul(int i, int j) {
        System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] mul 方法结束了,结果是:" + result);
        return result;
    }

    public int div(int i, int j) {
        System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] div 方法结束了,结果是:" + result);
        return result;
    }
}

动态代理

下面就有用到了反射

public class ProxyFactory {
    //代理对象的工厂类
    private Object target;

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

    public Object getProxy(){
        /***
         * newProxyInstance():创建一个代理实例
         * * 其中有三个参数:
         * * 1、classLoader:加载动态生成的代理类的类加载器
         * * 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
         * * 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接 口中的抽象方法
         * */

        ClassLoader classLoader=target.getClass().getClassLoader();//获得类加载器
        Class<?>[] interfaces = target.getClass().getInterfaces();//获取接口
        InvocationHandler invocationHandler=new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*** proxy:代理对象 * method:代理对象需要实现的方法,即其中需要重写的方法 * args:method所对应方法的参数
                 * */
                System.out.println("日志:方法名:"+method.getName()+"参数:"+ Arrays.toString(args));
                Object invoke = method.invoke(target, args);
                System.out.println("结果:"+invoke);
                return invoke;
            }
        };

        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

调用

public class ProxyTest {
    public static void main(String[] args) {
        //普通调用方法
//        Calculator cal=new CalculatorImpl();
//        cal.add(1,2);

        //调用有日志的方法
//        Calculator calLog=new CalculatorLogImpl();
//        calLog.add(1,2);

//
//        System.out.println("#################");
//        Calculator calStaticProxy=new CalculatorStaticProxy(calLog);
//        calStaticProxy.add(1,2);

        ProxyFactory proxyFactory=new ProxyFactory(new CalculatorImpl());
        Calculator proxy = (Calculator) proxyFactory.getProxy();//如果不转型 不知道要调用哪个方法
        proxy.add(1,2);

    }
}

2.AOP的使用场景

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging  调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence  持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/165753.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java JUC 中 Object里wait()、notify() 实现原理及实战讲解

1.Object中的wait()实现原理 在进行wait()之前&#xff0c;就代表着需要争夺Synchorized&#xff0c;而Synchronized代码块通过javap生成的字节码中包含monitorenter和monitorexit两个指令。 当在进加锁的时候会执行monitorenter指令&#xff0c;执行该指令可以获取对象的mon…

前端与HTML

本节课程围绕“前端要解决的基本问题”及“什么是 HTML ”两个基本问题展开&#xff0c;了解 HTML 高效的编写原则。 什么是前端 使用web技术栈解决多端的人机交互问题 技术栈 html&#xff08;内容&#xff09; css &#xff08;样式&#xff09;javascript &#xff08;行…

linux部署KubeSphere和k8s集群

上一篇文章讲述了在单个节点上安装 KubeSphere和k8s&#xff0c;这节主要讲解k8s多节点集群部署 准备环境&#xff1a;Alibaba Cloud Linux系统3台机器第一步&#xff1a;设置主机名称hostname--(3台机器都设置) hostnamectl set-hostname master hostnamectl set-hostname nod…

智云通CRM:为什么你总是在请客,但业绩却上不来?

王总是一位企业老板&#xff0c;社会资源比较好&#xff0c;在过去的一年里&#xff0c;他新代理的一个保健品的项目&#xff0c;需要销售产品和招募合伙人。他想利用自己的人脉资源做销售&#xff0c;但他的销售过程并不顺利&#xff0c;在连续主动邀约之后效果不佳。 于是他…

2023/1/15 JS-变量提升与函数提升 执行上下文

1 变量提升与函数提升 变量声明提升 通过 var 声明的变量&#xff0c;在声明语句之前就可以访问到 - 值: undefined <script>console.log(a); // undefinedvar a 10 </script>函数声明提升 通过 function 声明的函数, 在声明语句之前就可以直接调用 - 值: 函数…

走近软件生态系统

生态系统&#xff08;Ecosystem&#xff09;原本是一个生物学术语&#xff0c;意思是由一些生命体相互依存、相互制约而形成的大系统&#xff0c;就像我们学生时代在生物学课堂上学到的那样。隐喻无处不在&#xff0c;人们把这个术语移植到了 IT 领域中来&#xff0c;比如我们常…

计算机基础(六):静态链接与动态链接

上一篇文章简单概括了 C语言程序经过编译&#xff0c;生成汇编语言、机器语言的基本过程。今天主要介绍其中链接阶段的实现思路。 静态链接 静态链接是将被依赖的代码片段复制到执行程序中&#xff0c;进行代码整合。因为我们在汇编代码中看到的是具体的符号&#xff0c;而且…

电路方案分析(十七)TI远程声控参考设计

远程声控参考设计 描述 CC2650远程控制设计为基于ZigBeeRF4CE™兼容的软件架构RemeTI™或蓝牙低能耗软件堆栈的快速测试、评估和开发远程控制应用程序提供了最佳基础。 该方案设计包含了CC2560远程控制的原理图和布局文件&#xff0c;以及一个演示了使用RF4CE和低能耗蓝牙的…

层次分析法和熵值法经典实操案例+数据

1、数据来源&#xff1a;无 2、时间跨度&#xff1a;无 3、区域范围&#xff1a;无 4、指标说明&#xff1a; 层次分析法&#xff08;Analytic Hierarchy Process&#xff0c;简称AHP&#xff09;是美国运筹学家、匹兹堡大学T. L. Saaty教授在20世纪70年代初期提出的&#…

《Buildozer打包实战指南》第二节 安装Kivy和Buildozer

目录 2.1 安装Kivy 2.2 安装Buildozer 2.3 验证安装 2.4 一点建议 Python是Ubuntu系统中自带的&#xff0c;我们在桌面上右键打开终端&#xff0c;然后输入python3 --version就可以看到Ubuntu系统中的Python版本了。 可以看到&#xff0c;Python的版本是3.10.6。虽然Python…

【Go基础】结构体

1. 结构体引入 Golang也支持面向对象编程&#xff0c;但是和传统的面向对象有区别&#xff0c;并不是像Java、C那样纯粹的面向对象语言&#xff0c;而是通过特别的手段实现面向对象特点。 Golang没有类(Class)的概念&#xff0c;但是提供了结构体(struct)&#xff0c;和其他编…

Nacos的学习

Nacos的学习 1、下载地址 https://github.com/alibaba/nacos/releases/tag/2.1.1 在bin目录中输入命令 startup.cmd -m standalone 输入localhost:8848/nacos 账号&#xff1a;nacos&#xff0c;密码&#xff1a;nacos 2、Spring与Nacos &#xff08;1&#xff09;新增一个配…

100天精通Python(数据分析篇)——第72天:Pandas文本数据处理方法之判断类型、去除空白字符、拆分和连接

文章目录每篇前言一、Python字符串内置方法1. 判断类型2. 去除空白字符3. 拆分和连接二、Pandas判断类型1. str.isspace()2. str.isalnum()3. str.isalpha()4. str.isdecimal()5. str.isdigit()6. str.isnumeric()7. str.istitle()8. str.islower()9. str.isupper()三、Pandas去…

音视频技术开发周刊 | 279

每周一期&#xff0c;纵览音视频技术领域的干货。新闻投稿&#xff1a;contributelivevideostack.com。基于NeRF的APP上架苹果商店&#xff01;照片转3D只需一部手机这个名叫Luma AI的“NeRF APP”&#xff0c;正式上架App Store后爆火。反 AiArt 运动中两件匪夷所思的蠢事Redd…

Elastic:使用 Postman 来访问

Elastic&#xff1a;使用 Postman 来访问 学习资料 Elastic&#xff1a;使用 Postman 来访问 Elastic Stack 当我们配置好elasticsearch的SSL之后&#xff0c;我们用网页https访问&#xff0c;输入账户及密码之后&#xff0c;可以成功访问数据。 但是用postman时&#xff0c;我…

2023/1/15 JS-闭包问题研究

1 举个栗子分析执行上下文 1: let a 3 2: function addTwo(x) { 3: let ret x 2 4: return ret 5: } 6: let b addTwo(a) 7: console.log(b)为了理解 JavaScript 引擎是如何工作的&#xff0c;让我们详细分析一下&#xff1a; 在第 1 行&#xff0c;我们在全局执行上…

Linux chattr命令

Linux chattr命令Linux 命令大全Linux chattr命令用于改变文件属性。这项指令可改变存放在ext2文件系统上的文件或目录属性&#xff0c;这些属性共有以下8种模式&#xff1a;a&#xff1a;让文件或目录仅供附加用途。b&#xff1a;不更新文件或目录的最后存取时间。c&#xff1…

从上到下看内存

从上到下看内存 1. 本篇目录 内存条,总线,DMAC 内存管理内存分类 内存相关的系统调用 java中的内存 2. 内存条,总线,DMAC 内存条 内存条&#xff1a;内存条其实是非常常见的一个组件。内存条是插在主板上的。 总线 内存条插好以后&#xff0c;计算机之间要进行交互。…

Linux 中断子系统(四):GIC中断初始化

以我手中的 imx6ull开发板为例。 如果使用设备树的话就需要在设备树中设置好中断属性信息,Linux 内核通过读取设备树中的中断属性信息来配置中断。对于中断控制器而言,设备树绑定信息参考文档 Documentation/devicetree/bindings/arm/gic.txt。 打开 imx6ull.dtsi 文件,其…

UDS诊断系列介绍12-11服务

本文框架1. 系列介绍1.1 11服务概述2. 11服务请求与应答2.1 11服务请求2.2 11服务正响应2.3 11服务否定响应3. Autosar系列文章快速链接1. 系列介绍 UDS&#xff08;Unified Diagnostic Services&#xff09;协议&#xff0c;即统一的诊断服务&#xff0c;是面向整车所有ECU的…