在前几篇文章中,分别介绍了实现pointcut表达式的功能,从xml中获取信息创建一个简单的ioc容器,如何解析注解,利用cglib生成新代理类该如何获取字段注解根据这些知识点,就可以实现一个简易版的ioc容器。
IOC
创建bean过程
在扫描xml中的配置时可能会有通过注解配置bean,由于在配置bean时可能会有相互引用bean的情况,因此需要xml中配置的bean信息和注解bean的信息加载完之后再来创建bean;
在创建bean时需要考虑到以下几个方面:
- 1.单例,多例bean应该在什么时候创建
- 2.bean的循环引用检测
- 3.在什么时候填充bean的属性
在创建bean的时候考虑到bean可能是单例或者是多例;那么在根据bean信息创建bean时多例bean就不需要在这个时候创建了,只需要在使用到多例bean时再创建;而单例bean则需要在这个阶段创建,因为只需要创建一次可供多次使用;
在创建bean时,如果在构造方法中引用了其他bean就可能会造成循环引用,因此在创建bean时还需要检测是否构成了循环引用;那如何检测呢?其实也比较简单,只需要在创建bean时,将正在创建的beanName放入到一个set集合中,bean创建完成就将beanName从集合中删除;
在初始化bean之后,应该立即将bean的属性填充吗?
答案是否定的,因为可能会有aop切面的配置;aop代理有2种方式:JDK动态代理,cglib代理;cglib代理会导致代理后的对象属性值消失;因而为了统一处理,应该在aop代理完成之后再填充bean的属性;
cglib代理会生成一个新class,而target只是提供class信息;新代理生成的cglibObject对象与原target对象没有关联;因而如果创建target对象时给target对象属性赋值经cglib代理之后,新生成的对象cglibObject的属性值仍然是null;
jdk动态代理的生成的类,proxyObject对原target对象保持引用;因此对target对象的属性赋值之后,经aop代理生成的类不会丢失原target的属性值;
如何获取bean?
在创建bean时只创建了单例bean,如果获取的bean是多例,那么就需要重新创建;
AOP
流程:
从配置中获取到所有的advices,容器中的bean去匹配advices;这样bean就得到了匹配的advice,通过bean+advice由aop代理生成代理类对象;
advice的类型
advice一共有5中类型,其中after,around可以看作是复合类型,因此5种adviceType,可以分解为3种;
AOP代理
由于同一个目标类,可能同时被多个切面类代理;如果每被一个切面代理都要生成一个新的类型,这样效率就比较低;因此要将这些作用到同一个类的切面整合到一起,一次代理整合多个不同切面的advice;
切面类的整合实际是advice的整合,advice分为3类:before,afterReturning,afterThrowing;将所有切面配置的advice,按照类型分别存放到这3种类型中;
advice去重
advice加入到MethodAdvice时,是根据type类型分别加入到对应的XXXadviceInterceptor中;before,afterReturning,afterThrowing这三种advice对应:BeforeAdviceInterceptor,AfterReturningAdviceInterceptor,AfterThrowingAdviceInterceptor;
而advice,around与其他三种类型不同;
- after要将其分别加入到:AfterReturningAdviceInterceptor,AfterThrowingAdviceInterceptor;
- around要将其加入到:BeforeAdviceInterceptor,AfterReturningAdviceInterceptor,AfterThrowingAdviceInterceptor;
每个advice可以分成三部分:adviceType + pointcut + method;假设现在有2个advice:
- before:
<before pointcut = " execution( * * * test*())" method="before" />
- around:
<around pointcut = " execution( * * * test*())" method="before" />
这2个advice是同一个切面,那么before与around的:pointcut ,method,都相同;而around可以分解为:before + afterReturning + afterThrowing;around生成的advice会加入到这3个XXXadviceInterceptor中;在beforeAdviceInterceptor中就有2个advice对象,而这2个advice调用的切面方法相同都是before,就会造成调用相同切面的before方法2次;那如何去重也就简单了,只需要在XXXadviceInterceptor中判断:新加入的advice在该XXXadviceInterceptor中是否存在;advice是否存在由:pointcut ,method决定;
这里需要注意的是,Advice是放入到Set中,需要重写Advice的hashcode和equals方法;而Advice包含Pointcut对象,因此还需要重写Pointcut的hashcode和equals方法;
测试
- AOP 的advice去重测试
<beans>
<bean id="aspect" class="tt.AOPtest"/>
<bean class ="tt.Aspected"/>
<aopConfig>
<aspect ref = "aspect">
<before pointcut="execution (* * tt.inr.impl.* *())" method="before" />
<!--<after pointcut="execution (* * * *())" method="after" />-->
<around pointcut="execution (* * tt.inr.impl.* *())" method="before" />
</aspect>
</aopConfig>
<propertyPlaceholder load="test.properties"/>
<componentScan /><!--- 扫描注解-->
</beans>
test.properties
t1=dfgdfg
t2=ty
t3=name1
name=78
fg=456
bean
@Component
public class QAZ implements QWE {
@Value(" ${t1} ")
String tt;
@Value("${ fg }")
String gh;
@Value("${name}")
String nb;
@Value("my addr")
String addr;
@Override
public String toString() {
return "QAZ{" +
"tt='" + tt + '\'' +
", gh='" + gh + '\'' +
", nb='" + nb + '\'' +
", addr='" + addr + '\'' +
'}';
}
@Override
public void print() {
System.out.println(toString());
}
}
测试代码:
@Test
public void test() throws Throwable {
Map<String , XmlNode> labelParse = new HashMap<>();
labelParse.put("aopConfig", AdvisorParse.getInstance());
ApplicationContext context =
new XmlApplicationContext("aop.xml",labelParse,null);
Aspected aspected = (Aspected) context.getBean(Aspected.class);
QWE qaz = (QWE) context.getBean(QWE.class);
qaz.print();
}
==============================result===============================================================
aspect before test
QAZ{tt='dfgdfg', gh='456', nb='78', addr='my addr'}
aspect before test
- 循环引用测试
只有在构造方法中引用其他bean,才可能造成循环引用;
public class A {
B b;
public A(B b){
this.b = b;
}
}
public class B {
public A a;
public B(A a){
this.a = a;
}
}
xml配置
<beans >
<bean id="test2" class="tt.B" >
<constructor type="tt.A" ref="test1" />
</bean>
<bean id="test1" class="tt.A">
<constructor type="tt.B" ref="test2" />
</bean>
</beans>
结果
exception.CycleDependencyException: tt.B->tt.A->tt.B
at Context.BeanFactory.creatBean(BeanFactory.java:75)
at Context.BeanFactory.getConstructorParamRefValue(BeanFactory.java:136)
- @Autowired测试
@Component
public class QAZ implements QWE {
@Autowired
AutowiredTest autowiredTest;
....
..
}
@Component
public class AutowiredTest {
@Value("${autowired }")
String q1;
@Value("dgd")
String q2;
@Value("1")
int yy;
@Value("true")
boolean rr;
@Override
public String toString() {
return "AutowiredTest{" +
"q1='" + q1 + '\'' +
", q2='" + q2 + '\'' +
", yy=" + yy +
", rr=" + rr +
'}';
}
}
结果
aspect before test
QAZ{autowiredTest=AutowiredTest{q1='testAutowire', q2='dgd', yy=1, rr=true}, tt='dfgdfg', gh='456', nb='78', addr='my addr'}
aspect before test
代码地址:ioc-aop