Spring做了两件事:1.管理Bean的生命周期 2. 创建Bean的代理对象
一、Spring容器
1.1 BeanFactory接口
BeanFactory接口表面只定义了获取Bean的方法,但是其实现类实际上负责了IOC、DI和管理Bean的生命周期。
实现类:DefaultListableBeanFactory
1.读取Bean的定义只能通过registerBeanDefinition()以编码的形式。
2.手动添加后处理器;如果要支持注解等扩展功能,只能通过AnnotationConfigUtils手动添加。
3.不会主动实例化单例对象。
Spring容器,从根源上是由BeanFactory接口的实现类DefaultListableBeanFactory实现的;它是真正可以作为一个独立容器使用的类。
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(即bean的一些描述信息,包含class:bean是哪个类,scope:单例还是多例,初始化、销毁方法等)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
// 给 BeanFactory添加一些常用的后处理器,让它具备解析@Configuration、@Bean等注解的能力
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 从bean工厂中取出BeanFactory的后处理器,并且执行这些后处理器
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
System.out.println(beanFactoryPostProcessor);
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
// 打印BeanFactory中Bean
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
// 要想@Autowired、@Resource等注解被解析,还要添加Bean的后处理器,可以针对Bean的生命周期的各个阶段提供扩展
beanFactory.addBeanPostProcessors(beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().sorted(beanFactory.getDependencyComparator()).collect(Collectors.toCollection(ArrayList::new)));
// 通过AnnotationConfigUtils给beanFactory添加一些后处理的时候会默认设置比较器,可以对BeanPostProcessor进行排序,排序的依据是BeanPostProcessor内部的order属性,其中internalAutowiredAnnotationProcessor的order属性的值为Ordered.LOWEST_PRECEDENCE - 2,internalCommonAnnotationProcessor的order属性的值为Ordered.LOWEST_PRECEDENCE - 3
System.out.println("internalAutowiredAnnotationProcessor:" + (Ordered.LOWEST_PRECEDENCE - 2));
System.out.println("internalCommonAnnotationProcessor:" + (Ordered.LOWEST_PRECEDENCE - 3));
// DefaultListableBeanFactory不会主动初始化单例,所以:准备好所有单例,get()前就把对象初始化好
beanFactory.preInstantiateSingletons();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>");
Bean1 bean1 = beanFactory.getBean(Bean1.class);
System.out.println(bean1.getBean2());
System.out.println(bean1.getInter());
}
1.2 ApplicationContext接口
ApplicationContext接口比BeanFactory接口多4个接口的功能:
1.EnvironmentCapable:获取环境变量,可以通过ApplicationContext获取操作系统环境变量和JVM环境变量。
2.MessageSource:ApplicationContext继承了这个接口,就拥有了国际化功能,比如可以直接利用MessageSource对象获取某个国际化资源。
3.ResourcePatternResolver:ApplicationContext继承了这个接口,就拥有了加载并获取资源的功能,这里的资源可以是文件,图片等某个URL资源都可以。
4.ApplicationEventPublisher:ApplicationContext继承了这个接口,就拥有了事件发布功能,可以发布事件,这是ApplicationContext相对于BeanFactory比较突出、常用的功能。
1.实现类:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
1.读取Bean的定义以XML的形式。
2.如果要支持注解功能,只能通过<context:annotation-config/>。
3.主动实例化单例对象。
DefaultListableBeanFactory+XmlBeanDefinitionReader;基于类/磁盘路径下XML格式的配置文件创建容器;
2.实现类:AnnotationConfigApplicationContext
1.读取Bean的定义以配置类的形式。
2.默认要支持注解。
3.主动实例化单例对象。
基于JAVA的配置类进行容器的创建。
3.实现类:AnnotationConfigServletWebServerApplicationContext
基于JAVA的配置类进行容器的创建,模拟了 springboot:用于WEB环境(SpringMVC)。可以内嵌Web容器(个人理解就是用来将Spring容器放到ServletContext)和DispatcherServlet(解析HTTP请求)。
// 模拟了 springboot web项目内嵌Tomcat的工作原理
public void testAnnotationConfigServletWebServerApplicationContext() throws IOException {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 防止程序终止
System.in.read();
}
@Configuration
class WebConfig {
@Bean
// 1. WebServer工厂
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
// 2. web项目必备的DispatcherServlet
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
// 3. 将DispatcherServlet注册到WebServer上
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
4.实现类:GenericApplicationContext
1.它不预设指定任何bean定义格式(例如XML或注释)。
2.refresh()会自动执行添加的BeanFactory后处理器和Bean后处理器。
3.会初始化所有单例。
实现内部有一个DefaultListableBeanFactory实例。可以采用混合方式处理bean的定义,而不是采用特定的bean定义方式来创建bean。
二、Bean的生命周期
-
容器初始化:
BeanFactory
及其子接口和实现类,完成容器初始化并管理Bean的整个生命周期。 -
Bean定义:
BeanDefinitionReader
负责加载BeanDefinition
(定义方式:编码、XML、注解)。 -
Bean注册:
BeanDefinitionRegistry
配合扫描器(如ScannedGenericBeanDefinition
)将扫描到的BeanDefinition注册到容器。BeanFactoryPostProcessor
在BeanDefinition注册容器后,读取和修改BeanDefinition。 -
Bean实例化:
4.1 Bean实例化前,InstantiationAwareBeanPostProcessor
调用postProcessBeforeInstantiation()。
4.2 Bean实例化:Spring调用构造方法。
4.3 Bean实例化后,InstantiationAwareBeanPostProcessor
调用postProcessAfterInstantiation()。如果Bean出现循环依赖,则在此阶段之后创建Bean代理对象(即4.4用到了另一个被代理Bean,此时会先会生成该Bean的代理对象,存入二级缓存
中后给本Bean的注入)。
4.4 Bean属性注入前InstantiationAwareBeanPostProcessor
调用postProcessProperties();如果4.3返回false,该4.4不会被调用(会跳过依赖注入)。
4.4 Bean属性注入:给Bean属性进行赋值。 -
Bean初始化:
5.1 Bean初始化前,Aware
可获取spring的容器,然后获得相关的Bean。
5.2 Bean初始化前,BeanPostProcessor
添加自己的Before逻辑。
5.3 Bean初始化前,InitializingBean
执行特定业务化的操作。
5.4 Bean的初始化,init方法执行。
5.5 Bean初始化后,BeanPostProcessor
添加自己的After逻辑。正常情况下如果Bean需要被代理,就是在此阶段进行。 -
Bean的AOP: 提供代理Bean,增强可被重写方法的功能。注意Spring中只存储代理后的对象。代理对象与目标对象是两个不同的对象,不会共享成员变量。代理对象也不会走前面的流程。
-
Bean的使用: 单例对象会放入BeanFactory池中。
-
Bean的销毁:
8.1 Bean销毁前,DisposableBean
获得一次回调。
8.2 Bean的销毁,destroy方法执行。
Bean的初始化和销毁的方式:
初始化:
1.@Bean指定init-method
2.实现InitializingBean
3.@PostConstruct
4.BeanPostProcessor
销毁:
1.@Bean指定destroy-method
2.实现DisposableBean
3.@PreDestroy
InitializingBean和BeanPostProcessor区别:
BeanPostProcessor对所有Bean都有作用而InitializingBean、Aware是对实现本接口的Bean起作用。
实现InstantiationAwareBeanPostProcessor和BeanPostProcessor的Bean,后处理器自身的方法为什么不会被调用?
答:后处理器的注册前提是完成了后处理器的实例化;而Bean的注册不需要实例化。
1.完成Bean的注册。
2.实例化前:不会执行InstantiationAwareBean后处理器的方法,
是由于此时本身还没实例化,且没有被注册“后处理器”。
3.实例化:完成Bean的实例化。此时才有注册“后处理器”的资格。
4.初始化前:不会执行BeanPost后处理器的方法,是由于没有被注册成为“后处理器”。
5.完成Bean初始化。
6.注册成为“后处理器”。
三、AOP
1.AOP的实现方式:proxy、Aspectj、agent
-
Aspectj
:编译器在编译阶段改写class文件,需要使用Aspectj插件。 -
agent
:JVM加载class过程中改写class文件。 -
proxy
:运行时生成代理对象;不能代理静态方法;其实现方式:JDK和CGLIB;
JDK:实现同一个接口;代理对象与原对象是兄弟关系,不能互转。
CGLIB:利用继承关系,重写父类方法;代理对象与原对象是父子关系,可以互转,但父类不能为final。
proxy的两种方式,为了优化避免利用反射回调被代理对象的方法,采用的优化手段有区别。
Spring的AOP:JDK或CGLIB
public class Aop {
public static void main(String[] args) {
//1.准备切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution (* foo())");
//2.准备通知
MethodInterceptor advice = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before ^" );
invocation.proceed();
System.out.println("after ^" );
return null;
}
};
//3.准备切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
// 4.创建代理
Target target= new Target();
ProxyFactory factory = new ProxyFactory();
//proxyTargetClass = false,且目标实现了接口,使用JDK。
//proxyTargetClass = false,且目标没有实现接口,使用CGLIB。
//proxyTargetClass = trun,直接使用CGLIB。
factory.setProxyTargetClass(false);
factory.setTarget(target);
factory.addAdvisor(advisor);
Target proxy = (Target)factory.getProxy();
proxy.foo();
}
static class Target {
public void foo(){
System.out.println("foo……");
}
}
}
2. AOP的组成部分:切点、通知以及通知类型
对于日志的注解案例,以Logger类:
1.通知:即方法本身。
2.通知类型:在方法上注解。
3.切点:在Logger类或者方法(通知)上设置切点表达式。
对于Spring事务的注解案例,事务的通知类是在jar包中,导致:
1.通知:即方法本身,Spring已经固定。
2.通知类型:Spring使用环绕通知。
3.切点:只能在目标方法上添加事务注解@Transactional。
3.Spring的AOP切面的细节
1.会将高级切面转换为基本的切面类。
2.对基本切面排序后将所有非环绕通知转换为环绕通知。
3.经过适配器转换后,MethodInterceptor执行调用链。
Spring常用工具类:https://www.cnblogs.com/q1359720840/p/16291116.html
网友评论