Spring核心概念
Spring 的两大特性就是 IoC 和 AOP。
1. 什么是 IoC
IoC叫控制反转。在谈IoC之前,要了解什么是容器,因为Spring又叫IoC容器。容器是一个软件环境,它为某种特定组件的运行提供环境支持。例如,Tomcat就是一个Servlet容器,它可以为Servlet的运行提供运行环境。Docker也是一个容器,它提供了必要的Linux环境以便运行一个特定的Linux进程。
通常的Java程序,在需要一个类的时候,都是自己主动去创建,也就是说,控制权在程序本身,一个组件在哪里需要就在哪里创建。这种创建组件方式的问题是,在一个大型系统中,各种组件随意创建,相互依赖,会让对象管理的复杂度变高,也极大提升了组件之间的耦合度,不利于程序的维护和测试。
在IoC模式下,控制权发生反转,组件不由程序自己创建,而是在IoC容器中创建。程序依赖某个组件,从IoC容器中拿来即用。而这个“拿来”的过程,又是一个组件注入的过程,因此IoC又叫做依赖注入(DI)。
IoC的好处:将组件的创建、配置与组件的使用解耦,且由IoC容器来管理组件的生命周期。
2. 什么是 AOP
AOP是即面向切面编程。AOP本质上就是一个代理模式,使得调用方能无感知地调用指定方法,但运行期却动态“织入”了其他逻辑。
Spring AOP的实现有两种方式:JDK动态代理和CGLIB代理:
- 对实现接口的类使用JDK动态代理,
- 对普通类使用CGLIB创建子类来实现代理。
Spring AOP选择代理方式的代码位置:DefaultAopProxyFactory
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
...
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
...
}
2.1 AOP 问题
CGLIB代理因为是通过创建被代理类的子类来实现扩展,所以它又称子类代理。因此使用CGLIB代理要注意的问题是:
- 被代理类不能申明为final
- 被代理类中的方法如果为static或final,不会被代理
另外,Spring通过CGLIB创建的代理类,不会初始化代理类自身继承的任何成员变量,包括final类型的成员变量!因此,正确使用AOP,我们需要一个避坑指南:
- 访问被注入的Bean时,总是调用方法而非直接访问字段;
- 编写Bean时,如果可能会被代理,就不要编写
public final
方法。 - 如果不想Bean被代理,就申明为final类,也不实现接口。