Spring IOC和AOP

作者: 雪飘千里 | 来源:发表于2020-03-16 19:04 被阅读0次

    1、IOC

    概念:所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。

    Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。

    image.png

    1.1 底层实现:

    image.png

    如果是xml文件,那么需要解析xml文件;如果是注解,需要通过反射获取注解,然后根据获取到的bean信息通过反射实例化bean,实例化之后将bean放到spring容器的bean缓存池中(hashMap),当要使用bean时,可以通过applicationContext获取bean(getBean)。

    1.2 spring ioc autowired如何实现

    @Autowired表示被修饰的类需要注入对象,spring会扫描所有被@Autowired标注的类,然后根据 类型type 在ioc容器中找到匹配的类注入

    1.3 @component

    普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>
    虽然有了@Autowired,但是我们还是要写一堆bean的配置文件,相当麻烦,而@Component就是告诉spring,我是pojo类,把我注册到容器中吧,spring会自动提取相关信息。那么我们就不用写麻烦的xml配置文件了。

    https://zhuanlan.zhihu.com/p/29344811

    1.4 bean生命周期

    image.png
    • 1.4.1.当调用者通过 getBean(beanName)向容器请求某一个 Bean 时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口,在实例化 Bean 之前,将调用接口的 postProcessBeforeInstantiation()方法;
    • 1.4.2.根据配置情况调用 Bean 构造函数或工厂方法实例化 Bean;
    • 1.4.3.如果容器注册了 InstantiationAwareBeanPostProcessor 接口,在实例化 Bean 之后,调用该接口的 postProcessAfterInstantiation()方法,可在这里对已经实例化的对象进行一些“梳妆打扮”;
    • 1.4.4.如果 Bean 配置了属性信息,容器在这一步着手将配置值设置到 Bean 对应的属性中,不过在设置每个属性之前将先调用InstantiationAwareBeanPostProcessor 接口的postProcessPropertyValues()方法;
    • 1.4.5.调用 Bean 的属性设置方法设置属性值;
    • 1.4.6.如果 Bean 实现了 org.springframework.beans.factory.BeanNameAware 接口,将调用setBeanName()接口方法,将配置文件中该 Bean 对应的名称设置到 Bean 中;
    • 1.4.7.如果 Bean 实现了 org.springframework.beans.factory.BeanFactoryAware 接口,将调用 setBeanFactory()接口方法,将 BeanFactory 容器实例设置到 Bean 中;
    • 1.4.8.如果 BeanFactory 装配了 org.springframework.beans.factory.config.BeanPostProcessor后处理器,将调用 BeanPostProcessor 的 Object postProcessBeforeInitialization(Object bean, String beanName)接口方法对 Bean 进行加工操作。其中入参 bean 是当前正在处理的 Bean,而 beanName 是当前 Bean 的配置名,返回的对象为加工处理后的 Bean。用户可以使用该方法对某些 Bean 进行特殊的处理,甚至改变 Bean 的行为, BeanPostProcessor 在 Spring 框架中占有重要的地位,为容器提供对 Bean 进行后续加工处理的切入点, Spring 容器所提供的各种“神奇功能”(如 AOP,动态代理等)都通过 BeanPostProcessor 实施;
    • 1.4.9.如果 Bean 实现了 InitializingBean 的接口,将调用接口的 afterPropertiesSet()方法;
    • 1.4.10.如果在<bean>通过 init-method 属性定义了初始化方法,将执行这个方法;
    • 1.4.11.BeanPostProcessor 后处理器定义了两个方法:其一是 postProcessBeforeInitialization() 在第 8 步调用;其二是 Object postProcessAfterInitialization(Object bean, String beanName)方法,这个方法在此时调用,容器再次获得对 Bean 进行加工处理的机会;
    • 1.4.12.如果在<bean>中指定 Bean 的作用范围为 scope=“prototype”,将 Bean 返回给调用者,调用者负责 Bean 后续生命的管理, Spring 不再管理这个 Bean 的生命周期。如果作用范围设置为 scope=“singleton”,则将 Bean 放入到 Spring IoC 容器的缓存池中,并将 Bean引用返回给调用者, Spring 继续对这些 Bean 进行后续的生命管理;
    • 1.4.13.对于 scope=“singleton”的 Bean,当容器关闭时,将触发 Spring 对 Bean 的后续生命周期的管理工作,首先如果 Bean 实现了 DisposableBean 接口,则将调用接口的afterPropertiesSet()方法,可以在此编写释放资源、记录日志等操作;
    • 1.4.14.对于 scope=“singleton”的 Bean,如果通过<bean>的 destroy-method 属性指定了 Bean 的销毁方法, Spring 将执行 Bean 的这个方法,完成 Bean 资源的释放等操作。

    可以将这些方法大致划分为三类:

    • Bean 自身的方法:如调用 Bean 构造函数实例化 Bean,调用 Setter 设置 Bean 的属性值以及通过<bean>的 init-method 和 destroy-method 所指定的方法;

    • Bean 级生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,这些接口方法由 Bean 类直接实现;

    • 容器级生命周期接口方法:在上图中带“★” 的步骤是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“ 后处理器” 。 后处理器接口一般不由 Bean 本身实现,它们独立于 Bean,实现类以容器附加装置的形式注册到 Spring 容器中并通过接口反射为 Spring 容器预先识别。当Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用,所以这些后处理器的影响是全局性的。当然,用户可以通过合理地编写后处理器,让其仅对感兴趣Bean 进行加工处理。

    ApplicationContext 和 BeanFactory 的不同之处在于:ApplicationContext会利用 Java 反射机制自动识别出配置文件中定义的 BeanPostProcessor、 InstantiationAwareBeanPostProcessor 和 BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要在代码中通过手工调用 addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时,我们普遍使用 ApplicationContext 而很少使用 BeanFactory 的原因之

    2、AOP

    2.1 概念

    AOP的全称是Aspect Orient Programming,即面向切面编程,扩展功能不通过修改源代码实现。

    2.2实现方式

    • 2.2.1 JDK 动态代理(必须有接口)
      通过java.lang.reflect.Proxy类实现。
      动态代理就是为了解决静态代理不灵活的缺陷而产生的。静态代理是固定的,一旦确定了代码,如果委托类新增一个方法,而这个方法又需要增强,那么就必须在代理类里重写一个带增强的方法。而动态代理可以灵活替换代理方法,动态就是体现在这里。同时,静态代理每个方法都需要单独写一个代理类,而动态代理一个接口只实现一个动态代理类即可。

    • 2.2.2 实现

    Moveable move = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new LogHandler(new Car()));
    

    使用JDK的动态代理去生成代理只需要一行代码,传入的参数中其实就俩,一是被代理类的类对象,二是自定义的增强处理代码。
    从上面的例子中可以看出,动态代理除了接受Car类型的目标对象,还可以接受任何其他类型的对象;也不管目标对象实现的接口有多少方法,都可以被代理。

    
    public class LogHandler implements InvocationHandler{ 
     
     private Object target; 
     
     public LogHandler(Object object){
       super();
       this.target = object;
     }
     
     //增强处理
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
       Object o = method.invoke(target,args);
       return o;
     }
    }
    
    
    image.png
    • 2.2.2 cglib 动态代理(不需要类继承任何接口,字节码技术)
    public class Plane {
    
        public void fly(long ms) {
            System.out.println("plane is flying!");
            try {
                Thread.sleep(ms);
            } catch (Exception e) {
    
            }
        }
    }
    
    public class CglibProxy implements MethodInterceptor {
        private Object target;
    
        public CglibProxy(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance() {
            //1. 实例化工具类
            Enhancer en = new Enhancer();
            //2. 设置父类对象
            en.setSuperclass(this.target.getClass());
            //3. 设置回调函数
            en.setCallback(this);
            //4. 创建子类,也就是代理对象
            return en.create();
        }
    
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before invoke ");
            long begin = System.currentTimeMillis();
    
            //执行目标对象的方法
            Object returnValue = method.invoke(target, objects);
    
            long end = System.currentTimeMillis();
            System.out.println("after invoke elpased " + (end - begin));
    
            return returnValue;
        }
    
    }
    
    public static void main() {
        CglibProxy cglibProxy = new CglibProxy(new Plane()); 
        Plane plane = (Plane) cglibProxy.getProxyInstance();             
        plane.fly(150);
    }
    
    

    2.4使用场景:

      1. Logging 日志
      1. Authentication 权限
      1. Transactions 事务
      1. Context passing 内容传递
      1. Error handling 错误处理
      1. Lazy loading 懒加载
      1. Debugging 调试
      1. logging,tracing,profiling and monitoring 记录跟踪 优化 校准
      1. Performance optimization 性能优化
      1. Persistence 持久化
      1. Resource pooling 资源池
      1. Synchronization 同步
      1. Caching 缓存

    2.5 在Spring的AOP编程中

    如果加入容器的目标对象有实现接口,就使用JDK代理
    如果目标对象没有实现接口,就使用Cglib代理。

    3、反射:

    反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的Java程序获取类的信息,并且可以操作类或对象内部属性

    程序中对象的类型一般都是在编译期就确定下来的,而当我们的程序在运行时,可能需要动态的加载一些类,这些类因为之前用不到,所以没有加载到jvm,这时,使用Java反射机制可以在运行期动态的创建对象并调用其属性,它是在运行时根据需要才加载。

    3.1 new和反射创建有什么区别

    new:静态编译,在编译期就将模块编译进来,执行该字节码文件,所有的模块都被加载;
    反射:动态编译,编译期没有加载,等到模块被调用时才加载;

    3.2 反射的作用

    • 在运行时判断任意一个对象所属的类;
    • 在运行时构造任意一个类的对象;
    • 在运行时判断任意一个类所具有的成员变量和方法;
    • 在运行时调用任意一个对象的方法;

    3.3 反射的实现

    要使用一个类,就要先把它加载到虚拟机中,生成一个Class对象。这个class对象就保存了这个类的一切信息。

    反射机制的实现,就是获取这个Class对象,通过Class对象去访问类、对象的元数据以及运行时的数据。

    //通过反射机制创建class对象
    class1 = Class.forName(className);
    //在运行时,通过创建的class对象,获取自己的父类信息
    Class<?> parentClass = class1.getSuperclass();
    //通过反射机制创建一个类的对象
    Classname 对象=class1.newInstance(参数);
    //取得本类已声明的所有字段,包括私有的、保护的
    Field[] field = class1.getDeclaredFields();
    //返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
    Method method = clazz.getMethod(方法名,参数类型);
    //调用具体某个实例对象的这个公有方法
    method.invoke(实例对象,参数值);
    

    相关文章

      网友评论

        本文标题:Spring IOC和AOP

        本文链接:https://www.haomeiwen.com/subject/bpnxgctx.html