美文网首页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