Spring原理学习笔记 学习来源:黑马程序员
学习时间:2023年11月4日
1 容器接口 1.1 BeanFactory和ApplicationContext 最重要的两个接口分别是BeanFactory
和ApplicationContext
。关系如下:
从这个类图我们可以大致看出BeanFactory
和ApplicationContext
的关系,BeanFactory
是ApplicationContext
的基类,BeanFactory所拥有的功能ApplicationContext都拥有,不仅如此,ApplicationContext还拓展了一些功能,它通过实现MessageSource、 ResourceLoader等接口,在BeanFactory简单IOC容器的基础上添加了许多高级容器的特征。
Spring的核心容器是BeanFactory
。
在SpringBoot的引导类中,run方法的返回值就是一个可配置的Spring容器
1 2 3 4 5 6 7 8 9 10 @SpringBootApplication public class A01Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); context.getBean("aaa" ); } }
BeanFactory接口中的方法:
1.2 BeanFactory接口的功能 从1.1节
可以看到BeanFactory提供的方法只有getBean
,而控制反转,依赖注入直到Bean的生命周期的各种功能,是由它的实现类来提供的。
BeanFactory的一个重要的实现类是DefaultListableBeanFactory
,类图如下:
其中DefaultListableBeanFactory
的父类DefaultSingletonBeanRegistry
管理着单例的对象:
1 2 3 4 5 6 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256 ); }
准备两个bean,分别为Component1
和Component2
:
1 2 3 4 5 6 7 8 9 @Component public class Component1 {} @Component public class Component2 {}
打印这两个bean的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @SpringBootApplication @ComponentScan public class A01Application { private static final Logger log = LoggerFactory.getLogger(A01Application.class); public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException { ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects" ); singletonObjects.setAccessible(true ); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory); map.entrySet().stream() .filter(e -> e.getKey().startsWith("component" )) .forEach(e -> { System.out.println(e.getKey() + "=" + e.getValue()); }); } }
打印结果:
1 2 component1=com.hongyi.apiintegration.spring.Component1@3aa41da1 component2=com.hongyi.apiintegration.spring.Component2@74fab04a
1.3 ApplicationContext接口的功能 暂略
2 容器实现 2.1 BeanFactory接口实现 2.1.1 BeanFactoryPostProcessor DefaultListableBeanFactory
类是BeanFactory
的一个重要的实现类。以此为讲解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class TestBeanFactory { public static void main (String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton" ).getBeanDefinition(); beanFactory.registerBeanDefinition("config" , beanDefinition); for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); } System.out.println("--------------" ); } @Configuration static class Config { @Bean public Bean1 bean1 () { return new Bean1(); } @Bean public Bean2 bean2 () { return new Bean2(); } } static class Bean1 { private static final Logger log = LoggerFactory.getLogger(Bean1.class); public Bean1 () { log.debug("构造 Bean1()" ); } @Autowired private Bean2 bean2; public Bean2 getBean2 () { return bean2; } } static class Bean2 { private static final Logger log = LoggerFactory.getLogger(Bean2.class); public Bean2 () { log.debug("构造 Bean2()" ); } } }
打印信息:
发现只有一个config的bean,而没有bean1和bean2的bean。原因在于BeanFactory
并没有提供解析注解的功能,而是需要一些后处理器 来完成。
1 2 3 4 5 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); }
打印结果:
1 2 3 4 5 6 7 8 9 config org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory
可以看到容器中多出了几个后处理器的bean,主要分为两类:
BeanFactory后处理器,BeanFactoryPostProcessor
,用于解析Component,Bean注解
Bean后处理器,BeanPostProcessor
,用于解析Autowired,Resource注解
其中internalConfigurationAnnotationProcessor
后处理器用于解析注解,例如@Configuration
,@Bean
等。
现在执行这些后处理器:
1 2 3 4 5 6 7 8 9 beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> { beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); }); for (String name : beanFactory.getBeanDefinitionNames()) { System.out.println(name); }
打印结果:
1 2 3 4 5 6 7 8 config org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory bean1 bean2
可以看到bean1和bean2的定义被加载进了容器当中。
2.1.2 BeanPostProcessor 现在执行:
1 System.out.println(beanFactory.getBean(Bean1.class).getBean2());
打印结果:
1 2 3 4 14:56:08.360 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1' 14:56:08.363 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config' 14:56:08.383 [main] DEBUG com.hongyi.apiintegration.spring.TestBeanFactory$Bean1 - 构造 Bean1() null
bean1中注入了bean2,但是打印时却为null,说明@Autowired
注解并未生效。同样需要后处理器来执行,这里后处理器的类型为BeanPostProcessor
。
1 2 3 4 beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor); System.out.println(beanFactory.getBean(Bean1.class).getBean2());
打印结果:
2.1.3 即时加载和延时加载 此外注意到,目前bean对象的产生是延时的(懒加载) :当调用到getBean()
时,才调用构造方法,创建出对象。
如果要实现即时加载(饿加载):
1 2 3 4 beanFactory.preInstantiateSingletons(); System.out.println("--------------" ); System.out.println(beanFactory.getBean(Bean1.class).getBean2());
打印结果:
可以看到,在执行beanFactory.getBean(Bean1.class).getBean2()
之前,容器就为我们创建好了bean1和bean2的实例对象。
2.1.4 小结 BeanFactory接口不会做的事:
不会主动添加和调用BeanFactory后处理器
不会主动添加和调用Bean后处理器
不会主动初始化单例对象,需要显式调用preInstantiateSingletons()
不会解析${}
和#{}
2.2 ApplicationContext接口实现 ApplicationContext接口实现类主要有:
ClassPathXmlApplicationContext
类:基于classpath下xml格式的配置文件来创建容器
FileSystemXmlApplicationContext
类:基于磁盘路径下xml格式的配置文件来创建容器
AnnotationConfigApplicationContext
类:基于java配置类来创建
AnnotationConfigServletWebServerApplicationContext
:基于java配置类来创建,用于web环境
2.2.1 ClassPathXmlApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Slf4j public class A02Application { public static void main (String[] args) { testClassPathXmlApplicationContext(); } private static void testClassPathXmlApplicationContext () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml" ); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } System.out.println(context.getBean(Bean2.class).getBean1()); } static class Bean1 { } static class Bean2 { private Bean1 bean1; public void setBean1 (Bean1 bean1) { this .bean1 = bean1; } public Bean1 getBean1 () { return bean1; } } }
在resources
创建配置文件b01.xml
:
1 2 3 4 5 6 7 8 9 10 11 12 <?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 ="bean1" class ="com.hongyi.apiintegration.spring.a02.A02Application.Bean1" /> <bean id ="bean2" class ="com.hongyi.apiintegration.spring.a02.A02Application.Bean2" > <property name ="bean1" ref ="bean1" /> </bean > </beans >
打印结果:
2.2.2 FileSystemXmlApplicationContext 略
2.2.3 AnnotationConfigApplicationContext 以Java配置类(以@Configuration
标注)的形式替代了xml
格式的配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Slf4j public class A02Application { public static void main (String[] args) { testAnnotationConfigApplicationContext(); } private static void testAnnotationConfigApplicationContext () { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } System.out.println(context.getBean(Bean2.class).getBean1()); } @Configuration static class Config { @Bean public Bean1 bean1 () { return new Bean1(); } @Bean public Bean2 bean2 () { return new Bean2(); } } static class Bean1 { } static class Bean2 { @Autowired private Bean1 bean1; public void setBean1 (Bean1 bean1) { this .bean1 = bean1; } public Bean1 getBean1 () { return bean1; } } }
打印结果:
1 2 3 4 5 6 7 8 9 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory a02Application.Config bean1 bean2 com.hongyi.apiintegration.spring.a02.A02Application$Bean1@1fb700ee
可以看到,容器中有后处理器的定义,还有配置类Config的定义,以及bean1和bean2的定义,依赖注入也成功了。
2.2.4 AnnotationConfigServletWebServerApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Slf4j public class A02Application { public static void main (String[] args) { testAnnotationConfigServletWebServerApplicationContext(); } private static void testAnnotationConfigServletWebServerApplicationContext () { AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class); } @Configuration static class WebConfig { @Bean public ServletWebServerFactory servletWebServerFactory () { return new TomcatServletWebServerFactory(); } @Bean public DispatcherServlet dispatcherServlet () { return new DispatcherServlet(); } @Bean public DispatcherServletRegistrationBean dispatcherServletRegistrationBean (DispatcherServlet dispatcherServlet) { return new DispatcherServletRegistrationBean(dispatcherServlet, "/" ); } @Bean("/hello") public Controller controller1 () { return (request, response) -> { response.getWriter().print("Hello World!" ); return null ; }; } } }
访问结果:
3 Bean的生命周期 3.1 四个阶段 按顺序为:
实例化(构造)
依赖注入
初始化
销毁
在每个阶段的前后,都可以添加一些Bean后处理器(BeanPostProcessor
)来进行功能增强。
1 2 3 4 5 6 7 8 @SpringBootApplication public class A03Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args); context.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Slf4j @Component public class LifeCycleBean { public LifeCycleBean () { log.info("构造" ); } @Autowired public void autowire (@Value("${JAVA_HOME}") String home) { log.info("依赖注入:{}" , home); } @PostConstruct public void init () { log.info("初始化" ); } @PreDestroy public void destroy () { log.info("销毁" ); } }
执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Slf4j @Component public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor , DestructionAwareBeanPostProcessor { @Override public void postProcessBeforeDestruction (Object o, String s) throws BeansException { if (s.equals("lifeCycleBean" )) { log.info("<<<<<<<<<< 销毁之前执行, 如@PreDestroy" ); } } @Override public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean" )) { log.info("<<<<<<<<<< 实例化之前执行, 这里返回的对象会替换原本的bean" ); } return null ; } @Override public boolean postProcessAfterInstantiation (Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean" )) { log.info("<<<<<<<<<< 实例化之后执行, 这里如果返回false会跳过依赖注入阶段" ); } return true ; } @Override public PropertyValues postProcessProperties (PropertyValues pvs, Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean" )) { log.info("<<<<<<<<<< 依赖注入阶段执行, 如@Autowired, @Value, @Resource" ); } return pvs; } @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean" )) { log.info("<<<<<<<<<< 初始化之前执行, 这里返回的对象会替换原本的bean, 如@PostConstruct, @ConfigurationProperties" ); } return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean" )) { log.info("<<<<<<<<<< 初始化之后执行, 这里返回的对象会替换原本的bean, 如代理增强" ); } return bean; } }
执行结果:
3.2 模板方法 模板方法设计模式详见[[Java设计模式学习笔记新#6.1 模板方法模式]]
Bean后处理器采用了模板方法的设计模式。
1 2 3 public interface BeanPostProcessor { void inject (Object bean) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class MyBeanFactory { private List<BeanPostProcessor> processors = new ArrayList<>(); public Object getBean () { Object bean = new Object(); System.out.println("构造 " + bean); System.out.println("依赖注入 " + bean); for (BeanPostProcessor processor : processors) { processor.inject(bean); } System.out.println("初始化 " + bean); return bean; } public void addBeanPostProcessors (BeanPostProcessor processor) { processors.add(processor); } public static void main (String[] args) { MyBeanFactory beanFactory = new MyBeanFactory(); beanFactory.addBeanPostProcessors(bean -> { System.out.println("解析@Autowired" ); }); beanFactory.addBeanPostProcessors(bean -> { System.out.println("解析@Resource" ); }); Object bean = beanFactory.getBean(); System.out.println(bean); } }
打印结果:
1 2 3 4 5 构造 java.lang.Object@6433a2 依赖注入 java.lang.Object@6433a2 解析@Autowired 初始化 java.lang.Object@6433a2 java.lang.Object@6433a2
4 Bean后处理器 Bean后处理器(BeanPostProcessor
)的作用:为Bean的生命周期的各个阶段(实例化,依赖注入,初始化和销毁)提供扩展。
4.1 常见的Bean后处理器
AutowiredAnnotationBeanPostProcessor
:解析@Autowired
,@Value
注解(涉及依赖注入)
CommonAnnotationBeanPostProcessor
:解析@Resource
,@PostConstruct
,@PreDestroy
注解(涉及依赖注入,初始化和销毁)
ConfigurationPropertiesBindingPostProcessor
:解析@ConfigurationProperties
注解(涉及依赖注入)
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Slf4j public class Bean1 { private Bean2 bean2; @Autowired public void setBean2 (Bean2 bean2) { log.info("@Autowired生效: {}" , bean2); this .bean2 = bean2; } private Bean3 bean3; @Resource public void setBean3 (Bean3 bean3) { log.info("@Resource生效: {}" , bean3); this .bean3 = bean3; } private String home; @Autowired public void setHome (@Value("${JAVA_HOME}") String home) { log.info("@Value生效: {}" , home); this .home = home; } }
1 2 3 public class Bean2 { }
1 2 3 4 5 6 7 @ConfigurationProperties(prefix = "java") @Data public class Bean4 { private String home; private String version; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class A04Application { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("bean1" , Bean1.class); context.registerBean("bean2" , Bean2.class); context.registerBean("bean3" , Bean3.class); context.registerBean("bean4" , Bean4.class); context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.registerBean(CommonAnnotationBeanPostProcessor.class); ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory()); context.refresh(); System.out.println(context.getBean(Bean4.class)); context.close(); } }
GenericApplicationContext
基本就是对DefaultListableBeanFactory 做了个简易的封装,几乎所有方法都是使用了DefaultListableBeanFactory的方法去实现。
执行结果:
4.2 AutowiredAnnotationBeanPostProcessor流程解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class DigInAutowired { public static void main (String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("bean2" , new Bean2()); beanFactory.registerSingleton("bean3" , new Bean3()); beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor(); processor.setBeanFactory(beanFactory); Bean1 bean1 = new Bean1(); System.out.println(bean1); processor.postProcessProperties(null , bean1, "bean1" ); System.out.println(bean1); } }
打印结果:
可以看到,在未添加bean后处理器时打印bean1,bean1依赖的bean2和home为null,当添加AutowiredAnnotationBeanPostProcessor
类型的后处理器后再次打印,可以看到bean2和home被成功注入,bean3为@Resource
注解,因此未能被注入。
postProcessProperties
方法源码:
1 2 3 4 5 public PropertyValues postProcessProperties (PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = this .findAutowiringMetadata(beanName, bean.getClass(), pvs); }
现在使用反射来执行私有方法findAutowiringMetadata
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class DigInAutowired { public static void main (String[] args) throws Throwable { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("bean2" , new Bean2()); beanFactory.registerSingleton("bean3" , new Bean3()); beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor(); processor.setBeanFactory(beanFactory); Bean1 bean1 = new Bean1(); Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata" , String.class, Class.class, PropertyValues.class); findAutowiringMetadata.setAccessible(true ); InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1" , Bean1.class, null ); System.out.println(metadata); metadata.inject(bean1, "bean1" , null ); System.out.println(bean1); } }
invoke
执行后,后处理器将@Autowired注解的属性和方法中的参数封装为InjectionMetadata
,可以看到InjectionMetadata metaData
是一个ArrayList
类型的数组,包含两个元素,即是Bean1中的@Autowired依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private Bean2 bean2;private String home;@Autowired public void setBean2 (Bean2 bean2) { log.info("@Autowired生效: {}" , bean2); this .bean2 = bean2; } @Autowired public void setHome (@Value("${JAVA_HOME}") String home) { log.info("@Value生效: {}" , home); this .home = home; }
执行inject
时,容器会从中寻找Bean2
类型和String
类型的bean,完成这些bean的实例化,打印结果为:
那么Spring容器是如何按照类型从容器中来查找Bean1所依赖的bean呢?这涉及到inject
方法的源码,由于源代码实现太过复杂,这里通过反射进行模拟实现:
对于Bean1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j public class Bean1 { private Bean2 bean2; @Autowired public void setBean2 (Bean2 bean2) { log.info("@Autowired生效: {}" , bean2); this .bean2 = bean2; } @Autowired private Bean3 bean3; }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class DigInAutowired { public static void main (String[] args) throws Throwable { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("bean2" , new Bean2()); beanFactory.registerSingleton("bean3" , new Bean3()); Field bean3 = Bean1.class.getDeclaredField("bean3" ); DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false ); Object o1 = beanFactory.doResolveDependency(dd1, null , null , null ); System.out.println(o1); Method setBean2 = Bean1.class.getDeclaredMethod("setBean2" , Bean2.class); DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0 ), false ); Object o2 = beanFactory.doResolveDependency(dd2, null , null , null ); System.out.println(o2); } }
打印结果:可见被成功注入了。
1 2 com.hongyi.apiintegration.spring.a04.Bean3@55f3ddb1 com.hongyi.apiintegration.spring.a04.Bean2@2b98378d
如果去掉beanFactory.registerSingleton("bean3", new Bean3());
,即不注册Bean3类型的bean,则:
1 2 null com.hongyi.apiintegration.spring.a04.Bean2@d8355a8
再者,如果设置DependencyDescriptor
为true
:
1 DependencyDescriptor dd1 = new DependencyDescriptor(bean3, true );
则结果为:
抛出NoSuchBeanDefinitionException
的异常。
5 BeanFactory后处理器 BeanFactory后处理器(BeanFactoryPostProcessor
)的作用:为BeanFactory做扩展,例如解析注解@ComponentScan
,@Bean
等,将这些被注解标注了的类和方法转换为BeanDefinition
注册到容器中。
5.0 BeanDefinition BeanDefinition
是定义 Bean 的配置元信息接口,存储Bean的相关信息,主要包括:Bean的属性、是否单例、延迟加载、Bean的名称、构造方法等。
Spring中每一个被扫描到的bean都会生成一个BeanDefinition
BeanDefinition的主要作用是为了在只解析一次类的情况下,最大程度的拿到这类的信息。防止重复解析导致效率变低。
该接口派生出 AnnotatedBeanDefinition
接口,以及常用子类 RootBeanDefinition
、GenericBeanDefinition
。
可以使用 BeanDefinitionBuilder
或 new BeanDefinition
实现类构建 BeanDefinition
对象。
5.1 常见的BeanFactory后处理器
ConfigurationClassPostProcessor
:解析@ComponentScan
,@Bean
,@Import
,@ImportResource
MapperScannerConfigurer
:解析@Mapper
,将接口转换为对象
代码示例
1 2 3 4 5 public class Bean1 { public Bean1 () { log.info("我被 Spring 管理了" ); } }
1 2 3 4 5 6 @Component public class Bean2 { public Bean2 () { log.info("我被 Spring 管理了" ); } }
1 2 3 @Mapper public interface Mapper1 {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Configuration @ComponentScan("com.hongyi.apiintegration.spring.a05.component") public class Config { @Bean public Bean1 bean1 () { return new Bean1(); } @Bean public SqlSessionFactoryBean sqlSessionFactoryBean (DataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } @Bean(initMethod = "init") public DruidDataSource druidDataSource () { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/test" ); dataSource.setUsername("root" ); dataSource.setPassword("12345678" ); return dataSource; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Slf4j public class A05Application { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("config" , Config.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
没有配置BeanFactory后处理器时:
配置后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Slf4j public class A05Application { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("config" , Config.class); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(MapperScannerConfigurer.class, bd -> { bd.getPropertyValues().add("basePackage" , "com.hongyi.apiintegration.spring.a05.mapper" ); }); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
打印的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 config org.springframework.context.annotation.ConfigurationClassPostProcessor org.mybatis.spring.mapper.MapperScannerConfigurer bean2 bean1 sqlSessionFactoryBean druidDataSource mapper1 mapper2 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory
5.2 @ComponentScan流程解析 这里对@ComponentScan
注解的流程进行模拟实现,并抽取成一个自定义的BeanFactory后处理器。
bean的准备:位于包com.hongyi.apiintegration.spring.a05.component
下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component public class Bean2 { public Bean2 () { log.info("我被 Spring 管理了" ); } } @Controller @Slf4j public class Bean3 { public Bean3 () { log.info("我被 Spring 管理了" ); } } public class Bean4 { public Bean4 () { log.info("我被 Spring 管理了" ); } }
步骤:
查看配置类是否有@ComponentScan
注解
获取@ComponentScan
的value
属性(可能有多个),即扫描的包com.hongyi.apiintegration.spring.a05.component
,遍历每个包
遍历该包下的所有类 ,查看每一个类是否有@Component
注解或者派生注解修饰
如果有修饰,则根据该类的信息构造BeanDefinition
,然后注册到Bean工厂中,完成注解扫描的功能
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 @Slf4j public class A05Application { public static void main (String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("config" , Config.class); ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class); if (componentScan != null ) { for (String p : componentScan.basePackages()) { System.out.println(p); String path = "classpath*:" + p.replace("." , "/" ) + "/**/*.class" ; System.out.println(path); Resource[] resources = context.getResources(path); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator(); for (Resource resource : resources) { MetadataReader reader = factory.getMetadataReader(resource); if (reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()) || reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) { DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(reader.getClassMetadata().getClassName()) .getBeanDefinition(); String beanName = generator.generateBeanName(beanDefinition, beanFactory); beanFactory.registerBeanDefinition(beanName, beanDefinition); } } } } context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
打印结果:
现在封装成ComponentScanPostProcessor
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class ComponentScanPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { try { ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class); if (componentScan != null ) { for (String p : componentScan.basePackages()) { System.out.println(p); String path = "classpath*:" + p.replace("." , "/" ) + "/**/*.class" ; System.out.println(path); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path); AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator(); for (Resource resource : resources) { MetadataReader reader = factory.getMetadataReader(resource); if (reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()) || reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(reader.getClassMetadata().getClassName()) .getBeanDefinition(); if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory; String beanName = generator.generateBeanName(beanDefinition, beanFactory); beanFactory.registerBeanDefinition(beanName, beanDefinition); } } } } } } catch (IOException e) { e.printStackTrace(); } } }
将该后处理器注册到容器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Slf4j public class A05Application { public static void main (String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("config" , Config.class); context.registerBean(ComponentScanPostProcessor.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
打印结果:
5.3 @Bean流程解析 对配置类Config
中每一个被标注了@Bean
的方法进行遍历,然后构造BeanDefinition
注册到容器中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class AtBeanPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { try { CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/hongyi/apiintegration/spring/a05/Config.class" )); Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata method : methods) { System.out.println(method); Map<String, Object> annotationAttributes = method.getAnnotationAttributes(Bean.class.getName()); String initMethod = (String) annotationAttributes.get("initMethod" ); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); builder.setFactoryMethodOnBean(method.getMethodName(), "config" ); builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); if (initMethod.length() > 0 ) { builder.setInitMethodName(initMethod); } AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory; beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition); } } } catch (IOException e) { e.printStackTrace(); } } }
将这个后处理器注册到容器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Slf4j public class A05Application { public static void main (String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("config" , Config.class); context.registerBean(ComponentScanPostProcessor.class); context.registerBean(AtBeanPostProcessor.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
执行结果:
5.4 @Mapper流程解析 暂略
6 Aware接口
Aware
接口提供了一种内置 的注入手段,可以注入BeanFactory
,ApplicationContext
InitializingBean
接口提供了一种内置 的初始化手段
内置的注入和初始化不受扩展功能的影响,总是会被执行
6.1 常见接口
BeanNameAware
接口:注入bean的名字
BeanFactoryAware
接口:注入BeanFactory容器
ApplicationContextAware
接口:注入ApplicationContext容器
EmbeddedValueResolverAware
接口:解析${}
和#{}
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Slf4j public class MyBean implements BeanNameAware , ApplicationContextAware , InitializingBean { @Override public void setBeanName (String name) { log.info("当前的bean" + this + " 名字是: " + name); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { log.info("当前的bean" + this + " 容器是: " + applicationContext); } @Override public void afterPropertiesSet () { log.info("当前的bean" + this + " 初始化" ); } }
测试:
1 2 3 4 5 6 7 8 public class A06Application { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("myBean" , MyBean.class); context.refresh(); context.close(); } }
执行结果:
这些接口与@Autowired
和@PostConstruct
的不同:
@Autowired
和@PostConstruct
需要使用BeanPostProcessor
后处理器,属于扩展功能
Aware
接口属于内置功能,在某些情况下,扩展功能会失效,而内置功能不会失效
代码示例
MyBean
增加代码:
1 2 3 4 5 6 7 8 9 @Autowired public void aaa (ApplicationContext applicationContext) { log.info("当前的bean" + this + " 使用@Autowired注入的容器是: " + applicationContext); } @PostConstruct public void init () { log.info("当前的bean" + this + " 使用@PostConstruct初始化" ); }
运行后和上面的结果一样,说明@Autowired
和@PostConstruct
注解失效。
解决办法:为容器注册相应的Bean后处理器
1 2 context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.registerBean(CommonAnnotationBeanPostProcessor.class);
执行结果:
6.2 @Autowired失效
代码示例
在配置类中配置一个BeanFactoryPostProcessor的Bean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Configuration public class MyConfig1 { @Autowired public void setApplicationContext (ApplicationContext applicationContext) { log.info("注入ApplicationContext: " + applicationContext); } @PostConstruct public void init () { log.info("初始化" ); } @Bean public BeanFactoryPostProcessor processor1 () { return beanFactory -> { log.info("执行processor1" ); }; } }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class A06Application { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("myConfig1" , MyConfig1.class); context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.registerBean(CommonAnnotationBeanPostProcessor.class); context.registerBean(ConfigurationClassPostProcessor.class); context.refresh(); context.close(); } }
执行结果:
发现ApplicationContext
未能注入,并且@PostConstruct
注解的初始化方法也未能执行。
原因分析
配置类中不包含BeanFactoryPostProcessor
时的情况:
当配置类包含BeanFactoryPostProcessor
时,因此要创建BeanFactoryPostProcessor
必须提前创建Java配置类,而此时的BeanPostProcessor
尚未准备好,导致@Autowired
和@PostConstruct
失效:
修改代码
实现Aware
接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Slf4j @Configuration public class MyConfig2 implements InitializingBean , ApplicationContextAware { @Override public void afterPropertiesSet () throws Exception { log.info("初始化" ); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { log.info("注入ApplicationContext: " + applicationContext); } @Bean public BeanFactoryPostProcessor processor1 () { return beanFactory -> { log.info("执行processor1" ); }; } }
现在能够成功注入。
7 初始化和销毁 7.1 初始化 初始化的手段有三种:
使用@PostConstruct
注解
实现InitializingBean
接口,并重写
指定@Bean
的initMethod
属性
执行顺序:由上到下
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j public class Bean1 implements InitializingBean { @Override public void afterPropertiesSet () throws Exception { log.info("InitializingBean初始化" ); } @PostConstruct public void init2 () { log.info("@PostConstruct初始化" ); } public void init3 () { log.info("@Bean的initMethod初始化" ); } }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication public class A07Application { @Bean(initMethod = "init3") public Bean1 bean1 () { return new Bean1(); } public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args); context.close(); } }
执行结果:
7.2 销毁 销毁的手段有三种:
使用@PreDestroy
注解
实现Disposable
接口,并重写
指定@Bean
的destroyMethod
属性
代码略
8 Scope作用域 bean的Scope
有五种:
Singleton
Prototype
Request
Session
Application
8.1 域演示 现在演示后面三个域。
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Slf4j @Scope("application") @Component public class BeanForApplication { @PreDestroy public void destroy () { log.info("destroy" ); } } @Slf4j @Scope("request") @Component public class BeanForRequest { @PreDestroy public void destroy () { log.info("destroy" ); } } @Slf4j @Scope("session") @Component public class BeanForSession { @PreDestroy public void destroy () { log.info("destroy" ); } }
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @RestController public class MyController { @Lazy @Autowired private BeanForSession beanForSession; @Lazy @Autowired private BeanForApplication beanForApplication; @Lazy @Autowired private BeanForRequest beanForRequest; @GetMapping(value = "/test", produces = "text/html") public String test (HttpServletRequest request, HttpServletResponse response) { ServletContext sc = request.getServletContext(); return "<ul>" + "<li>" + "request scope:" + beanForRequest + "</li>" + "<li>" + "session scope:" + beanForSession + "</li>" + "<li>" + "application scope:" + beanForApplication + "</li>" + "</ul>" ; } }
发送请求后,返回:
1 2 3 request scope:com.hongyi.apiintegration.spring.a08.BeanForRequest@5e949f21 session scope:com.hongyi.apiintegration.spring.a08.BeanForSession@9eb8728 application scope:com.hongyi.apiintegration.spring.a08.BeanForApplication@52f10fb9
再次发送请求:
1 2 3 request scope:com.hongyi.apiintegration.spring.a08.BeanForRequest@3f3a62a3 session scope:com.hongyi.apiintegration.spring.a08.BeanForSession@9eb8728 application scope:com.hongyi.apiintegration.spring.a08.BeanForApplication@52f10fb9
发现只是request域的对象发生了变化,此外:
1 2 3 2023-11-17 14:25:07.754 c.h.a.spring.a08.BeanForRequest : destroy 2023-11-17 14:25:12.714 c.h.a.spring.a08.BeanForRequest : destroy 2023-11-17 14:25:20.673 c.h.a.spring.a08.BeanForRequest : destroy
可以看到当请求完成后,request域的对象会被销毁,而session域和application域的对象不会被销毁。
session域对象的存活时间默认为30min
,可以设置:
1 2 3 4 5 server: port: 80 servlet: session: timeout: 10s
8.2 @Scope失效 当一个单例对象注入其他scope(除了单例之外)的对象时,会发生后者scope失效的问题。
演示
1 2 3 4 5 6 7 8 9 @Component public class E { @Autowired private F f; public F getF () { return f; } }
1 2 3 4 5 @Scope("prototype") @Component public class F {}
测试:
1 2 3 4 5 6 7 8 9 10 11 12 @Slf4j @ComponentScan("com.hongyi.apiintegration.spring.a09") public class A09Application { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09Application.class); E e = context.getBean(E.class); System.out.println(e.getF()); System.out.println(e.getF()); System.out.println(e.getF()); context.close(); } }
执行结果:
1 2 3 com.hongyi.apiintegration.spring.a09.F@5e0e82ae com.hongyi.apiintegration.spring.a09.F@5e0e82ae com.hongyi.apiintegration.spring.a09.F@5e0e82ae 发现是同一个对象
失效原因
对于单例对象,依赖注入只执行一次 ,E
用的始终是第一次依赖注入的F
。
解决方法
方法1:依赖注入时使用@Lazy
注解生成一个代理对象,再由代理对象来调用F的方法
1 2 3 @Lazy @Autowired private F f;
1 2 3 4 System.out.println(e.getF().getClass()); System.out.println(e.getF()); System.out.println(e.getF()); System.out.println(e.getF());
执行结果:
1 2 3 4 class com.hongyi.apiintegration.spring.a09.F$$EnhancerBySpringCGLIB$$3be3693d // 被CGLIB代理增强的F的子类代理 com.hongyi.apiintegration.spring.a09.F@2b91004a com.hongyi.apiintegration.spring.a09.F@2fb3536e com.hongyi.apiintegration.spring.a09.F@35aea049
1 2 3 4 5 @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) @Component public class F {}
1 2 3 4 5 6 @Autowired private ObjectFactory<F> f;public F getF () { return f.getObject(); }
1 2 3 4 5 6 @Autowired private ApplicationContext context;public F getF () { return context.getBean(F.class); }
9 AOP实现 9.1 AspectJ AspectJ
编译器能够实现AOP的功能,原理在于它能在编译阶段将class
文件进行修改(增强)。
需要在pom
中引入AspectJ
的依赖和插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjrt</artifactId > </dependency > <plugin > <groupId > org.codehaus.mojo</groupId > <artifactId > aspectj-maven-plugin</artifactId > <version > 1.14.0</version > <configuration > <complianceLevel > 1.8</complianceLevel > <source > 8</source > <target > 8</target > <showWeaveInfo > true</showWeaveInfo > <verbose > true</verbose > <Xlint > ignore</Xlint > <encoding > UTF-8</encoding > </configuration > <executions > <execution > <goals > <goal > compile</goal > <goal > test-compile</goal > </goals > </execution > </executions > </plugin >
1 2 3 4 5 6 7 8 9 10 @Service public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); public void foo () { log.info("foo()" ); } }
1 2 3 4 5 6 7 8 9 10 11 @Aspect public class MyAspect { private static final Logger log = LoggerFactory.getLogger(MyAspect.class); @Before("execution(* com.hongyi.aspectj_01.service.MyService.foo())") public void before () { log.info("before()" ); } }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication public class Aspectj01Application { private static final Logger log = LoggerFactory.getLogger(Aspectj01Application.class); public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Aspectj01Application.class, args); MyService service = context.getBean(MyService.class); log.info("service class: {}" , service.getClass()); service.foo(); context.close(); } }
打印结果:
查看反编译后的MyService
的class
文件,发现源码中多了一行由AspectJ
添加的方法:
1 2 3 4 5 6 7 8 9 10 11 12 @Service public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); public MyService () { } public void foo () { MyAspect.aspectOf().before(); log.info("foo()" ); } }
切面类并没有被容器管理,将容器相关的测试代码删除后再进行测试:
1 2 3 4 5 6 public class Aspectj01Application { private static final Logger log = LoggerFactory.getLogger(Aspectj01Application.class); public static void main (String[] args) { new MyService().foo(); } }
打印结果:
依然能够进行方法增强。
9.2 Agent 在类加载阶段 修改代码来增强方法。
9.3 Proxy 9.3.1 JDK动态代理 可参考[[Java设计模式学习笔记新#5.1.4 JDK动态代理]]
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class JdkProxyDemo { interface Foo { void foo () ; } static class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } } public static void main (String[] args) { Target target = new Target(); Foo proxyInstance = (Foo) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args1) -> { System.out.println("before..." ); Object result = method.invoke(target, args1); System.out.println("after..." ); return result; } ); proxyInstance.foo(); } }
执行结果:
1 2 3 before... target foo after...
目标类和代理类是平级的兄弟关系,相同点在于他们都实现了目标类的接口。
9.3.2 CGLIB动态代理 参见[[Java设计模式学习笔记新#5.1.5 CGLIB动态代理]]
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class CglibProxyDemo { static class Target { public void foo () { System.out.println("target foo" ); } } public static void main (String[] args) { Target target = new Target(); Target proxyInstance = (Target) Enhancer.create(Target.class, (MethodInterceptor) (proxy, method, args1, methodProxy) -> { System.out.println("before..." ); Object result = method.invoke(target, args1); System.out.println("after..." ); return result; }); proxyInstance.foo(); } }
目标类和代理类是父子关系。
如果目标类是final
,则不能使用cglib
来代理。
如果目标类的方法是final
,也会代理失败(因为代理类是通过重写父类的方法来进行增强的)。
此外:methodProxy
可以避免反射来调用目标的方法
1 2 3 4 5 6 7 8 Object result = methodProxy.invoke(target, args1); Object result = methodProxy.invokeSuper(proxy, args1);
9.4 JDK动态代理原理 下面模拟实现JDK的动态代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class A13 { interface Foo { void foo () ; int bar () ; } static class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } @Override public int bar () { System.out.println("target bar" ); return 100 ; } } interface InvocationHandler { Object invoke (Object proxy, Method method, Object[] args) throws Throwable ; } public static void main (String[] args) { Foo proxy0 = new $Proxy0((proxy, method, args1) -> { System.out.println("before..." ); return method.invoke(new Target(), args1); }); proxy0.foo(); proxy0.bar(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class $Proxy0 implements Foo { private final InvocationHandler h; public $Proxy0(InvocationHandler h) { this .h = h; } @Override public void foo () { try { h.invoke(this , foo, new Object[0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public int bar () { try { Object result = h.invoke(this , bar, new Object[0 ]); return (int ) result; } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } static Method foo; static Method bar; static { try { foo = Foo.class.getMethod("foo" ); bar = Foo.class.getMethod("bar" ); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } }
执行结果:
1 2 3 4 before... target foo before... target bar
9.5 CGLIB动态代理原理 9.5.1 模拟实现 下面模拟实现CGLIB的动态代理。
MethodInterceptor
接口:和JDK动态代理的InvocationHandler类似
1 2 3 public interface MethodInterceptor { Object intercept (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Target { public void save () { System.out.println("save()" ); } public void save (int i) { System.out.println("save(int)" ); } public void save (long j) { System.out.println("save(long)" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class Proxy extends Target { private final MethodInterceptor methodInterceptor; public Proxy (MethodInterceptor methodInterceptor) { this .methodInterceptor = methodInterceptor; } static Method save0; static Method save1; static Method save2; static { try { save0 = Target.class.getMethod("save" ); save1 = Target.class.getMethod("save" , int .class); save2 = Target.class.getMethod("save" , long .class); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } @Override public void save () { try { methodInterceptor.intercept(this , save0, new Object[0 ], null ); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public void save (int i) { try { methodInterceptor.intercept(this , save1, new Object[]{i}, null ); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public void save (long j) { try { methodInterceptor.intercept(this , save2, new Object[]{j}, null ); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } }
1 2 3 4 5 6 7 8 9 10 11 public class A14 { public static void main (String[] args) { Proxy proxy = new Proxy((o, method, args1, methodProxy) -> { System.out.println("before..." ); return method.invoke(new Target(), args1); }); proxy.save(); proxy.save(1 ); proxy.save(1L ); } }
运行结果:
1 2 3 4 5 6 before... save() before... save(int) before... save(long)
9.5.2 MethodProxy CGLIB的MethodInterceptor
的方法intercept
相比于JDK的InvocationHandler
接口的invoke
方法参数多了一个MethodProxy
,它能避免使用反射来执行目标对象的方法。
9.6 Spring选择代理 9.6.1 Spring的代理选择规则 两个切面的概念:
aspect
:该切面是通知(advice
)和切点(pointcut
)的集合,例如通知1和切点1,通知2和切点2等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Aspect public class MyAspect { @Before("execution(* foo())") public void before () { System.out.println("前置增强" ); } @After("execution(* foo())") public void after () { System.out.println("后置增强" ); } }
advisor
:该切面是更细粒度的切面,只包含一个通知和切点。
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class A15 { public static void main (String[] args) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())" ); MethodInterceptor advice = invocation -> { System.out.println("before..." ); Object result = invocation.proceed(); System.out.println("after..." ); return result; }; DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice); Target2 target = new Target2(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); Target2 proxy = (Target2) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar(); } interface I1 { void foo () ; void bar () ; } static class Target1 implements I1 { @Override public void foo () { System.out.println("target1 foo" ); } @Override public void bar () { System.out.println("target1 bar" ); } } static class Target2 { public void foo () { System.out.println("target2 foo" ); } public void bar () { System.out.println("target2 bar" ); } } }
执行结果:
1 2 3 4 5 com.hongyi.apiintegration.spring.a15.A15$Target2$$EnhancerBySpringCGLIB$$e9831691 // CGLIB代理 before... target2 foo after... target2 bar
1 2 3 4 5 6 7 8 9 10 Target1 target = new Target1(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); I1 proxy = (I1) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar();
打印结果:
1 2 3 4 5 class com.hongyi.apiintegration.spring.a15.$Proxy0 // JDK代理 before... target1 foo after... target1 bar
1 2 3 4 5 6 7 8 9 10 11 Target1 target = new Target1(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); factory.setProxyTargetClass(true ); I1 proxy = (I1) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar();
打印结果:
1 2 3 4 5 classcom.hongyi.apiintegration.spring.a15.A15$Target1$$EnhancerBySpringCGLIB$$8ccfe76e before... target1 foo after... target1 bar
总结
Spring创建代理的选择规则:
proxyTargetClass=false
:目标实现了接口,则Spring采用JDK来创建代理
proxyTargetClass=false
:目标没有实现接口,则Spring采用CGLIB来创建代理
proxyTargetClass=true
:Spring采用CGLIB来创建代理
9.6.2 切点匹配 SpringAOP通过切点表达式 来进行切点的匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class A16 { public static void main (String[] args) throws NoSuchMethodException { AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut(); pt1.setExpression("execution(* bar())" ); System.out.println(pt1.matches(T1.class.getMethod("foo" ), T1.class)); System.out.println(pt1.matches(T1.class.getMethod("bar" ), T1.class)); AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut(); pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)" ); System.out.println(pt2.matches(T1.class.getMethod("foo" ), T1.class)); System.out.println(pt2.matches(T1.class.getMethod("bar" ), T1.class)); } static class T1 { @Transactional public void foo () { } public void bar () { } } }
但这种方式不能匹配在类上或接口上的注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class A16 { public static void main (String[] args) throws NoSuchMethodException { StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() { @Override public boolean matches (Method method, Class<?> targetClass) { MergedAnnotations annotations = MergedAnnotations.from(method); if (annotations.isPresent(Transactional.class)) { return true ; } annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY); return annotations.isPresent(Transactional.class); } }; System.out.println(pt3.matches(T1.class.getMethod("foo" ), T1.class)); System.out.println(pt3.matches(T1.class.getMethod("bar" ), T1.class)); System.out.println(pt3.matches(T2.class.getMethod("foo" ), T2.class)); System.out.println(pt3.matches(T3.class.getMethod("foo" ), T3.class)); } static class T1 { @Transactional public void foo () { } public void bar () { } } @Transactional static class T2 { public void foo () { } } @Transactional interface I3 { void foo () ; } static class T3 implements I3 { @Override public void foo () { } } }
总结:底层切点的实现调用了aspectj
的匹配方法
9.6.3 两种切面
底层实现中需要将高级切面转换为低级切面。
切面的识别和转换依赖于AnnotationAwareAspectJAutoProxyCreator
。它是一个Bean后处理器,在依赖注入之前和初始化之后执行。
作用:
该类有两个重要方法:
findEligibleAdvisors
:找到有资格的Advisors(切面)
一部分切面是低级的,可以自己编写
一部分切面是高级的,需要对@Aspect
进行解析后获得
wrapIfNecessary
:内部调用了findEligibleAdvisors
,只要返回集合不为空,就表示需要创建代理
9.6.4 代理对象的创建时机