Spring5源码深度分析(二)之理解@Conditional,@Import注解
代码地址: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注解的更多相关文章
- Spring5源码深度解析(一)之理解Configuration注解
代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian 1.Spring体系结构 ...
- libevent源码深度剖析二
libevent源码深度剖析二 ——Reactor模式 张亮 前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个 ...
- spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
@Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供 ...
- spring5 源码深度解析----- Spring事务 是怎么通过AOP实现的?(100%理解Spring事务)
此篇文章需要有SpringAOP基础,知道AOP底层原理可以更好的理解Spring的事务处理. 自定义标签 对于Spring中事务功能的代码分析,我们首先从配置文件开始人手,在配置文件中有这样一个配置 ...
- 惊人!Spring5 AOP 默认使用Cglib ?从现象到源码深度分析
Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...
- spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)
面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理. 事务的介绍 1.数据库事物特性 原子性多个数据库操作是不可分割的,只有所有的操作 ...
- spring5 源码深度解析----- 事务增强器(100%理解事务)
上一篇文章我们讲解了事务的Advisor是如何注册进Spring容器的,也讲解了Spring是如何将有配置事务的类配置上事务的,实际上也就是用了AOP那一套,也讲解了Advisor,pointcut验 ...
- spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)
上一篇文章讲解了获取事务,并且通过获取的connection设置只读.隔离级别等,这篇文章讲解剩下的事务的回滚和提交 回滚处理 之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的无非 ...
- ThreadLocal的原理,源码深度分析及使用
文章简介 ThreadLocal应该都比较熟悉,这篇文章会基于ThreadLocal的应用以及实现原理做一个全面的分析 内容导航 什么是ThreadLocal ThreadLocal的使用 分析Thr ...
随机推荐
- MSVC编译Boost的几种链接方式
折腾了好几个小时,终于理清了Boost链接的组合方式,记录一下. A1.动态链接Boost的动态库A2.静态链接Boost的动态库 B1.动态链接VC运行库B2.静态链接VC运行库 那么这样就有2x2 ...
- 【转】eth0 no such device(reload)
[转自:http://blog.chinaunix.net/uid-25554408-id-292638.html 北国的春的ChinaUnix博客] 今天我在vmware里安装了虚拟机,安装虚拟机就 ...
- Python属性和方法
关键字:Python 属性 方法原文:http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and ...
- 由Maximum Gap,对话桶排序,基数排序和统计排序
一些非比较排序 在LeetCode中有个题目叫Maximum Gap.是求一个非排序的正数数列中按顺序排列后的最大间隔.这个题用桶排序和基数排序都能够实现.以下说一下桶排序.基数排序和计数排序这三种非 ...
- 图解Http协议 url长度限制
http请求报文的格式 一般请求所带有的属性: http响应报文的格式: 响应首部一般包含如下内容: 一.技术基石及概述 问:什么是HTTP? 答:HTTP是一个客户端和服务器端请求和响应的标准TCP ...
- 如何构造请求处理对象链(Pipeline)
在开发中,我们经常会遇到这样一个场景:传入一个对象,经过不同的节点对这个对象做不同的操作,比如ASP.NET Core 中的pipeline,IIS中的HTTPpipeline等.在这类问题中,往往我 ...
- 文件上传(bootstrap fileinput)
在做Excel文件上传和下载时,原生文件输入框太不美观,从网上找的bootstrap fileinput还是挺漂亮的, 需要引用的文件 //4.fileUpload bundles.Add(new S ...
- 微信小程序支付结果 c#后台回调
又为大家带来简单的c#后台支付结果回调方法,首先还是要去微信官网下载模板(WxPayAPI),将模板(WxPayAPI)添加到服务器上,然后在打开WxPayAPI项目中的example文件下的 Nat ...
- Android开源项目SlidingMenu本学习笔记(两)
我们已经出台SlidingMenu使用:Android开源项目SlidingMenu本学习笔记(一个),接下来再深入学习下.依据滑出项的Menu切换到相应的页面 文件夹结构: watermark/2/ ...
- 消息队列中点对点与发布订阅区别(good)
背景知识 JMS一个在 Java标准化组织(JCP)内开发的标准(代号JSR 914).2001年6月25日,Java消息服务发布JMS 1.0.2b,2002年3月18日Java消息服务发布 1.1 ...