前言
小编在spring的beanPostProcessor详解以及模拟Aop中讲到springBean创建的最后一步调用BeanPostProcessor的postProcessAfterInitialization来进行Aop的处理,Aop的模拟是使用了JDK的动态代理,这次小编使用cglib来实现。
问题
spring在面试的时候总是会问到AOP的原理,事务的原理等,然后继续会问动态代理的实现以及场景?这些相信大家都会容易回答,但是大家有具体看过cglib到底是怎么产生一个代理对象的吗,接下来跟着小编一起使用cglib来创建一个代理对象实现Aop吧。
代码示例
这边小编写了个简单示例,大家一起看下代码
测试类
@ComponentScan("com.xxx.xxx.cglibaop")
public class AopTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AopTest.class);
AopService bean = applicationContext.getBean(AopService.class);
bean.forkAopMethod();
}
}
需要代理的目标对象
@Component
public class AopService {
public void forkAopMethod() {
System.out.println("fork aop method impl......");
}
}
Cglib模拟实现Aop类
@Component
public class AopBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?>[] interfaces = bean.getClass().getInterfaces();
Object beanProxy = bean;
if(AopService.class.getSimpleName().equals(bean.getClass().getSimpleName())){
Enhancer enhancer = createEnhancer(bean);
beanProxy = enhancer.create();
}
return beanProxy;
}
private Enhancer createEnhancer(Object bean) {
// createCGLIB Enhancer...
Enhancer enhancer = new Enhancer();
enhancer.setUseFactory(false);
//增强的那个目标类
enhancer.setSuperclass(bean.getClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
//类似与jdk动态代理的invocationHandler的调用方法
//源码中为enhancer.setCallbacks 和enhancer.setCallbackFilter这样实现比较复杂小编偷懒模拟一个
//这里使用的是Callback的子接口MethodInterceptor实现方法,其他还有很多大家可以直接研究
enhancer.setCallback(new MyCallBack());
return enhancer;
}
class MyCallBack implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib enhance before method");
//目标类对象的方法调用
Object invokeSuper = methodProxy.invokeSuper(o, objects);
System.out.println("cglib enhance after method");
return invokeSuper;
}
}
}
测试打印结果
cglib enhance before method
fork aop method impl......
cglib enhance after method
这里返回的对象就是别cglib代理过的对象。这样就完成了代理。
原理解释
cglib的基本原理图。
上面小编写了一个简单示例,其实源代码中还是比较复杂的,这边简单贴一下源码信息。
org.springframework.aop.framework.CglibAopProxy#getProxy
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
这边小编不深入底层了。大家有兴趣自己研究。
CGLIB使用场景
在将cglib在spring其他地方的使用场景之前,小编写个代码考考大家:
有两个类,分别为TestBean1 和TestBean2,里面只有构造方法。
public class TestBean1 {
public TestBean1(){
System.out.println("---TestBean1--");
}
}
public class TestBean2 {
public TestBean2(){
System.out.println("---TestBean2--");
}
}
测试类
@ComponentScan("com.dtyunxi.yundt.cglibaop")
public class BeanConfigTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(BeanConfig.class);
}
}
以及BeanConfig类
public class BeanConfig {
@Bean
public TestBean1 testBean1(){
return new TestBean1();
}
@Bean
public TestBean2 testBean2(){
testBean1();
return new TestBean2();
}
}
请问TestBean1创建了几次,大家思考10秒啊
测试结果
---TestBean1--
---TestBean1--
---TestBean2--
这边大家是不是和小编一样,本应该单例的TestBean1,却被创建两次
修改以下代码
@Configuration
public class BeanConfig {
@Bean
public TestBean1 testBean1(){
return new TestBean1();
}
@Bean
public TestBean2 testBean2(){
testBean1();
return new TestBean2();
}
}
这样再次测试
---TestBean1--
---TestBean2--
思考代码
这边小编在调用testBean1方法的时候,肯定会创建一次TestBean1,然后调用testBean2方法时又调用一次testBean1方法,则肯定又创建一次TestBean1,然后再创建TestBean2,那上面测试打印是没错的。
那为什么加了@Configuration注解后,测试结果只打印一次TestBean1呢。Spring肯定做了一些事情,从测试代码看,这里实例化BeanConfig 类然后调用@Bean修饰的方法。那BeanConfig 是怎样的呢?小编打了断点看一下。
这边被@Configuration注解后的类实例化后被Cglib给代理了。去掉@Configuration的时候就是原生Bean。
看到这儿Cglib的另一个使用场景就出现了,接着小编讲一下这个原理。
@Configuration注解下实例化对象的原理
这里大家先可以回顾Spring中refresh中invokeBeanFactoryPostProcessors方法源码分析,这里可以看下BeanFactoryPostProcessor的执行顺序,以及spring内置的重要的BeanFactoryPostProcessor的实现类或子接口实现类。这里扫描的主要是ConfigurationClassPostProcessor这个类,调用postProcessBeanFactory这个方法
相关源码:
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory
类上面有详细注释
BeanFactoryPostProcessor used for bootstrapping processing of @Configuration classes.
Registered by default when using context:annotation-config/ or context:component-scan/. Otherwise, may be declared manually as with any other BeanFactoryPostProcessor.
This post processor is priority-ordered as it is important that any Bean methods declared in @Configuration classes have their corresponding bean definitions registered before any other BeanFactoryPostProcessor executes.
上面主要意思如下:
BeanFactoryPostProcessor用于引导@Configuration类的处理。
使用context:annotation-config/或context:component-scan/时默认注册。否则,可以像使用任何其他BeanFactoryPostProcessor一样手动声明。
这个后处理程序是按优先级排序的,因为在@Configuration类中声明的任何Bean方法都必须在任何其他BeanFactoryPostProcessor执行之前注册相应的Bean定义,这一点很重要。
方法上的详细注释
Prepare the Configuration classes for servicing bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
通过用cglib增强的子类替换配置类,为在运行时服务bean请求准备配置类。
这边说的配置类为全配置类,即加了@Configuration,而上面小编没加这个注解也可以是配置类,也可以实例化相应加了@Bean注解的类。
小编稍微贴一下源码做解释
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
//遍历所有被@Configuration注解的bean,并enhance他们做个代理
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any candidates are then enhanced by a ConfigurationClassEnhancer. Candidate status is determined by BeanDefinition attribute metadata.,大家自己翻译啊。
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
//遍历扫描出来的beanDefinition,取出名字
for (String beanName : beanFactory.getBeanDefinitionNames()) {
//获取beanDefiniton
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//获取beanDifiniton 里面属性值
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
//这边为full,全配置类时进入 这就是加了@Configuration注解的类
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
//就会放入map容器中
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty() || IN_NATIVE_IMAGE) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
//遍历全配置类
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
// 把beanDefintion得到的class做个代理类,然后封装进去,当示例化对象的时候
//这个代理类就被实例化了
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}
这边小编在给大家解释一下,为什么加了@Configuration就是全配置类了?
相关源码:
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
//加上全配置类标记
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//第一次拿到的时候没有验证过所以为空
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//否则走验证,接下来看验证代码
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
//最重要代码,如果拿到Configuration这个注解不为空
//config.get("proxyBeanMethods")并且这个是true,大家自己深拔一下
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
//设置全配置类
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
接下来为什么代理对象后只需要创建一次就可以了。为什么不创建两次了。
其实cglib代理后调用里面的方法,都实现了代理,里面会产生对象,也就是说testBean1方法和testBean2方法都产生了代理方法,然后里面有testBean1代理后就已经实例化对象了TestBean1,然后在testBean2方法调用的时候就不需要再次去实例化一次TestBean1,直接返回就行了,所以只打印一次即可。
总结
今天主要给大家介绍cglib的一些应用场景,这边小编总是贴出源码,可能小编打断点也就明白了,但是大家一定要自己消化一下。在讲cglib在@Configuration所修饰类中的应用,同时讲到了以前BeanFactoryPostProcessor中的调用顺序。最后与君共勉加油。下次给大家带来最后的一个主要扩展点EventListener的应用。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13571.html