代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian

1.源码分析二主要分析的内容

1.使用@Condition多条件注册bean对象
2.@Import注解快速注入第三方bean对象
3.@EnableXXXX 开启原理
4.基于ImportBeanDefinitionRegistrar注册bean
5.基于FactoryBean注册bean对象

1.使用@Conditional多条件注册bean对象

conditional字面意思条件句,亦即满足某些条件将该类注册到IOC容器的意思

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional { /**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value(); }

可以看到 Conditional注解的value是Condition的子类或实现类

我这里写自定义的BriancCondition类来实现Condition接口,可以看到Condition就一个返回boolean值的mathes()方法

public class BrianCondition implements Condition {

    /*
* context:判断条件能使用的上下文(环境)
* metadata: 注释信息
* */
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { System.out.println("---male:" + context.getRegistry().containsBeanDefinition("person"));
     //我这里判断ioc容器中是否有person实例,有返回true,否则返回false
if(context.getRegistry().containsBeanDefinition("person"))
return true; return false;
}
}

现在在配置类中加上,注册Person类到容器中,然后用@Conditional控制是否将person01和person02注册到容器中

@Configuration //告诉spring这是一个配置类
/*
* @ComponentScan
* value:只当于扫描的的包
* excludeFilters = 指定扫描的时候按照什么规则排除哪些组件
* includeFilters = 指定扫描的时候只需要包含哪些组件
* Filter.ANNOTATION:按照注解
* Filter.ASSIGNABLE_TYPE: 按照给定的类型
* */ @ComponentScans(value = {
@ComponentScan(value = "com.brian",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
// @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class})
},useDefaultFilters = false)
})
//@Import({Brian.class,Alan.class,BrianSelector.class})
public class MainConfig { @Bean("person") //给容器中注册一个Bean;类型为返回值的类型;id默认是方法名作为id
public Person person(){
return new Person("Alan",18);
} /*
* @Conditional() 按照条件注册
*
* */
@Conditional({BrianCondition.class})
@Bean("person01")
public Person person01() {
return new Person("Brian",17);
} @Conditional({BrianCondition.class})
@Bean("person02")
public Person person02() {
return new Person("wenTao",19);
} /*
*
*给容器中注册组件
* 1,包扫描+ 组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的方法]
* 2, @Bean [导入的第三方包里面的组件]
* 3,@Import [快速的给容器导入一个组件]
* 1.@Import(要导入的组件class)
* 2.ImportSelector:返回需要导入的组件的全类名数组
* 3.ImportBeanDefinitionRegistrar: 手动注册bean到容器
* 4. 使用Spring提供的FactoryBean
* */
@Bean
public BrianBeanFactory brianBeanFactory() {
return new BrianBeanFactory();
} }

加上测试类

public class MainTest {
public static void main(String[] args) {
ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfig.class);
/* ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/
System.out.println("ioc容器创建成功");
// Alan alan1 = acac.getBean(Alan.class);
// Alan alan2 = acac.getBean(Alan.class);
//System.out.println("比较两个Alan实例: " + (alan1 == alan2));
Person person1 = (Person) acac.getBean("person01");
System.out.println("---main---test---person1---: " + person1.toString());
Person person2 = (Person) acac.getBean("person02");
System.out.println("---main---test---person2---: " + person2.toString()); //关闭ioc容器
((AnnotationConfigApplicationContext) acac).close();
}
}

你会发现控制台可以获取到对象person01和person02的信息

我这边再做一个matches发返回false的测试,亦即修改BrianCondition类的matches返回值为false,可以下面的测试结果:NoSuchBeanDefinitionException: No bean named 'person01' available。所i以根据上面我们测试的接口可以知道@Conditional注解的使用也是简单的

2.@Import注解快速注入第三方bean对象

通过@Import可以快速的导入依赖的bean对象,比如我们在配置类上导入其他类@Import({Brian.class,Alan.class})

configure配置类

@Configuration //告诉spring这是一个配置类
/*
* @ComponentScan
* value:只当于扫描的的包
* excludeFilters = 指定扫描的时候按照什么规则排除哪些组件
* includeFilters = 指定扫描的时候只需要包含哪些组件
* Filter.ANNOTATION:按照注解
* Filter.ASSIGNABLE_TYPE: 按照给定的类型
* */ @ComponentScans(value = {
@ComponentScan(value = "com.brian",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
// @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class})
},useDefaultFilters = false)
})
@Import({Brian.class,Alan.class})
//@Import({BrianSelector.class})
public class MainConfig { @Bean("person") //给容器中注册一个Bean;类型为返回值的类型;id默认是方法名作为id
public Person person(){
return new Person("Alan",18);
} /*
* @Conditional() 按照条件注册
*
* */
@Conditional({BrianCondition.class})
@Bean("person01")
public Person person01() {
return new Person("Brian",17);
} @Conditional({BrianCondition.class})
@Bean("person02")
public Person person02() {
return new Person("wenTao",19);
} /*
*
*给容器中注册组件
* 1,包扫描+ 组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的方法]
* 2, @Bean [导入的第三方包里面的组件]
* 3,@Import [快速的给容器导入一个组件]
* 1.@Import(要导入的组件class)
* 2.ImportSelector:返回需要导入的组件的全类名数组
* 3.ImportBeanDefinitionRegistrar: 手动注册bean到容器
* 4. 使用Spring提供的FactoryBean
* */
@Bean
public BrianBeanFactory brianBeanFactory() {
return new BrianBeanFactory();
} }

测试类


public class MainTest {
public static void main(String[] args) {
ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfig.class); /* ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/
System.out.println("ioc容器创建成功");
Alan alan1 = acac.getBean(Alan.class);
System.out.println("--ALAN--:" + alan1);
// Alan alan2 = acac.getBean(Alan.class);
//System.out.println("比较两个Alan实例: " + (alan1 == alan2)); // Person person1 = (Person) acac.getBean("person01");
// System.out.println("---main---test---person1---: " + person1.toString());
// Person person2 = (Person) acac.getBean("person02");
// System.out.println("---main---test---person2---: " + person2.toString()); // MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator");
// System.out.println("----get--mathCalculator---: " + mathCalculator); //关闭ioc容器
((AnnotationConfigApplicationContext) acac).close();
}
}
 

@Import上面的使用方式属于静态的导入依赖,当然Import注解还有一种动态导入第三组件的方式是和ImportSelector结合使用

比如我在这里MainConfig配置类上通过Import注解导入BrianSelector类.

@Configuration //告诉spring这是一个配置类
/*
* @ComponentScan
* value:只当于扫描的的包
* excludeFilters = 指定扫描的时候按照什么规则排除哪些组件
* includeFilters = 指定扫描的时候只需要包含哪些组件
* Filter.ANNOTATION:按照注解
* Filter.ASSIGNABLE_TYPE: 按照给定的类型
* */ @ComponentScans(value = {
@ComponentScan(value = "com.brian",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
// @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class})
},useDefaultFilters = false)
})
//@Import({Brian.class,Alan.class})
@Import({BrianSelector.class})
public class MainConfig { @Bean("person") //给容器中注册一个Bean;类型为返回值的类型;id默认是方法名作为id
public Person person(){
return new Person("Alan",18);
} /*
* @Conditional() 按照条件注册
*
* */
@Conditional({BrianCondition.class})
@Bean("person01")
public Person person01() {
return new Person("Brian",17);
} @Conditional({BrianCondition.class})
@Bean("person02")
public Person person02() {
return new Person("wenTao",19);
} /*
*
*给容器中注册组件
* 1,包扫描+ 组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的方法]
* 2, @Bean [导入的第三方包里面的组件]
* 3,@Import [快速的给容器导入一个组件]
* 1.@Import(要导入的组件class)
* 2.ImportSelector:返回需要导入的组件的全类名数组
* 3.ImportBeanDefinitionRegistrar: 手动注册bean到容器
* 4. 使用Spring提供的FactoryBean
* */
@Bean
public BrianBeanFactory brianBeanFactory() {
return new BrianBeanFactory();
} }

BrianSelector类,该类实现了ImportSelector接口,通过实现selectImports方法,返回需要动态导入到IOC容器的其他的配置类的全量类名

/*
* 自定义返回需要导入的组件
* */
public class BrianSelector implements ImportSelector { /**
*
* @param importingClassMetadata 当前被标记有@Import注解的所有注解信息
* @return
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) { System.out.println("----ImportSelector----:"+importingClassMetadata.getClassName());
//return new String[]{};
return new String[]{MainConfigOfAOP.class.getName()};
}
}

MainConfigOfAOP配置类有注入MathCalculator对象

@Configuration
ublic class MainConfigOfAOP { @Bean
public MathCalculator mathCalculator()
{
return new MathCalculator();
} }

测试类,主要测试在IOC容器中获取MathCalculator类的信息

public class MainTest {
public static void main(String[] args) {
ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfig.class); /* ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/
System.out.println("ioc容器创建成功");
// Alan alan1 = acac.getBean(Alan.class);
// System.out.println("--ALAN--:" + alan1);
// Alan alan2 = acac.getBean(Alan.class);
//System.out.println("比较两个Alan实例: " + (alan1 == alan2)); // Person person1 = (Person) acac.getBean("person01");
// System.out.println("---main---test---person1---: " + person1.toString());
// Person person2 = (Person) acac.getBean("person02");
// System.out.println("---main---test---person2---: " + person2.toString()); MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator");
System.out.println("----get--mathCalculator---: " + mathCalculator); //关闭ioc容器
((AnnotationConfigApplicationContext) acac).close();
}
}

这里简单的扩展下@Import注解和@Bean注解异同点
1.都是导入的外部的Jar包
2.@Import的bean id是当前完整路径地址注册到IOC容器,@Bean的bean id是以方法名注册到IOC容器,相比来说@Import注入类更加简单

3.@EnableXXXX 开启原理

enable字面意思启动,亦即开关的概念,有@EnableXXXX注解的地方,基本会看到@Import这个注解,一般他们都是结合起来使用的

比如看到我代码里面的MainConfigOfAutowired这个配置类,上有加上@EnableTransactionManagement这个注解,亦即打开事务管理

/**
* 自动装配
* Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
*1).@Autowired,自动注入:
* 1.默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);
* 2.如果找到多个相同类型的组件,再将属性方法的名称作为组件的id去容器中查找
* applicationContext.getBean("bookDao");
* 3.@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件id,而不是使用属性名
* 4.自动装配默认一定要将属性赋值好,没有就会报错
* 使用@Autoeired(required=false),没有默认值也不会报错
* 5.@Primary, 让Spring进行自动装配的时候,默认使用首先的Bean
*
* 2).Spring还支持使用@Resource(JSR250)和@Inject(JSR330) [java规范的注解]
* 3).@Autowired :构造器,参数,方法,属性,
*
*/
@EnableAspectJAutoProxy //开启AOP代理自动配置
@EnableTransactionManagement //基于注解的事务管理
//@ComponentScan(value = {"com.brian.bean","com.write.annotation"})
@ComponentScan(value = {"com.write.annotation.transaction"})
@Configuration
public class MainConfigOfAutowired { @Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://remotemysql.com:3306/khgvUiO4eh");

我们再看看@EnableTransactionManagement的代码,通过Import快速导入TransactionManagementConfigurationSelector

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement { /**
* Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
* opposed to standard Java interface-based proxies ({@code false}). The default is
* {@code false}. <strong>Applicable only if {@link #mode()} is set to
* {@link AdviceMode#PROXY}</strong>.
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Transactional}. For example, other beans marked with Spring's
* {@code @Async} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
*/
boolean proxyTargetClass() default false; /**
* Indicate how transactional advice should be applied.
* <p><b>The default is {@link AdviceMode#PROXY}.</b>
* Please note that proxy mode allows for interception of calls through the proxy
* only. Local calls within the same class cannot get intercepted that way; an
* {@link Transactional} annotation on such a method within a local call will be
* ignored since Spring's interceptor does not even kick in for such a runtime
* scenario. For a more advanced mode of interception, consider switching this to
* {@link AdviceMode#ASPECTJ}.
*/
AdviceMode mode() default AdviceMode.PROXY; /**
* Indicate the ordering of the execution of the transaction advisor
* when multiple advices are applied at a specific joinpoint.
* <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
*/
int order() default Ordered.LOWEST_PRECEDENCE; }

我们再点进去看看TransactionManagementConfigurationSelector这个类,会发现selectImports会根据条件,选择不同的配置类,所以这就是为什么说ImportSelector可以动态加载其他配置类了

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    /**
* Returns {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
* and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
* respectively.
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
} private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
} }

4.基于ImportBeanDefinitionRegistrar注册bean

再回到MainConfigOfAOP这个配置类

/**
* AOP: [动态代理]
* 指在程序运行时期间将某段代码切入到指定方法指定位置执行的编程方式
*
* 1.将业务逻辑类和切面类注入到容器中(加上@Aspect注解表示切面类 )
* 2.在切面类上的每个通知方法注解上注解,定义好切点
* 3.开启基于注解的AOP模式: @EnableAspectAutoProxy
*
*
* AOP 原理:
* @EnableAspectJAutoProxy
* @Import(AspectJAutoProxyRegistrar.class) 给容器中导入AspectJAutoProxyRegistrar类
* 利用AspectJAutoProxyRegistrar自定义向容器中注册bean
* AnnotationAwareAspectJAutoProxyCreator
* ->AspectJAwareAdvisorAutoProxyCreator
* ->AbstractAdvisorAutoProxyCreator
* ->AbstractAutoProxyCreator
* implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
* 后置处理器(在bean初始化完成前后执行) ,自动装配BeanFactory
*
*
*
*/
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP { @Bean
public MathCalculator mathCalculator()
{
return new MathCalculator();
} @Bean
public LogAspects logAspects() {
return new LogAspects();
}
}

上面通过@EnableAspectJAutoProxy开启基于注解的AOP模式,我们点进去看看,又是熟悉的@Import注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy { /**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false; /**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false; }

我们再点进去看看AspectJAutoProxyRegistrar这个类

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    /**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
} }

你会发现该类通过实现ImportBeanDefinitionRegistrar接口的registerBeanDefinitions的方法,最终通过AopConfigUtil工具类来注册到容器中

5.基于FactoryBean注册bean对象

再次回到我的MainConfig这个配置类

@Configuration //告诉spring这是一个配置类
/*
* @ComponentScan
* value:只当于扫描的的包
* excludeFilters = 指定扫描的时候按照什么规则排除哪些组件
* includeFilters = 指定扫描的时候只需要包含哪些组件
* Filter.ANNOTATION:按照注解
* Filter.ASSIGNABLE_TYPE: 按照给定的类型
* */ @ComponentScans(value = {
@ComponentScan(value = "com.brian",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
// @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class})
},useDefaultFilters = false)
})
//@Import({Brian.class,Alan.class})
@Import({BrianSelector.class})
public class MainConfig { @Bean("person") //给容器中注册一个Bean;类型为返回值的类型;id默认是方法名作为id
public Person person(){
return new Person("Alan",18);
} /*
* @Conditional() 按照条件注册
*
* */
@Conditional({BrianCondition.class})
@Bean("person01")
public Person person01() {
return new Person("Brian",17);
} @Conditional({BrianCondition.class})
@Bean("person02")
public Person person02() {
return new Person("wenTao",19);
} /*
*
*给容器中注册组件
* 1,包扫描+ 组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的方法]
* 2, @Bean [导入的第三方包里面的组件]
* 3,@Import [快速的给容器导入一个组件]
* 1.@Import(要导入的组件class)
* 2.ImportSelector:返回需要导入的组件的全类名数组
* 3.ImportBeanDefinitionRegistrar: 手动注册bean到容器
* 4. 使用Spring提供的FactoryBean
* */
@Bean
public BrianBeanFactory brianBeanFactory() {
return new BrianBeanFactory();
} }

通过@Bean注解注入BrianBeanFactory,我们点进去看看

public class BrianBeanFactory  implements FactoryBean<WenTao> {
//获取对象
public WenTao getObject() throws Exception {
return new WenTao();
}
//获取对象的类型
public Class<?> getObjectType() {
return WenTao.class;
} //获取对象是单例模式还是原型模式
public boolean isSingleton() {
return true;
}
}

BrianBeanFactoryt通过实现了BeanFactory接口的getObject()获取到bean对象

上测试类

public class MainTest {
public static void main(String[] args) {
ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfig.class); /* ApplicationContext acac =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);*/
System.out.println("ioc容器创建成功");
// Alan alan1 = acac.getBean(Alan.class);
// System.out.println("--ALAN--:" + alan1);
// Alan alan2 = acac.getBean(Alan.class);
//System.out.println("比较两个Alan实例: " + (alan1 == alan2)); // Person person1 = (Person) acac.getBean("person01");
// System.out.println("---main---test---person1---: " + person1.toString());
// Person person2 = (Person) acac.getBean("person02");
// System.out.println("---main---test---person2---: " + person2.toString()); // MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator");
// System.out.println("----get--mathCalculator---: " + mathCalculator); BrianBeanFactory beanFactory = acac.getBean(BrianBeanFactory.class);
WenTao wentao = null;
try {
wentao = beanFactory.getObject();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("----get--WenTao---: " + wentao); //关闭ioc容器
((AnnotationConfigApplicationContext) acac).close();
}
}

这里拓展一点FactoryBean和 BeanFactory的区别,FactoryBean是创建bean对象,BeanFactory是获取bean对象。

最后说一下,我的博客可能不是首创,但也属于我自己根据自己理解一点点分析的,如果有帮助到你,转载请注明出处!

Spring5源码深度分析(二)之理解@Conditional,@Import注解的更多相关文章

  1. Spring5源码深度解析(一)之理解Configuration注解

    代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian 1.Spring体系结构 ...

  2. libevent源码深度剖析二

    libevent源码深度剖析二 ——Reactor模式 张亮 前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个 ...

  3. spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理

    @Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供 ...

  4. spring5 源码深度解析----- Spring事务 是怎么通过AOP实现的?(100%理解Spring事务)

    此篇文章需要有SpringAOP基础,知道AOP底层原理可以更好的理解Spring的事务处理. 自定义标签 对于Spring中事务功能的代码分析,我们首先从配置文件开始人手,在配置文件中有这样一个配置 ...

  5. 惊人!Spring5 AOP 默认使用Cglib ?从现象到源码深度分析

    Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...

  6. spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)

    面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理. 事务的介绍 1.数据库事物特性 原子性多个数据库操作是不可分割的,只有所有的操作 ...

  7. spring5 源码深度解析----- 事务增强器(100%理解事务)

    上一篇文章我们讲解了事务的Advisor是如何注册进Spring容器的,也讲解了Spring是如何将有配置事务的类配置上事务的,实际上也就是用了AOP那一套,也讲解了Advisor,pointcut验 ...

  8. spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)

    上一篇文章讲解了获取事务,并且通过获取的connection设置只读.隔离级别等,这篇文章讲解剩下的事务的回滚和提交 回滚处理 之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的无非 ...

  9. ThreadLocal的原理,源码深度分析及使用

    文章简介 ThreadLocal应该都比较熟悉,这篇文章会基于ThreadLocal的应用以及实现原理做一个全面的分析 内容导航 什么是ThreadLocal ThreadLocal的使用 分析Thr ...

随机推荐

  1. 树莓派挂载移动硬盘开启samba

    本文参考 [https://blog.csdn.net/u010906068/article/details/38455363],原文部分步骤在我的树莓派上,可能是版本不同吧,进行了修改后部署成功 一 ...

  2. element-ui 自定义表单验证 , 但是不出现小红心了

    基本上按照文档上提供的方式做就没啥大问题 , 我遇到的问题是 , 自定义以后不显示小红星了 <el-form :model="ruleForm2" status-icon : ...

  3. ddraw 视频下绘图 不闪烁的方法

    我们假设是在在RGB视频上绘图(直线,矩形等),一般採用双缓冲区继续,使用内存MemoryDC,来实现画的图形在视频上显示不闪烁的功能,可是我们知道用RGB显示视频都是使用GDI进行渲染,这样非常耗C ...

  4. OpenGL(二十一) glPolygonOffset设置深度偏移解决z-fighting闪烁问题

    开启深度测试后OpenGL就不会再去绘制模型被遮挡的部分,这样实现的显示画面更为真实,但是由于深度缓冲区精度的限制,对于深度相差非常小的情况(例如在同一平面上进行两次绘制),OpenGL就不能正确判定 ...

  5. JS中常用函数总结

    //LTrim(string):去除左边的空格 function LTrim(str) { var whitespace = new String(" \t\n\r"); var ...

  6. 微信小程序特殊效果合集

    微信小程序的开发并不难,但是有时我们想做出比较炫的效果,可能会一时没有思路或找不到方法.下面就整理了微信小程序的一些特殊效果的案例,均来自网络,供你参考. 1.文字跑马灯效果:http://www.w ...

  7. 解决MacOS下readlink: illegal option -- f

    时间: 2017.03.21 分类: [操作系统] 评论 Mac下的readlink没有-f参数,诸如screenfetch又会去调用readlink -f,于是每次都会出现: readlink: i ...

  8. UVA - 825Walking on the Safe Side(dp)

    id=19217">称号: UVA - 825Walking on the Safe Side(dp) 题目大意:给出一个n * m的矩阵.起点是1 * 1,终点是n * m.这个矩阵 ...

  9. glibc 内存申请和释放及堆连续检查

    C语言有两种内存申请方式: 1.静态申请:当你声明全局或静态变量的时候,会用到静态申请内存.静态申请的内存有固定的空间大小.空间只在程序开始的时候申请一次,并且不再释放(除非程序结束). 2.自动申请 ...

  10. Linux IO模式

    原文地址:https://segmentfault.com/a/1190000003063859 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案 ...