Spring学习笔记(2)
Spring学习笔记(2)
- 一、Spring配置非定义Bean
- 1.1 DruidDataSource
- 1.2、Connection
- 1.3、Date
- 1.4、SqlSessionFactory
- 二、Bean实例化的基本流程
- 2.1 BeanDefinition
- 2.2 单例池和流程总结
- 三、Spring的bean工厂后处理器
- 3.1 bean工厂后处理器入门
- 3.2、注册BeanDefinition
- 3.3、BeanDefinitionRegistryPostProcessor
- 3.4 完善实例化流程图
- 3.5、自定义@Component
- 四、Spring的Bean后处理器
- 4.1 概念和入门
- 4.2 当中的一些调用方法的顺序
- 4.3 更加详细的实例化基本操作
- 五、Spring的生命周期
- 5.1 概述
- 5.2 初始化阶段执行的步骤
- 5.3 初始化阶段注入属性信息封装
- 5.4、属性注入的三种情况
- 5.4.1 单项注入的代码验证
- 5.4.2 循环依赖概念及解决方案
- 5.4.3 三级缓存的设计原理
- 5.5 循环依赖源码流程剖析
- 5.6 Aware接口
- 六、IoC容器实例化Bean完整流程图展示
- 七、Spring xml方式整和第三方框架
- 7.1 Mybatis整合Spring实现
- 7.2、MyBatis整合Spring源码解析
- 7.3 加载外部properties文件
- 7.4、自定义空间步骤
一、Spring配置非定义Bean
以上在xml中配置的Bean都是自己定义的,例如: UserDaolmpl,UserServicelmpl。但是,在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置
- 配置非自定义的Bean需要考虑如下两个问题:
- 被配置的Bean的实例化方式是什么?无参构造、有参构造、静态工厂方式还是实例化工厂方式;
- 被配置的Bean是否需要注入必要属性
1.1 DruidDataSource
配置Druid数据源交由Spring管理
导入Druid坐标
<!--mysql驱动-->
<dependency><groupId>mysql</groupid><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!-- druid数据源-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.23</version>
</dependency>
配置文件xml
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property><property name="username" value="root"></property><property name="password" value="123456"></property>
</bean>
1.2、Connection
Connection的产生是通过DriverManager的静态方法getConnection获取的,所以我们要用静态工厂方式配置
<bean class="java.lang.class" factory-method="forName"><constructor-arg name="className" value="com.mysql.jdbc.Driver" />
</bean>
<bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype"><constructor-arg name="url" value="jdbc:mysql:///mybatis" /><constructor-arg name="user" value="root"/><constructor-arg name="password" value="123456"/>
</bean>
1.3、Date
产生一个指定日期格式的对象,原始代码如下
String currentTimeStr = "2023-08-27 07:20:00" ;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(currentTimeStr);
可以看成是实例工厂方式,使用Spring配置方式产生Date实例
<bean id="simpleDateFormat" class="java.text.SimpleDateFormat"><constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss" />
</bean>
<bean id="date" factory-bean="simpleDateFormat" factory-method="parse"><constructor-arg name="source" value="2023-08-27 07:20:00"/>
</bean>
1.4、SqlSessionFactory
配置SqlSessionFactory交由Spring管理
导入Mybatis相关坐标
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
<!--静态工厂方法-->
<bean id="inputStream" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream"><constructor-arg name="resource" value="mybatis-config.xml"></constructor-arg>
</bean>
<!--无参构造实例化-->
<bean id="builder" class="org.apache.ibatis.session.SqlSessionFactoryBuilder"></bean>
<!--实例化工厂-->
<bean id="factory" factory-bean="builder" factory-method="build"><constructor-arg name="inputStream" ref="inputStream"></constructor-arg>
</bean>
二、Bean实例化的基本流程
2.1 BeanDefinition
- Spring容器在进行初始化的时候,会将xml配置的
<bean>信息
封装成一个BeanDefinition对象 - 所有的BeanDefinition对象会集中存储在
BeanDefinitionMap
集合当中 - Spring框架会调用读取器读取BeanDdfinitionMap当中的每一个信息,通过反射创建出bean对象,然后再进行初始化操作,最后将所有的数据全部存储到
singletonObjects
集合当中 - 最后通过
getBean
方法在singletonObjects集合当中找到创建的Bean对象
DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap
public class DefaultListableBeanFactory extends ... implements ... {//存储<bean>标签对应的BeanDefinition对象//key:是Bean的beanName,value:是Bean定义对象BeanDefinitionprivate final Map<String,BeanDefinition> beanDefinitionMap;
}
Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息,反射构造方法或调用指定的工厂方法生成Bean实例对象,所以只要将BeanDefinition注册到beanDefinitionMap这个Map中,Spring就会进行对应的Bean的实例化操作
2.2 单例池和流程总结
基本流程:
- 加载xml配置文件,解析获取配置中的每个
<bean>
的信息,封装成一个个的BeanDefinition
对象; - 将BeanDefinition存储在一个名为
beanDefinitionMap
的Map<String,BeanDefinition>
中; - ApplicationContext底层遍历
beanDefinitionMap
,创建Bean实例对象; - 创建好的Bean实例对象,被存储到一个名为singletonObjects的
Map<String,Object>
中; - 当执行applicationContext.getBean(beanName)时,从
singletonObjects
去匹配Bean实例返回。
三、Spring的bean工厂后处理器
3.1 bean工厂后处理器入门
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。
- Spring主要有两种后处理器:
BeanFactoryPostProcessor
: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;BeanPostProcessor
: Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
Bean工厂后处理器-BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范
,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
BeanFactoryPostProcessor定义如下:
public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
3.2、注册BeanDefinition
使用这种方法,可以不用再spring容器内在创建一个类的<bean>
标签
public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("beandefinitionMap填充完毕后回调该方法");//1.注册一个beandefinition 创建一个RootBeanDefinition()对象RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();rootBeanDefinition.setBeanClassName("com.huanglei.Dao.Impl.processorImpl");//2.将beanFactory强转成DefaultListableBeanFactory类型DefaultListableBeanFactory beanFactory1 = (DefaultListableBeanFactory) beanFactory;beanFactory1.registerBeanDefinition("processor",rootBeanDefinition);}
}
@Test
public void demo2(){ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");Object processor = classPathXmlApplicationContext.getBean("processor");System.out.println(processor);
}
3.3、BeanDefinitionRegistryPostProcessor
Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition
操作
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {//创建一个RootBeanDefinition()对象RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();rootBeanDefinition.setBeanClassName("com.huanglei.Dao.Impl.processorImpl");//不需要强转就可以创建一个BeanbeanDefinitionRegistry.registerBeanDefinition("personDao",rootBeanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}
}
3.4 完善实例化流程图
BeanFactoryPostProcessor
在SpringBean的实例化过程中的体现
3.5、自定义@Component
要求:
- 自定义@MyComponent注解,使用在类上;
- 使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;
- 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。
BaseClassScanUtils:
public class BaseClassScanUtils {//设置资源规则private static final String RESOURCE_PATTERN = "/**/*.class";public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {//创建容器存储使用了指定注解的Bean字节码对象Map<String, Class> annotationClassMap = new HashMap<String, Class>();//spring工具类,可以获取指定路径下的全部类ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();try {String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;Resource[] resources = resourcePatternResolver.getResources(pattern);//MetadataReader 的工厂类MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);for (Resource resource : resources) {//用于读取类信息MetadataReader reader = refractory.getMetadataReader(resource);//扫描到的classString classname = reader.getClassMetadata().getClassName();Class<?> clazz = Class.forName(classname);//判断是否属于指定的注解类型if(clazz.isAnnotationPresent(MyComponent.class)){//获得注解对象MyComponent annotation = clazz.getAnnotation(MyComponent.class);//获得属value属性值String beanName = annotation.value();//判断是否为""if(beanName!=null&&!beanName.equals("")){//存储到Map中去annotationClassMap.put(beanName,clazz);continue;}//如果没有为"",那就把当前类的类名作为beanNameannotationClassMap.put(clazz.getSimpleName(),clazz);}}} catch (Exception exception) {}return annotationClassMap;}public static void main(String[] args) {Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.huanglei");System.out.println(stringClassMap);}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {String value();
}
@MyComponent("One")
public class OneBean {
}
四、Spring的Bean后处理器
4.1 概念和入门
Bean被实例化过后,在最后缓存到singletonObjects单例池之前,中间会进行Bean的初始化操作,例如:属性的赋值,初始化操作(init-method),其中包含着一个点BeanPostProcessor
,这里被称为Bean后处理器
。如果实现了这个接口,那么就会被Spring管理,然后Spring会自动调用
public class MyBeanPostProcesser implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+"postProcessBeforeInitialization");return null;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+"postProcessAfterInitialization");return null;}
}
postProcessBeforeInitialization
方法和postProcessAfterInitialization
方法需要进行手动创建,接口中实现的是null返回值的方法- 两者方法在bena创建之后执行,加入到singletonObjects之前执行
4.2 当中的一些调用方法的顺序
- 先执行bean的构造方法
- 执行before方法
- 执行InitializingBean接口中的afterPropertiesSet()方法
- 执行在xml文件中设置的Bean的init-method方法
- 执行after方法
执行操作如下
4.3 更加详细的实例化基本操作
BeanPostProcessor在 SpringBean的实例化过程中的体现
五、Spring的生命周期
5.1 概述
Spring Bean的生命周期是从Bean 实例化之后,即通过反射创建出对象之后,导Bean成为一个完整对象,最终储存到单例池中,这个过程被称为SpringBean的生命周期。
- Spring Bean的生命周期大体上分为三个阶段:
- Bean的实例化阶段: Spring框架会取出
BeanDefinition
的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton
的Bean通过反射
进行实例化 - Bean的初始化阶段∶Beane创建之后还仅仅是个”半成品“,还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行
BeanPostProcessor
方法、执行InitializingBean
接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能
,后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的 - Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池
singletonObjects
中去了,即完成了Spring Bean的整个生命周期。
- Bean的实例化阶段: Spring框架会取出
5.2 初始化阶段执行的步骤
由于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段
Spring Bean的初始化过程涉及如下几个过程:
Bean
实例的属性填充Aware
接口属性注入BeanPostProcessor
的before()方法回调lnitializingBean
接口的初始化方法回调- 自定义初始化方法
init回调
BeanPostProcessor
的after()
方法回调
5.3 初始化阶段注入属性信息封装
BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储
<bean id="userDao" class="com.huanglei.Dao.Impl.UserDaoImpl"></bean><bean id="userService" class="com.huanglei.service.Impl.UserServiceImpl"><property name="dao" ref="userDao"></property><property name="username" value="AAA"></property>
</bean>
5.4、属性注入的三种情况
Spring在进行属性注入时,会分为如下几种情况:
- 注入
普通属性
,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去; - 注入
单向
对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例
(完成整个生命周期)后,在进行注入操作 - 注入
双向
对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。
5.4.1 单项注入的代码验证
public class UserServiceImpl implements UserService{private UserDao dao;private String username;public UserServiceImpl() {System.out.println("实例化UserService");}public UserServiceImpl(UserDao dao, String username) {this.dao = dao;this.username = username;}public void setDao(UserDao dao) {System.out.println("执行setDao方法");this.dao = dao;}public void setUsername(String username) {this.username = username;}
}
public class UserDaoImpl implements UserDao{public UserDaoImpl() {System.out.println("实例化UserDao");}
}
userDao在userService前面
<bean id="userDao" class="com.huanglei.Dao.Impl.UserDaoImpl"></bean><bean id="userService" class="com.huanglei.service.Impl.UserServiceImpl"><property name="dao" ref="userDao"></property><property name="username" value="AAA"></property>
</bean>
执行顺序:
- 实例化UserDao
- 实例化UserService
- 执行setDao方法
userDao在userService后面
<bean id="userService" class="com.huanglei.service.Impl.UserServiceImpl"><property name="dao" ref="userDao"></property><property name="username" value="AAA"></property>
</bean><bean id="userDao" class="com.huanglei.Dao.Impl.UserDaoImpl"></bean>
总结:
就是说明如果包含了我要调用的Bean对象,有就调用,若无就先创建Bean对象
5.4.2 循环依赖概念及解决方案
多个实体之间相互依赖并形成闭环的情况就叫做 “循环依赖”,也叫做 “循环引用”
<bean id="userService" class="com.huanglei.service.Impl.UserServiceImpl"><property name="dao" ref="userDao"></property>
</bean><bean id="userDao" class="com.huanglei.Dao.Impl.UserDaoImpl"><property name="service" ref="userService"></property>
</bean>
public class UserDaoImpl implements UserDao{private UserService service; public void setService(UserService service){this.service = service;}
}
public class UserServiceImpl implements UserService{private UserDao dao;public void setDao(UserDao dao) {this.dao = dao;}
}
可以利用三级缓存来
5.4.3 三级缓存的设计原理
Spring提供了三级缓存
存储完整Bean实例
和半成品Bean实例
,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:
public class DefaultsingletonBeanRegistry ... {//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"Map<String,Object> singletonObjects = new ConcurrentHashMap(256);//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"Map<String,Object> earlySingletonObjects = new ConcurrentHashMap(16);//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"Map<String,ObjectFactory<?>> singletonFactories = new HashMap(16);
}
5.5 循环依赖源码流程剖析
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下
- UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
- UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
- UserDao 实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
- UserDao 属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
- UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
- UserService注入UserDao;
- UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。
5.6 Aware接口
Aware接口是一种框架辅助属性注入
的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到
,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架
提供的类似Aware的接口
,让框架给我们注入该对象
。
Aware接口 | 回调方法 | 作用 |
---|---|---|
ServletContextAware | setServletContext(ServletContext context) | Spring框架回调方法注入ServletContext对象,web环境下才生效 |
BeanFactoryAware | setBeanFactory(BeanFactory factory) | Spring框架回调方法注入beanFactory对象 |
BeanNameAware | setBeanName(String beanName) | Spring框架回调方法注入当前Bean在容器中的beanName |
ApplicationContextAware | setApplicationContext(ApplicationContext applicationContext) | Spring框架回调方法注入applicationContext对象 |
六、IoC容器实例化Bean完整流程图展示
七、Spring xml方式整和第三方框架
- xml整合第三方框架有两种整合方案:
- 不需要自定义名空间,不需要使用Spring的配置文件配置第三方框架本身内容,例如: MyBatis;
- 需要引入第三方框架命名空间,需要使用Spring的配置文件配置第三方框架本身内容,例如:Dubbo。
7.1 Mybatis整合Spring实现
Spring整合MyBatis,之前已经在Spring中简单的配置了SqlSessionFactory,但是这不是正规的整合方式,MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。
Spring整合MyBatis的步骤如下:
- ==导入MyBatis整合Spring的相关坐标; ==
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><!--注意版本,因为版本过低的原因在这里卡了很久!!!--><version>3.0.1</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.25.RELEASE</version>
</dependency>
- 编写Mapper和Mapper.xml;
- 配置SqlSessionFactoryBean和MapperScannerConfigurer;
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itheima"></property><property name="username" value="root"></property><property name="password" value="123456"></property>
</bean>
<!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property>
</bean>
<!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.huanglei.mapper"></property>
</bean><bean id="userService" class="com.huanglei.service.Impl.UserServiceImpl"><property name="carMapper" ref="carMapper"></property>
</bean>
- 编写测试代码
7.2、MyBatis整合Spring源码解析
整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象,SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:
sqlSessionFactoryBean
:需要进行配置,用于提供SqlSessionFactory;MapperScannerConfigurer
:"需要进行配置,用于扫描指定mapper注册BeanDefinition;MapperFactoryBean
: Mapper的FactoryBean,获得指定Mapper时调用getObject方法;ClassPathMapperScanner
: definition.setAutowireMode(2)修改了自动注入状态,所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。
7.3 加载外部properties文件
Spring整合其他组件时就不像MyBatis这么简单了,例如Dubbo框架在于Spring进行整合时,要使用Dubbo提供的命名空间的扩展方式,自定义了一些Dubbo的标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbohttp://dubbo.apache.org/schema/dubbo/dubbo.xsd"><!-- 配置Dubbo应用信息 --><dubbo:application name="your-application-name" /><!-- 配置注册中心 --><dubbo:registry address="zookeeper://127.0.0.1:2181" /><!-- 配置服务提供者 --><dubbo:protocol name="dubbo" port="20880" /><dubbo:service interface="com.example.YourServiceInterface" ref="yourServiceBean" /><!-- 配置其他Bean --><!-- 消费者配置 --><dubbo:consumer check="false" timeout="1000" retries="0"/>
</beans>
通过配置context文件来加载外部properties文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--通过context加载properties文件--><context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器--><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property></bean><!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.huanglei.mapper"></property></bean><bean id="userService" class="com.huamglei.service.Impl.UserServiceImpl"><property name="carMapper" ref="carMapper"></property></bean>
</beans>
7.4、自定义空间步骤
- 将自定义标签的约束与物理约束文件与网络约束名称的约束以键值对形式存储到一个
spring.schemas
文件里,该文件存储在类加载路径的META-INF
里,Spring会自动加载到
; - 将自定义命名空间的名称与自定义命名空间的处理器映射关系以键值对形式存在到一个叫
spring.handlers
文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到; - 准备好NamespaceHandler,如果命名空间只有一个标签,那么直接在
parse
方法中进行解析即可,一般解析结果就是注册该标签对应的BeanDefinitio
n。如果命名空间里有多个标签,那么可以在init方法
中为每个标签都注册一个BeanDefinitionParser
,在执行NamespaceHandler
的parse方法时在分流给不同的
BeanDefinitionParser
进行解析(重写doParse方法即可)。
相关文章:

Spring学习笔记(2)
Spring学习笔记(2) 一、Spring配置非定义Bean1.1 DruidDataSource1.2、Connection1.3、Date1.4、SqlSessionFactory 二、Bean实例化的基本流程2.1 BeanDefinition2.2 单例池和流程总结 三、Spring的bean工厂后处理器3.1 bean工厂后处理器入门3.2、注册Be…...

cmd使用ssh连接Linux脚本
前言 在开发过程中,由于MobaXterm,我不知道怎么分页(不是屏内分页),用crtlTab,用起来不习惯,主要是MobaXterm连接了多个服务器,切换起来很麻烦。我是比较习惯使用altTab,…...

Python万圣节蝙蝠
目录 系列文章 前言 蝙蝠 程序设计 程序分析 运行结果 尾声 系列文章 序号文章目录直达链接1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://want5…...

TCP流套接字编程
文章目录 前言TCP 和 UDP 的特点对比TcpEchoServer 服务端实现1. 创建 ServerSocket 类实现通信双方建立连接2. 取出建立的连接实现双方通信3. 服务端业务逻辑实现关闭资源服务端整体代码 TcpEchoClient 客户端实现1. 创建出 Socket 对象来与服务端实现通信2. 实现客户端的主要…...

Python迭代器创建与使用:从入门到精通
一、可迭代对象 1、 什么是可迭代对象? 表示可以逐一迭代或者遍历的对象,序列:列表、元组、集合、字符串。非序列:字典、文件。自定义对象:实现了__iter__()方法的对象;实现了使用整数索引的 getitem()方…...

mac虚拟机安装homebrew时的问题
安装了mac虚拟机,结果在需要通过“brew install svn”安装svn时,才注意到没有下载安装homebrew。 于是便想着先安装homebrew,网上查的教程大多是通过类似以下命令 “ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)” 但是都会出现…...

学信息系统项目管理师第4版系列32_信息技术发展
1. 大型信息系统 1.1. 大型信息系统是指以信息技术和通信技术为支撑,规模庞大,分布广阔,采用多级 网络结构,跨越多个安全域;处理海量的,复杂且形式多样的数据,提供多种类型应用 的大系统 1.1.…...

Vue3 + Nodejs 实战 ,文件上传项目--大文件分片上传+断点续传
目录 1.大文件上传的场景 2.前端实现 2.1 对文件进行分片 2.2 生成hash值(唯一标识) 2.3 发送上传文件请求 3.后端实现 3.1 接收分片数据临时存储 3.2 合并分片 4.完成段点续传 4.1修改后端 4.2 修改前端 5.测试 博客主页:専心_前端…...

宏(预编译)详解
目录 一、程序的编译环境 二、运行环境 三、预编译详解 3.1预定义符号 3.2.1 #define 定义标识符 3.2.2 #define 定义宏 3.2.3#define替换规则 3.2.4 #和## 2)##的作用: 3.2.5宏和函数的对比 3.2.6宏的命名约定和#undef指令 一、命名约定: …...

hue实现对hiveserver2 的负载均衡
如果你使用的是CDH集群那就很是方便的 在Cloudera Manager中,进入HDFS Service 进入Instances标签页面,点击Add Role Instances按钮,如下图所示 点击Continue按钮,如下图所示 返回Instances页面,选择HttpFS角色…...

SkyWalking 告警规则配置说明
Skywalking告警功能是在6.x版本新增的,其核心由一组规则驱动,这些规则定义在config/alarm-settings.yml 文件中。告警规则定义分为两部分: 1、告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件 2、webhook(网络钩子):定义当告警触发时,哪些服务终端需要被…...

HTML 表单笔记/练习
表单 概述 表单用于收集用户信息,用户填写表单提交到服务器 一般传参方式: GETPOSTCookie 传参要素 传参方式 GETPOST 参数的名字目标页面内容的数据类型(只有在上传文件的时候) 提示信息 一个表单中通常还包含一些说明性的文…...

关于Java Integer和Long使用equals直接比较
Integer和Long不能直接equals比较会返回False Long.class源码 public boolean equals(Object obj) {if (obj instanceof Long) {return this.value (Long)obj;} else {return false;} }Integer.class源码 public boolean equals(Object obj) {if (obj instanceof Integer) {…...

nodejs+vue衣服穿搭推荐系统-计算机毕业设计
模块包括主界面,系统首页、个人中心、用户管理、风格标签管理、衣服分类管理、衣服穿搭管理、服装信息管理、我的搭配管理、用户反馈、系统管理等进行相应的操作。无论是日常生活,还是特定场景,诸如面试、约会等,人们都有展现自我…...

Java并发面试题:(七)ThreadLocal原理和内存泄漏
ThreadLocal是什么? ThreadLocal是线程本地存储机制,可以将数据缓存在线程内部。ThreadLocal存储的变量在线程内共享的,在线程间又是隔离的。 ThreadLocal实现原理? ThreadLocal的底层是ThreadLocalMap,每个Thread都…...

香港服务器在国内访问太慢怎么能提高?
一直以来,全球化业务需求的增长是跟随着蓬勃向上的互联网而发展的。有了网络,海外贸易就在鼠标的轻点中完成。而IDC市场中的香港服务器也因为免备案政策的特性,开始逐渐成为企业想要跨越地域壁垒而考虑的对象。但在使用过程中ÿ…...

使用Proxyman抓取Android的https请求
使用Proxyman抓取Android的https请求 有时,您可能需要测试您的移动应用程序并检查与其关联的所有网络请求。在网络上,此任务非常简单,只需按Ctrl Shift I打开开发人员工具即可。从那里,您可以导航到网络选项卡并检查与网页相关的…...

基础MySQL的语法练习
基础MySQL的语法练习 create table DEPT(DEPTNO int(2) not null,DNAME VARCHAR(14),LOC VARCHAR(13) );alter table DEPTadd constraint PK_DEPT primary key (DEPTNO);create table EMP (EMPNO int(4) primary key,ENAME VARCHAR(10),JOB VARCHAR(9),MGR …...

RAID和LVM配置指南:创建、扩容和管理RAID设备和逻辑卷的方法
文章目录 1. 简介1.1 什么是RAID和LVM1.2 RAID和LVM的作用和优势 2. RAID配置命令:mdadm2.1 安装mdadm2.2 创建RAID设备2.2.1 RAID 02.2.2 RAID 12.2.3 RAID 52.2.4 RAID 10 2.3 添加磁盘到RAID设备2.4 删除磁盘从RAID设备2.5 查看和管理RAID设备2.6 故障处理与恢复…...

MapStruct使用方法
一、用途 1.1 优势 与动态映射框架相比,MapStruct 具有以下优势: (1)通过使用普通方法getter、setter调用,而不是反射来快速执行,效率很高。 (2)编译时类型安全:只能映…...

【LeetCode】50. Pow(x, n)
1 问题 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即, x n x^n xn )。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000 示例 2: 输入:x 2.10000, n 3 输出&a…...

vue2技能树(2)-模板语法、vue的工具链、渐进式框架
目录 Vue2技能树Vue 2 简单的模板语法详解插值绑定属性指令v-if 和 v-elsev-forv-on 计算属性过滤器插槽 Vue 2 生态系统详解1. Vue Router2. Vuex3. Vue CLI4. Axios5. Vue Devtools6. Element UI、Vuetify、Quasar等UI框架7. Nuxt.js8. Vue Apollo、Vue Router、Vue Fire等插…...

【Git系列教程-目录大纲】
《Git系列教程-目录大纲》 完完全全从零开始深入学习Git,教程配图200张,其中包括包括Git基本命令、命令原理、Git底层命令、分支、分支的原理、Git代码冲突原理/解决、tag标签、Git存储状态、分支合并原理、典型合并、快进合并、同轴开发、非同轴开发、…...

【高等数学】导数与微分
文章目录 1、导数的概念1.1、引例1.1.1、变速直线运动瞬时速度1.1.2、曲线的切线 1.2、导数的定义1.3、证明常用导数1.4、导数的几何意义1.5、可导与连续的关系 2、函数的求导法则2.1、函数的和、差、积、商的求导法则2.2、反函数的求导法则2.3、复合函数的求导法则2.4、基本初…...

springboot之quartz动态可控定时任务
Quartz Quartz是一个开源的任务调度框架,可以用来实现定时任务的调度,如定时发送邮件、定时备份数据等。Quartz具有很高的可靠性和灵活性,支持集群部署和分布式调度,并且提供了丰富的API和插件,可以轻松实现复杂的调度…...

什么是CSS的外边距重叠?
区块的上下外边距有时会合并(折叠)为单个边距,其大小为两个边距中的最大值(或如果它们相等,则仅为其中一个),这种行为称为外边距折叠。注意:有设定浮动和绝对定位的元素不会发生外边…...

设计模式之抽象工厂模式
前言 工厂模式一般指的是简单工厂模式、工厂方法模式、抽象工厂模式,这是三种工厂模式的最后一篇,其他两种的文章链接如下: 设计模式之简单工厂模式-CSDN博客 设计模式之工厂方法模式-CSDN博客 建议三种模式放在一起对比学习,…...

Compose预处理组件大比拼:性能、应用场景和可视化对比总结
在机器学习的世界里,预处理组件就像是厨师的烹饪工具。选择合适的工具不仅可以让整个烹饪过程更加顺畅,还能确保最终的菜肴更加美味。 本文将深入探讨四种“烹饪工具”:TransformedTargetRegressor、make_column_transformer、make_column_selector和ColumnTransformer。通…...

【小米】Linux 实习生
下午不准备去图书馆自习来着,中午就狠狠地多睡了一个小时,三点起床靠在椅子上剥柚子,太爽了,这秋天的下午。“邮件:小米公司邀请你预约面试时间”.......... 我擦,投了一个月了,认真准备的时候…...

python一点通:coroutine (协程)是什么和重要知识点?
协程已经成为Python用于编写并发和异步代码的重要工具之一。在这篇博客文章中,我们将深入探讨协程是什么,它们的优点,以及它们与传统的线程和进程有何不同。 什么是协程? 协程是用于合作式多任务处理的子程序(或函数…...