美文网首页Java技术区
ApplicationContext与Spring AOP

ApplicationContext与Spring AOP

作者: 拼搏男孩 | 来源:发表于2020-02-26 23:10 被阅读0次

    一、ApplicationContext

    ApplicationContext被称为Spring上下文,Application Context 是 Spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。

    1、BeanFactory容器

    这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactor 中被定义。BeanFactory 和相关的接口,比如BeanFactoryAware、DisposableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext。

    这个接口是Spring中最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和取对象的功能。

    2、ApplicationContext与BeanFactory的关系

    2.1 联系

    在IDEA中可以查看两者的继承关系

    image.png

    从上图可以看出,AllicationContext这个接口继承了ListableBeanFactory与HierarchicalBeanFactory这两个接口,而这两个接口都继承了BeanFactory这个接口。所以它们都可以用来配置XML属性,也支持属性的自动注入。

    2.2 区别

    1)功能

    BeanFactory是Spring中最底层的容器,提供的功能较为简单,ApplicationContext继承了两个接口,相对而言可以提供更多功能:

    • 国际化(i18n)(MessageSource)

    • 访问资源,如URL和文件(ResourceLoader)

    • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

    • 消息发送、响应机制(ApplicationEventPublisher)

    • AOP(拦截器)

    2)装载Bean

    BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;

    • 延迟实例化的优点:(BeanFactory

    应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势;

    • 不延迟实例化的优点: (ApplicationContext

      • 所有的Bean在启动的时候都加载,系统运行的速度快;
    • 在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题

      • 建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成)

    3 、重要的实现类

    image.png

    ApplicationContext的实现类如上图所示,重要的实现类有两个:ClassPathXmlApplicationContext与FileSystemXmlApplicationContext

    • ClassPathXmlApplicationContext:从classpath(resources目录等同于classpath)中读取xml文件加载已经被定义的bean。(注意,使用这个类的对象读取xml文件后需要手动调用close()(继承自抽象类AbstractApplicationContext)方法,否则会引发警告)
    • FileSystemXmlApplicationContext:从系统文件中读取xml文件加载已经被定义的bean。

    二、Spring AOP

    AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。

    在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。与AOP有关的两个概念是OOP(面向对象编程)与POP(面向过程编程)。AOP的核心技术是代理。在介绍AOP之前有必要了解一下Java的动态代理。

    1、动态代理

    在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理对象,动态代理可以让我们在不修改源码的情况下增加新的功能。Java的动态代理有两种方式

    1.1 使用java.lang.reflect包

    这种方式要求被代理的类必须实现某个接口,实现原理是利用对象的类的字节码接口,写出一个新的类到本地区,通过编译器直接编译成.class文件,再通过类加载器加载进来。最重要的一步是:

    MyInterface p =  (MyInterface) Proxy.newProxyInstance(Student.class.getClassLoader(), Student.class.getInterfaces(), new InvocationHandler() {
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable       {
                System.out.println("你好");
                method.invoke(student);
                return null;
            }
        });
    

    其中,Student类实现了MyInterface这个接口,student是被代理的对象。

    1.2 使用cglib

    这是非Java原生的动态代理,效率更高,限制更小,而且不需要被代理的类实现接口。

    public static void main(String[] args) {
        //导入包   cglib-core  asm     ant     ant-launcher
        //创建运行器
        MethodInterceptor mi = new MethodInterceptor() {
            
            @Override
            public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
                System.out.println("运行前");
                arg3.invokeSuper(arg0, arg2);
                System.out.println("运行后");
                return null;
            }
        };
        //获取代理类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(Demo.class);
        //运行任务
        enhancer.setCallback(mi);
        //创建代理对象
        Demo d = (Demo)enhancer.create();
        d.method();
    }
    

    cglib实现动态代理的原理是代理类去继承目标类,然后重写其中目标类的方法,这样每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才调用目标类的该方法。

    2、第一种实现方式

    第一种实现方式就是使用Java原生的动态代理,需要四个类一个接口:

    Person接口,这个接口是目标类的实现接口

    package com.qianfeng.aop01;
    
    public interface Person {
        void eat();
        void drink();
    }
    
    

    Student类,这个类是目标类

    package com.qianfeng.aop01;
    
    public class Student implements Person {
        @Override
        public void eat() {
            System.out.println("I can eat");
        }
    
        @Override
        public void drink() {
            System.out.println("I can run");
        }
    }
    
    

    MyAspect类,我们想要进行切面的类,可以扩展我们想要增加的功能

    package com.qianfeng.aop01;
    
    public class MyAspect {
        public void before(){
            System.out.println("---------before----------");
        }
        public void after(){
            System.out.println("---------after----------");
        }
    }
    
    

    AOP01Test类,测试类

    package com.qainfeng.aop01;
    
    import com.qianfeng.aop01.Person;
    import com.qianfeng.aop01.PersonFactory;
    import org.junit.Test;
    
    public class AOP01Test {
        @Test
        public void testStudent(){
            Person p = PersonFactory.getPerson();
            p.eat();
            p.drink();
        }
    }
    
    

    PersonFactory类,工厂类,用于生成Person对象

    package com.qianfeng.aop01;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class PersonFactory {
        public static Person getPerson(){
            //创建被代理对象(目标对象)
            Person p = new Student();
            //创建自定义切面类对象
            MyAspect ma = new MyAspect();
    
            //创建代理对象
            Person p1 = (Person) Proxy.newProxyInstance(PersonFactory.class.getClassLoader(), p.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    ma.before();
                    Object obj = method.invoke(p,args);
                    ma.after();
                    return obj;
                }
            });
            //返回代理对象
            return p1;
        }
    }
    
    

    需要重点说明的是PersonFactory类中的Proxy.newProxyInstance()方法,这个方法用来实现对目标对象的代理,这个方法需要三个参数,第一个参数是目标类的加载器、第二个参数是目标类的所有接口,第三个参数是一个实现了InvocationHandler接口的类的对象,首先,我们查看InvocationHandler的注释:

    /**
     * {@code InvocationHandler} is the interface implemented by
     * the <i>invocation handler</i> of a proxy instance.
     *
     * <p>Each proxy instance has an associated invocation handler.
     * When a method is invoked on a proxy instance, the method
     * invocation is encoded and dispatched to the {@code invoke}
     * method of its invocation handler.
     */
    

    InvocationHanler是被一个代理实例的调用处理程序实现的接口,每一个代理实例都有一个关联的调用处理程序,当一个代理实例的方法被调用的时候,方法调用被编码分派到它的调用处理程序的invoke方法。通过阅读注释我们知道每次调用代理实例的方法时都会调用invoke方法。再来看一下invoke方法的注释:

    /**
         * Processes a method invocation on a proxy instance and returns
         * the result.  This method will be invoked on an invocation handler
         * when a method is invoked on a proxy instance that it is
         * associated with.
         *
         * @param   proxy the proxy instance that the method was invoked on
         *
         * @param   method the {@code Method} instance corresponding to
         * the interface method invoked on the proxy instance.  The declaring
         * class of the {@code Method} object will be the interface that
         * the method was declared in, which may be a superinterface of the
         * proxy interface that the proxy class inherits the method through.
         *
         * @param   args an array of objects containing the values of the
         * arguments passed in the method invocation on the proxy instance,
         * or {@code null} if interface method takes no arguments.
         * Arguments of primitive types are wrapped in instances of the
         * appropriate primitive wrapper class, such as
         * {@code java.lang.Integer} or {@code java.lang.Boolean}.
         */
    

    这个方法处理一个代理对象的方法调用然后返回结果。参数proxy是方法被调用的代理实例。参数method是与接口实例中被调用的接口方法一直的方法实例,参数args是传过来的参数,是一个对象数组。最后再来看一下Proxy类中的newProxyInstance方法:

    /**
         * Returns an instance of a proxy class for the specified interfaces
         * that dispatches method invocations to the specified invocation
         * handler.
         *
         * <p>{@code Proxy.newProxyInstance} throws
         * {@code IllegalArgumentException} for the same reasons that
         * {@code Proxy.getProxyClass} does.
         *
         * @param   loader the class loader to define the proxy class
         * @param   interfaces the list of interfaces for the proxy class
         *          to implement
         * @param   h the invocation handler to dispatch method invocations to
         * @return  a proxy instance with the specified invocation handler of a
         *          proxy class that is defined by the specified class loader
         *          and that implements the specified interfaces
         * @throws  IllegalArgumentException if any of the restrictions on the
         *          parameters that may be passed to {@code getProxyClass}
         *          are violated
         */
    

    这个方法返回指定接口的代理类的实例。参数loader是定义代理类的类加载器,参数interfaces是代理类将要实现的接口集合。参数h是一个调用处理程序。

    3、第二种实现方式

    第二种实现方式使用了cglib,由于不需要实现接口,这里只需要四个类:Student、AOP02Test、MyAspect、StudentFactory,其中前三个类与第一种实现方式基本一致,只不过不需要实现接口,这里详细解释一下第四个类

    package com.qianfeng.aop02;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    public class StudentFactory {
        public static Student getStudent(){
            Enhancer en = new Enhancer();
            //设置父类
            en.setSuperclass(Student.class);
            Student student = new Student();
            MyAspect ma = new MyAspect();
            //设置回调方法
            en.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    ma.before();
                    Object obj = method.invoke(student,objects);
                    ma.after();
                    return obj;
                }
            });
            //将动态生成的代理类的对象强转为目标类的对象
            Student student1 = (Student) en.create();
            return student1;
        }
    }
    
    

    Enhancer类是一个字节码增强器,用来动态生成代理类的对象,这里看一下注释:

    /**
     * Generates dynamic subclasses to enable method interception. This
     * class started as a substitute for the standard Dynamic Proxy support
     * included with JDK 1.3, but one that allowed the proxies to extend a
     * concrete base class, in addition to implementing interfaces. The dynamically
     * generated subclasses override the non-final methods of the superclass and
     * have hooks which callback to user-defined interceptor
     * implementations.
     */
    

    生成动态的子类用来确保方法拦截,这个类作为一种标准动态代理支持的替代,但是允许代理去扩展一个具体的基类,并且实现接口。动态生成的子类重写了父类的非final方法。

    MethodInterceptor是一个接口,实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。intercept方法和第一种实现方式中的invoke方法有点类似,不同的是多了一个参数MethodProxy,这个参数是对当前被调用方法的代理。

    4、第三种实现方式

    第三种实现方式基于xml文件,使用了昨天学过的IoC,需要四个文件:Student.java、MyAspect.java、Person.java(这个是接口)、AOC03Test.java与beans.xml,最重要的是beans.xml和MyAspect类,这个类需要实现一个接口:

    package com.qianfeng.aop03;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyAspect implements MethodInterceptor {
        public void before(){
            System.out.println("---------before----------");
        }
        public void after(){
            System.out.println("---------after----------");
        }
        //重写invoke方法,注意实现类
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            before();
            Object obj = invocation.proceed();
            after();
            return obj;
        }
    }
    
    

    AOC03Test.java

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class AOP03Test {
        @Test
        public void testStudent(){
            ApplicationContext ac = new ClassPathXmlApplicationContext("beans2.xml");
            Student student = ac.getBean("proxy",Student.class);
            student.eat();
            student.drink();
        }
    }
    

    beans.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">
        <bean id="st" class="com.qianfeng.aop03.Student" />
        <bean id="my" class="com.qianfeng.aop03.MyAspect" />
        <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="com.qianfeng.aop03.Person" />
            <property name="target" ref="st" />
            <property name="interceptorNames" value="my" />
            <property name="optimize" value="true" />
        </bean>
    </beans>
    

    ProxyFactoryBean代理的FactoryBean对象,我们现在要代理的是st包含四个属性注入:

    1. interfaces: 接口对象们
      <list>
      <value>com.qfedu.aop03.IUserService</value>
      <value>com.qfedu.aop03.IUserService</value>
      <value>com.qfedu.aop03.IUserService</value>
      </list>
    2. target:目标对象,哪个对象将被以代理的方式创建
    3. interceptorNames:拦截对象的名称,自定义的MethodInterceptor对象,注意它的包结构组成,和第二种方法中的不一样。注意使用的是value因为这里要的是名称而不是对象,所以不使用ref。
    4. optimize:boolean类型的值:
      true:强制使用cglib的动态代理方式
      false:使用jdk自带的动态代理

    cglib:code generation library,代码生成库,性能更高

    相关文章

      网友评论

        本文标题:ApplicationContext与Spring AOP

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