<meta charset="utf-8">
一、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中可以查看两者的继承关系
从上图可以看出,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 、重要的实现类
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包含四个属性注入:
- interfaces: 接口对象们
<list>
<value>com.qfedu.aop03.IUserService</value>
<value>com.qfedu.aop03.IUserService</value>
<value>com.qfedu.aop03.IUserService</value>
</list> - target:目标对象,哪个对象将被以代理的方式创建
- interceptorNames:拦截对象的名称,自定义的MethodInterceptor对象,注意它的包结构组成,和第二种方法中的不一样。注意使用的是value因为这里要的是名称而不是对象,所以不使用ref。
- optimize:boolean类型的值:
true:强制使用cglib的动态代理方式
false:使用jdk自带的动态代理
cglib:code generation library,代码生成库,性能更高