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)编译时类型安全:只能映…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...

动态规划-1035.不相交的线-力扣(LeetCode)
一、题目解析 光看题目要求和例图,感觉这题好麻烦,直线不能相交啊,每个数字只属于一条连线啊等等,但我们结合题目所给的信息和例图的内容,这不就是最长公共子序列吗?,我们把最长公共子序列连线起…...

EEG-fNIRS联合成像在跨频率耦合研究中的创新应用
摘要 神经影像技术对医学科学产生了深远的影响,推动了许多神经系统疾病研究的进展并改善了其诊断方法。在此背景下,基于神经血管耦合现象的多模态神经影像方法,通过融合各自优势来提供有关大脑皮层神经活动的互补信息。在这里,本研…...