本文主要内容包含 2.0 IoC实现 ,AOP实现 , aspectj原理与应用。
简介
2.0.0 版本发布时间为2006-10-03 ,JDK 为 1.5,同时期阿里将EJB 替换为Spring 。2.0 新增两个重要特性JavaBeans-based configuration,集成aspects。
IoC 部分
主要分析功能点:
- 新增bean 的自动装配,包含BY_NAME,BY_TYPE,CONSTRUCTOR
- 新增bean scope 实现。
- context 功能增强。
- 新增扩展接口
IoC 容器核心类图
0.9 实现 对比2.0 如下图,
![](https://img.haomeiwen.com/i1228444/ee1cfc0c850f747a.png)
![](https://img.haomeiwen.com/i1228444/f2935cf78ec923a0.png)
2.0主要类职责:
1、XMLBeanFactory : 负责读取配置文件以及注册bean配置信息。 这部分功能的实现委派给了 new XmlBeanDefinitionReader(BeanDefinitionRegistry),继承父类功能(强耦合),获取bean,bean 信息查询
2、BeanDefinitionRegistry: 负责bean 定义的注册,该功能从0.9 的ListableBeanFactory 独立出来,功能拆分。让类的职责更加单一。单一职责需要依据具体的业务场景与功能来看,没有统一的标准。
3、DefaultListableBeanFactory : 依赖BeanDefinitionRegistry 实现 查询bean 注册信息功能,复用父类功能。
4、bean 创建与连接的配置信息类
1、HierarchicalBeanFactory :继承beanFactory,比如一个文件文件对应一个BeanFactory,将所有配置文件merge到一起
2、SingletonBeanRegistry :单例配置
3、ConfigurableBeanFactory :添加的BeanPostProcessor 前后值处理,bean 的属性处理,bean 的作用范围,bean 销毁,与一个bean 的声明周期相关
5、AutowireCapableBeanFactory :bean 依据配置的自动装配类型进行装陪
Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
6、核心实现类 AbstractBeanFactory:bean 的生成,AbstractAutowireCapableBeanFactory: bean 自动依赖注入。
Scope 实现
1、单例:缓存创建对象,一次createBean
if (mergedBeanDefinition.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return createBean(beanName, mergedBeanDefinition, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition);
}
2、原型:每次getBean ,每次createBean。实现自动注入后,要实现原型。可以用代理模式,实现每次方法调用绑定的对象为BeanFactory.getBean() 详见 ScopedObject 对象的实现
if (mergedBeanDefinition.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mergedBeanDefinition, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, mergedBeanDefinition);
}
原型代理对象生成:
public Object getTargetObject() {
return this.beanFactory.getBean(this.targetBeanName);
}
3、自定义 :自定义 createBean 次数和如何创建 bean。
final Scope scope = (Scope) this.scopes.get(scopeName);
Object scopedInstance = scope.get(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
Object bean = createBean(beanName, mergedBeanDefinition, args);
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(scopedInstance, name, mergedBeanDefinition);
beans 组件的新增扩展点
1、xml 命名空间支持与自定义解析增强相关接口:
NamespaceHandler、NamespaceHandlerResolver
META-INF/spring.schemas
http\://www.springframework.org/schema/tx/spring-tx-2.0.xsd=org/springframework/transaction/config/spring-tx-2.0.xsd
META-INF/spring.handlers
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
2、bean 实例化的前后置处理接口BeanPostProcessor,postProcessBeforeInitialization,postProcessAfterInitialization
3、生成bean 对象后,执行bean 初始化操AbstractAutowireCapableBeanFactory#initializeBean作,BeanNameAware,BeanClassLoaderAware,BeanPostProcessor
BeanNameAware.setBeanName
BeanClassLoaderAware.setBeanClassLoader
BeanFactoryAware.setBeanFactory
applyBeanPostProcessorsBeforeInitialization (BeanPostProcessor 接口)
invokeInitMethods
applyBeanPostProcessorsAfterInitialization (BeanPostProcessor 接口)
Context
核心类图
![](https://img.haomeiwen.com/i1228444/64ea08c1f8317514.png)
类职责:
ClassPathXmlApplicationContext :定义xml 文件位置
AbstractXmlApplicationContext :读取xml 文件,解析注册
AbstractRefreshableApplicationContext #refreshBeanFactory:创建beanfactory ,调用AbstractXmlApplicationContext 进行bean 注册
AbstractApplicationContext :bean 流程管理,并且与context 集成
客户端使用:
New ClassPathXmlApplicationContext(file) 执行流程
this.configLocations = configLocations;
refresh();
主要流程还是在refresh 方法中,
1、创建BeanFactory,refreshBeanFactory,getBeanFactory
2、设置BeanFactory配置
addPropertyEditorRegistrar
addBeanPostProcessor(ApplicationContextAwareProcessor) ,与context 接口集成
ignoreDependencyInterface
postProcessBeanFactory(beanFactory)对BeanFactory 进行自定义设置
3、BeanFactoryPostProcessor,对bean 注册完成后进行修改的扩展点。比如属性文件读取的 PropertyPlaceholderConfigurer 就是一个BeanFactoryPostProcessor。解析bean 定义后,读取项目属性文件。
//扩展 ,context add BeanFactoryPostProcessor
BeanFactoryPostProcessor.postProcessBeanFactory (beanFactory)
//调用所有 bean 实现了 BeanFactoryPostProcessor 接口,来自于BeanDefine
invokeBeanFactoryPostProcessors :
4、registerBeanPostProcessors,将 所有实现 BeanPostProcessor 接口的bean 注册到 BeanFactory 中,供实例化bean 时候调用
5、其他步骤
initMessageSource :国际化资源文件
initApplicationEventMulticaster
onRefresh 扩展
registerListeners
beanFactory.preInstantiateSingletons(); 实例化所有单例bean
publishEvent(new ContextRefreshedEvent(this));
小结:
IoC 部分整体框架与流程与0.9 变化不大,添加了很多新的扩展点供客户端使用。对项目中使用的类进行了更细粒度的拆分,更高维度的抽象,比如 Resource 来定义资源。0.9 版本中只有File。2.0 版本 实现了 更多的扩展,更细的拆分,更高的抽象。框架随着业务进行演进。
AOP 部分
简介:
AOP 实现的目标就是讲两部分的代码集成到一起来执行。比如业务代码bussinessMethod+通用代码(切面代码)commonMethod 。为了到达 执行bussinessMethod 的时候,也将 commonMethod 执行了。并且需要对客户端是透明的。例如:
call bussinessMethod() :
bussinessMethod();commonMethod() 或者commonMethod() ;bussinessMethod()
Spring 的主要抽象:
Advice :代表 commonMethod(切面代码块)。有before ,after ,表示添加到bussinessMethod的什么地方。
Advisor :对Advice的一次封装
TargetSource:对代理目标的描述
MethodMatcher :方法匹配 ,bussinessMethod 与commonMethod 是否匹配
Interceptor :是一个特殊的Advice。
Pointcut :切点,将bussinessMethod 与commonMethod 连接到一起。
Aspectj 的抽象:
Advice : before ,after,afterreturning ,xxxx , 将业务代码与通用代码关联。切面代码以及切点在通用代码什么位置。与spring 一致
Join point: 业务代码method 定义,commonMethod
Pointcut : Advice(commonMethod)+Join point (bussinessMethod)连接关系, 与spring MethodMatcher 类似
Aspect : 一系列 Pointcut 的组合。
Spring AOP 实现
Spring 依据JdkDynamicAopProxy,Cglib2AopProxy 来实现AOP 功能,并且适配Aspectj 的注解配置形式,将Aspectj 的配置解析到Spring 的抽象上面来。配置的注解和接触注解依赖Aspectj 内置提供的功能。
核心类图:
![](https://img.haomeiwen.com/i1228444/8f194f2c11a70af0.png)
抽象工厂模式:
ProxyConfig —>AopProxyFactory#createAopProxy —>AopProxy(Fatory)#getObject()
AopProxyFactory :依据ProxyConfig 配置是接口代理还是类代理,接口代理使用创建JdkDynamicAopProxy ,类代理创建Cglib2AopProxy。
AopProxy :依据配置的advice ,TargetSource 创建代理对象。
客户端使用
ProxyFactory pf = new ProxyFactory();
pf.getProxy()
实现:
public static Object getProxy(Class proxyInterface, Interceptor interceptor) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addInterface(proxyInterface);
proxyFactory.addAdvice(interceptor);
return proxyFactory.getProxy();
}
手动配置创建Spring 容器的代理,ProxyFactoryBean,Interceptor 来源于xml 配置文件
![](https://img.haomeiwen.com/i1228444/4185230a7b4dc35c.png)
public Object getObject() throws BeansException {
// initializeAdvisorChain();
addAdvisorOnChainCreation(advice, this.interceptorNames[i]);
//getSingletonInstance 使用 ProxyFactory 创建代理对
this.singletonInstance = getProxy(createAopProxy());
}
自动创建代理对象,使用aspectj ,将aspectj 配置的切面适配到spring 的内容上面来,AnnotationAwareAspectJAutoProxyCreator 是一个BeanPostProcessor,Spring 实例化后,会进行2次修改创建的对象。查询系统中所有配置的@aspect 注解的类,对创建的对象生成代理对象。
![](https://img.haomeiwen.com/i1228444/8a3d3154e47473df.png)
AdvisorFactory :Advisor 生产工厂,将aspectj 的advice 转换成spring 的advice。ReflectiveAspectJAdvisorFactory 将解析 aspectj 定义的注解 before ,after 等
![](https://img.haomeiwen.com/i1228444/3e0bd250fff5ea63.png)
ReflectiveAspectJAdvisorFactory 部分代码
![](https://img.haomeiwen.com/i1228444/f902a28942302498.png)
小结:
由于添加了支持aspectj 语法的支持,做了大量工作将aspectj 与spring 之间进行适配。创建代理对象的核心是找到定义的advice,ProxyFactory 依据advice 创建代理对象。
Aspectj 部分
简介
Aspectj 是一个对java 语言就行切面编程的框架,依赖对java 自己码的修改,来实现业务代码与切面代码的连接。项目由以下jar 包组成:
aspectjrt - the AspectJ runtime
aspectjweaver - the AspectJ weaver ,包含了aspectjrt ,java agent 等
aspectjtools - the AspectJ compiler
Aspectj 编写切面的方式
1、使用.aj 源文件,这种语法需要用ajc 编译成字节码。无论是依赖那种植入方式,都需要先使用ajc 编译。
public aspect AccountAspect {
final int MIN_BALANCE = 10;
pointcut callWithDraw(int amount, Account acc) :
call(boolean Account.withdraw(int)) && args(amount) && target(acc);
before(int amount, Account acc) : callWithDraw(amount, acc) {
}
boolean around(int amount, Account acc) :
callWithDraw(amount, acc) {
System.out.println("aspect ..");
if (acc.balance < amount) {
return false;
}
return proceed(amount, acc);
}
after(int amount, Account balance) : callWithDraw(amount, balance) {
}
}
2、使用注解方式。这种可以使用javac 编译,也可以使用ajc 编译。
@Aspect
public class SecuredMethodAspect {
@Pointcut("@annotation(secured)")
public void callAt(Secured secured) {
}
@Around("callAt(secured)")
public Object around(ProceedingJoinPoint pjp,
Secured secured) throws Throwable {
return secured.isLocked() ? null : pjp.proceed();
}
}
AspectJ 对java 字节码修改时机
1、编译时期植入:使用ajc 将 java 和aj 文件编译成字节码。
2、编译后植入 : 使用ajc ,依据aspect 配置重新编译已经存在的字节码。效果和编译时植入一样
3、加载时植入(LTW) :类加载的时候进行植入,依赖java agent 实现。
编译时植入测试
AccountAspect.aj 文件
public aspect AccountAspect {
final int MIN_BALANCE = 10;
pointcut callWithDraw(int amount, Account acc) :
call(boolean Account.withdraw(int)) && args(amount) && target(acc);
before(int amount, Account acc) : callWithDraw(amount, acc) {
}
boolean around(int amount, Account acc) :
callWithDraw(amount, acc) {
System.out.println("aspect ..");
if (acc.balance < amount) {
return false;
}
return proceed(amount, acc);
}
after(int amount, Account balance) : callWithDraw(amount, balance) {
}
}
ajc 编译后 使用IDEA 反编译(有问题的)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.github.yulechen.aspectj.model;
import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.runtime.internal.AroundClosure;
@Aspect
public class AccountAspect {
final int MIN_BALANCE = 10;
static {
try {
ajc$postClinit();
} catch (Throwable var1) {
ajc$initFailureCause = var1;
}
}
public AccountAspect() {
}
@Before(
value = "callWithDraw(amount, acc)",
argNames = "amount,acc"
)
public void ajc$before$com_github_yulechen_aspectj_model_AccountAspect$1$14220714(int amount, Account acc) {
}
@Around(
value = "callWithDraw(amount, acc)",
argNames = "amount,acc,ajc$aroundClosure"
)
public boolean ajc$around$com_github_yulechen_aspectj_model_AccountAspect$2$14220714(int amount, Account acc, AroundClosure ajc$aroundClosure) {
System.out.println("aspect ..");
return ajc$inlineAccessFieldGet$com_github_yulechen_aspectj_model_AccountAspect$com_github_yulechen_aspectj_model_Account$balance(acc) < amount ? false : ajc$around$com_github_yulechen_aspectj_model_AccountAspect$2$14220714proceed(amount, acc, ajc$aroundClosure);
}
@After(
value = "callWithDraw(amount, balance)",
argNames = "amount,balance"
)
public void ajc$after$com_github_yulechen_aspectj_model_AccountAspect$3$a7995619(int amount, Account balance) {
}
public static AccountAspect aspectOf() {
if (ajc$perSingletonInstance == null) {
throw new NoAspectBoundException("com_github_yulechen_aspectj_model_AccountAspect", ajc$initFailureCause);
} else {
return ajc$perSingletonInstance;
}
}
public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}
}
调用的地方的字节码被修改了:
![](https://img.haomeiwen.com/i1228444/d57f2dc8f39e15f9.png)
编译时期植入:
1、编译时期,需要使用aspectjtools 进行编译。
2、运行,需要依赖 aspectjrt。
加载时植入测试:
1、配置jvm 启动参数
-javaagent:path/aspectjweaver.jar
2、配置classpath 下面 META-INF/aop.xml 文件,aspectjweaver 会依据默认的路径读取aop.xml 文件来判断加载类的时候是否进行植入。
3、如果使用aj 源文件,则还需要使用ajc 进行编译后才能使用,如果使用注解配置方式,则只需要使用javac 编译就可以。
4、自己码是在加载的时候进行修改。不是在磁盘上面,类似于groovy 的classloader 。
IDEA反编译坑:
AnnotationBeanConfigurerAspect.class idea 反编译后
@Aspect
public class AnnotationBeanConfigurerAspect extends AbstractBeanConfigurerAspect {
static {
try {
ajc$postClinit();
} catch (Throwable var1) {
ajc$initFailureCause = var1;
}
}
public AnnotationBeanConfigurerAspect() {
this.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
}
public static AnnotationBeanConfigurerAspect aspectOf() {
if (ajc$perSingletonInstance == null) {
throw new NoAspectBoundException("org_springframework_beans_factory_aspectj_AnnotationBeanConfigurerAspect", ajc$initFailureCause);
} else {
return ajc$perSingletonInstance;
}
}
public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}
}
show 字节码,字节码上面的内容和idea 反编译有不一致的地方。
![](https://img.haomeiwen.com/i1228444/3dc7a524bc295ea1.png)
Spring 集成aspectj
1、@Configurable 注解
@Configurable("beanOne")
private static class ShouldBeConfiguredBySpring {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
Spring bean beanConfigurerTests.xml配置
<bean class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"
factory-method="aspectOf"/>
<bean id="beanOne"
class="com.github.yulechen.spring.test.Main$ShouldBeConfiguredBySpring"
lazy-init="true">
<property name="name" value="Rod"/>
</bean>
myObject.getName==Rod
new ClassPathXmlApplicationContext("beanConfigurerTests.xml");
ShouldBeConfiguredBySpring myObject = new ShouldBeConfiguredBySpring();
怎么实现的?
public aspect AnnotationBeanConfigurerAspect extends AbstractBeanConfigurerAspect {
public AnnotationBeanConfigurerAspect() {
setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
}
/**
* The creation of a new bean (an object with the @Configurable annotation)
*/
protected pointcut beanCreation(Object beanInstance) :
initialization((@Configurable *).new(..)) && this(beanInstance);
}
切面匹配:
1、只有匹配到有@Configurable 注解的类,new 创建对象的之后,会调用AbstractBeanConfigurerAspect#configureBean(beanInstance);
2、AnnotationBeanConfigurerAspect 是一个单例,在整个系统中只有一份。Spring 读取bean定义的时候,把BeanFactory 注入到了AnnotationBeanConfigurerAspect 中,并且Spring 创建 AnnotationBeanConfigurerAspect 使用了factory-method="aspectOf" ,这个源码中没有该方法,使用ajc 编译后,所有的切面都会添加该方法,见前面编译时期植入代码。
3、LTW 植入
1、 添加jvm 启动参数 -javaagent:path/aspectjweaver.jar
2、aop 文件存在于spring-aspectj.jar/META-INF/aop.xml 中。
Spring LTW 实现
LTW 实现方式有两种
1、使用java agent,hook 所有类加载器。
2、使用自定义classloader
Spring classloader 方式实现
AbstractOverridingClassLoader,重写类加载过程,
![](https://img.haomeiwen.com/i1228444/f59ebfa16556c5c9.png)
Spring java agent 方式实现,只实现了获取Instrumentation 对象。需要启动的时候添加 -javaagent 参数
![](https://img.haomeiwen.com/i1228444/9972fb1a5c5362a7.png)
后续扩展操作:
public void addTransformer(ClassFileTransformer transformer) {
Assert.notNull(transformer, "Transformer must not be null");
Instrumentation instrumentation = InstrumentationSavingAgent.getInstrumentation();
if (instrumentation == null) {
throw new IllegalStateException(
"Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.");
}
instrumentation.addTransformer(transformer);
}
小结:
aspectj 整体在项目中使用不多,但是如果普通的切面编程没法满足需求时候。可以使用aspectj 现有的功能。java agent 在APM 项目中使用较多。可以通过java agent 获取jvm 运行时期的一些信息,没有代码侵入性。java agent 的instrumentation 是jvm 实现的留下的埋点,通过事件驱动触发。
网友评论