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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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 事务

在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: SpringJava