Java MyBatis实体类属性名与数据库字段表名称不一致解决办法
AI摘要 MyBatis解决实体类属性名与数据库字段名不一致问题有两种方法。一是查询时使用别名，如`select 数据库字段 as 实体属性`，可结合SQL片段复用。二是使用`resultMap`进行映射，通过``将数据库字段映射到实体类属性。`resultMap`是更灵活且推荐的方式，尤其适用于复杂映射场景。 此内容根据文章生成，仅用于文章内容的解释与总结 MyBatis实体类属性名与数据库字段表名称不一致解决办法1. 查询字段时使用别名对不一样的列名起别名，让别名和实体类的属性名一样<mapper namespace="org.example.mapper.UserMapper"> select 数据库表字段名 as 实体类属性名 from user; </mapper>但是每次查询都需要重新定义一次别名使用sql片段的方式解决<mapper namespace="org.example.mapper.UserMapper"> <sql id="dataName"> 数据库表字段名 as 实体类属性名 </sql> select <include refid="dataName" /> from user; </mapper>但是此方法缺点不灵活2.使用resultMap映射别名<mapper namespace="org.example.mapper.UserMapper"> <resultMap id="dataName" type="user"> <id column="id" property="id"></id> <result column="数据库表字段名" property="实体类属性名"></result> </resultMap> select * from user; </mapper>id作为主键映射，result作为一般字段映射
AI摘要 MyBatis代理开发通过定义Mapper接口和SQL映射文件实现。首先，创建与SQL映射文件同名的Mapper接口，并置于同一目录。将SQL映射文件的namespace设为Mapper接口的全限定名。在Mapper接口中定义与SQL语句id对应的方法，保持参数和返回值类型一致。配置mybatis-config.xml加载Mapper。最后，通过SqlSession的getMapper方法获取Mapper接口的代理对象，调用其方法执行SQL。也可使用包扫描简化Mapper加载。 此内容根据文章生成，仅用于文章内容的解释与总结 MyBatis代理开发0. 环境准备User.javapackage org.example; public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAll(){ return "id=" + id + " username=" + username + " password=" + password; } } 1. 定义与SQL映射文件同名的Mapper接口，并且将Mapper接口和SQL映射文件放置在同一目录下创建UserMapper.java空接口创建UserMapper.xml文件(IDEA中resources创建目录使用/代替.)2. 设置SQL映射文件的namespace属性为Mapper接口全限定名UserMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="org.example.mapper.UserMapper"> select * from user; </mapper>3. 在Mapper接口中定义方法，方法名就是SQL映射文件中sql语句的id，并保持参数类型和返回值类型一致UserMapper.javapackage org.example.mapper; import org.example.User; import java.util.List; public interface UserMapper { List<User> selectAll(); }mybatis-config.xml<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/java?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/example/mapper/UserMapper.xml"/> </mappers> </configuration>4.编码：通过 SqlSession的 getMapper方法获取 Mapper接口的代理对象调用对应方法完成sql的执行主函数调用 public static void main(String[] args) throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取UserMapper代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.selectAll(); System.out.println(users.get(0).getAll()); sqlSession.close(); }执行结果id=1 username=admin password=1ed1252369b609c48a93ea80b57f52ed细节：如果Mapper接口名称和SQL映射文件名称相同，并在同一目录下，则可以使用包扫描的方式简化SQL映射文件的加载mybatis-config.xml <mappers> <!-- <mapper resource="org/example/mapper/UserMapper.xml"/>--> <package name="org.example.mapper"/> </mappers>
AI摘要 本文介绍了Java MyBatis快速入门流程：包括数据库准备、Maven依赖配置、编写MyBatis核心及映射配置文件、创建实体类，并最终通过SqlSession实现数据库查询操作。 此内容根据文章生成，仅用于文章内容的解释与总结 MyBatis 快速入门1.数据库准备1.1 数据库信息地址端口用户名密码localhost3306rootroot1.2 创建java数据库create database java;1.3 创建user数据表并且添加数据SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名', `password` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `user_username_unique`(`username`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'admin', '1ed1252369b609c48a93ea80b57f52ed'); SET FOREIGN_KEY_CHECKS = 1; 2.Java Maven项目2.1 配置pom.xml <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.9</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.9</version> </dependency> </dependencies>2.2 在resources添加logback.xml文件（日志：可选）<?xml version="1.0" encoding="UTF-8"?> <configuration scan="false" scanPeriod="60 seconds" debug="false"> <property name="LOG_HOME" value="${catalina.base}/logs/" /> <property name="appName" value="u-plan"/> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <Encoding>UTF-8</Encoding> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </layout> </appender> <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <Encoding>UTF-8</Encoding> <file>${LOG_HOME}/${appName}.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log </fileNamePattern> <MaxHistory>30</MaxHistory> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>512MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [%logger{50} : %line ] - %msg%n</pattern> </layout> </appender> <!-- logger主要用于存放日志对象，也可以定义日志类型、级别 name：表示匹配的logger类型前缀，也就是包的前半部分 level：要记录的日志级别，包括 TRACE < DEBUG < INFO < WARN < ERROR additivity：作用在于children-logger是否使用 rootLogger配置的appender进行输出，false：表示只用当前logger的appender-ref，true：表示当前logger的appender-ref和rootLogger的appender-ref都有效 --> <logger name="org.hibernate" level="error" /> <logger name="org.springframework" level="error" additivity="false"/> <logger name="com.fairyland" level="info" additivity="true"> <appender-ref ref="appLogAppender" /> </logger> <root level="info"> <appender-ref ref="stdout" /> <appender-ref ref="appLogAppender" /> </root> </configuration> 2.3 在resources下创建mybatis-config.xml文件<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/java?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>2.4 创建数据表User实体类package org.example; public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAll(){ return "id=" + id + " username=" + username + " password=" + password; } }2.5在resources下创建MyBatis的Sql映射文件UserMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="user"> select * from user; </mapper>3. 运行效果主函数执行public class Main { public static void main(String[] args) throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> objects = sqlSession.selectList("user.selectAll"); System.out.println(objects.get(0).getAll()); } }执行结果id=1 username=admin password=1ed1252369b609c48a93ea80b57f52ed
AI摘要 本文总结了Java Spring IoC的整体流程，重点涵盖了Spring Bean的基本实例化过程、BeanFactory后处理器的执行逻辑以及Bean生命周期的各个阶段。通过对核心环节的梳理，清晰展示了Spring容器管理Bean的完整机制。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring IoC整体流程总结1. 整体流程2. SpringBean基本实例化过程3. BeanFactory后处理器过程4. Bean生命周期阶段
AI摘要 Spring Bean生命周期分为实例化、初始化和完成阶段。实例化后，在初始化阶段进行属性填充、Aware接口注入及BeanPostProcessor回调。针对循环依赖，Spring通过三级缓存机制解决。最终，完整Bean存入单例池完成生命周期。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring 生命周期1. 概述Spring Bean的生命周期是从 Bean 实例化之后，即通过反射创建出对象之后，到Bean成为一个完整对象，最终存储到单例池中，这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:Bean的实例化阶段: Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是sinaleton的是否不是延迟加载的，是否不是FactoryBean等，最终将一个普通的singleton的Bean通过反射进行实例化;Bean的初始化阶段: Bean创建之后还仅仅是个"半成品”，还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段，Aop增强功能，后面要学习的Spring的注解功能等spring高频面试题Bean的循环引用问题都是在这个阶段体现的:Bean的完成阶段: 经过初始化阶段，Bean就成为了一个完整的Spring Bean，被存储到单例池singletonObjects中去了，即完成了Spring Bean的整个生命周期2. 初始化阶段2.1 Bean的初始化阶段涉及过程Spring Bean的初始化过程涉及如下几个过程Bean实例的属性填充Aware接口属性注入BeanPostProcessor的before()方法回调InitializingBean接口的初始化方法回调自定义初始化方法init回调BeanPostProcessor的after()方法回调2.2 Bean实例属性填充Spring在进行属性注入时，会分为如下几种情况:注入普通属性，String、int或存储基本类型的集合时，直接通过set方法的反射设置进去;注入单向对象引用属性时，从容器中getBean获取后通过set方法反射设置进去，如果容器中没有，则先创建被注入对象Bean实例(完成整个生命周期)后，在进行注入操作;注入双向对象引用属性时，就比较复杂了，涉及了循环引用(循环依赖)问题2.2.1 propertyValuesBeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储PersonDao.javapackage org.example; public interface PersonDao { }UserDao.javapackage org.example; public interface UserDao { }UserDaoImpl.javapackage org.example; public class UserDaoImpl implements UserDao { }PersonDaoImpl.javapackage org.example; public class PersonDaoImpl implements PersonDao{ public String getName() { return name; } public void setName(String name) { this.name = name; } public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } private String name; private UserDao userDao; } beans.xml<bean class="org.example.UserDaoImpl" id="userDao"/> <bean class="org.example.PersonDaoImpl" id="personDao"> <property name="userDao" ref="userDao"/> <property name="name" value="名字"/> </bean>主函数调用ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");例如PersonDao的属性信息如下:2.2.2 循环引用多个实体之间相互依赖并形成闭环的情况就叫做”循环依赖”，也叫做”循环引用。死循环原理图UserDaoImpl.javapackage org.example; public class UserDaoImpl implements UserDao { private UserService userService; public UserService getUserService() { return userService; } public void setUserService(UserService userService) { this.userService = userService; } } UserServiceImpl.javapackage org.example; public class UserServiceImpl implements UserService { private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } } beans.xml<bean class="org.example.UserDaoImpl" id="userDao"> <property name="userService" ref="userService"/> </bean> <bean class="org.example.UserServiceImpl" id="userService"> <property name="userDao" ref="userDao"/> </bean>2.2.3 三级缓存解决循环引用问题Spring提供了三级缓存存储完整Bean实例和半成品Bean实例，用于解决循环引用问题在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:UserService和UserDao循环依赖的过程结合上述三级缓存描述一下UserService 实例化对象，但尚未初始化，将UserService存储到三级缓存UserService 属性注入，需要UserDao，从缓存中获取，没有UserDao;UserDao实例化对象，但尚未初始化，将UserDao存储到到三级缓存;UserDao属性注入，需要UserService，从三级缓存获取UserService，UserService从三级缓存移入二级缓存:UserDao执行其他生命周期过程，最终成为一个完成Bean，存储到一级缓存，删除二三级缓存UserService 注入UserDao:UserService执行其他生命周期过程，最终成为一个完成Bean，存储到一级缓存，删除二三级缓存2.3 常用的Aware接口Aware接口是一种框架辅助属性注入的一种思想，其他框架中也可以看到类似的接口。框架具备高度封装性，我们接触到的一般都是业务代码，一个底层功能API不能轻易的获取到，但是这不意味着永远用不到这些对象，如果用到了就可以使用框架提供的类似Aware的接口，让框架给我们注入该对象。在pom.xml添加ServletContext依赖jar包 <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>UserServiceImpl.javapackage org.example; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class UserServiceImpl implements UserService, ApplicationContextAware, BeanNameAware, BeanFactoryAware { public UserServiceImpl() { System.out.println("构造方法执行"); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("BeanFactory--------" + beanFactory.toString()); } @Override public void setBeanName(String s) { System.out.println("BeanName--------" + s); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("ApplicationContext--------" + applicationContext.toString()); } } 主函数调用 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) applicationContext.getBean("userService"); System.out.println(userService); }执行结果构造方法执行 BeanName--------userService BeanFactory--------org.springframework.beans.factory.support.DefaultListableBeanFactory@319b92f3: defining beans [userDao,userService]; root of factory hierarchy ApplicationContext--------org.springframework.context.support.ClassPathXmlApplicationContext@1a93a7ca, started on Sat Mar 04 15:19:28 CST 2023 org.example.UserServiceImpl@436a4e4b{x}
AI摘要 本文介绍了Spring两大核心扩展点：BeanFactoryPostProcessor用于在Bean实例化前动态修改或注册BeanDefinition；BeanPostProcessor则在Bean初始化前后介入，常用于属性填充或通过动态代理实现功能增强。两者为开发者提供了介入Bean生命周期的灵活机制。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring Bean工厂后处理器Spring的后处理器是Spring对外开发的重要扩展点，允许我们介入到Bean的整个实例化流程中来，以达到动态注册BeanDefinition，动态修改BeanDefinition，以及动态修改Bean的作用。Spring主要有两种后处理器:BeanFactoryPostProcessor: Bean工厂后处理器，在BeanDefinitionMap填充完毕，Bean实例化之前执行；BeanPostProcessor: Bean后处理器，一般在Bean实例化之后，填充到单例池singletonObjects之前执行。1. BeanFactoryPostProcessor1.1 快速入门BeanFactoryPostProcessor是一个接口规范，实现了该接口的类只要交由Spring容器管理的话，那么Spring就会回调该接口的方法，用于对BeanDefinition注册和修改的功能。实例化流程MyBeanFactoryPostProcessor.javapackage processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { System.out.println("beanDefinitionMap填充完毕后回调该方法"); } } beans.xml<bean class="processor.MyBeanFactoryPostProcessor"/>主函数断点调试public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); }执行结果beanDefinitionMap填充完毕后回调该方法1.2 强制修改BeanDefinition其他设置基于上文MyBeanFactoryPostProcessor.javapublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { System.out.println("beanDefinitionMap填充完毕后回调该方法"); BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService"); beanDefinition.setBeanClassName("org.example.UserDaoImpl"); } }主函数调用public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Object userService = applicationContext.getBean("userService"); System.out.println(userService); }输出结果beanDefinitionMap填充完毕后回调该方法 org.example.UserDaoImpl@3c875211.3 动态注入BeanDefinition创建一个新的接口PersonDaopublic interface PersonDao { }创建一个新的类PersonDaoImpl，并且继承PersonDao接口public class PersonDaoImpl implements PersonDao{ }MyBeanFactoryPostProcessor.javapublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { System.out.println("beanDefinitionMap填充完毕后回调该方法"); //注册BeanDefinition BeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(PersonDaoImpl.class.getName()); //强转为DefaultListableBeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory; defaultListableBeanFactory.registerBeanDefinition("personDao",beanDefinition); } }beans.xml<bean class="processor.MyBeanFactoryPostProcessor"/>主函数调用 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); PersonDao personDao = applicationContext.getBean(PersonDao.class); System.out.println(personDao); }执行结果beanDefinitionMap填充完毕后回调该方法 org.example.PersonDaoImpl@780cb77_Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作直接继承BeanDefinitionRegistryPostProcessor接口的方式简化代码MyBeanDefinitionRegistryPostProcessor.javapackage processor; import org.example.PersonDaoImpl; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { System.out.println("beanDefinitionMap填充完毕后回调该方法"); //注册BeanDefinition BeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(PersonDaoImpl.class.getName()); beanDefinitionRegistry.registerBeanDefinition("personDao",beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } } beans.xml<bean class="processor.MyBeanDefinitionRegistryPostProcessor"/>主函数调用 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); PersonDao personDao = applicationContext.getBean(PersonDao.class); System.out.println(personDao); }执行结果beanDefinitionMap填充完毕后回调该方法 org.example.PersonDaoImpl@50a7bc6e1.4使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描要求如下：自定义@MyComponent注解，使用在类上;使用资料中提供好的包扫描器工具BaseClassScanUtils 完成指定包的类扫描自定义BeanFactoryPostProcessor完成注解@MyComponent的解析，解析后最终被Spring管理。黑马程序员提供的BaseClassScanUtils.javapackage com.itheima.utils; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.util.ClassUtils; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.List; import java.util.Map; 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); //扫描到的class String 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; } //如果没有为"",那就把当前类的类名作为beanName annotationClassMap.put(clazz.getSimpleName(),clazz); } } } catch (Exception exception) { } return annotationClassMap; } public static void main(String[] args) { Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.itheima"); System.out.println(stringClassMap); } }由于我使用的是jdk19，所以使用上述文件会提示版本错误，所以在上面的基础上进行修改package utils; import anno.MyComponent; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.util.ClassUtils; import java.util.HashMap; import java.util.Map; 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); for (Resource resource : resources) { Class<?> clazz = Class.forName(resource.getFile().toString().split("classes\\\\")[1].replaceAll("\\\\",".").split(".class")[0]); //判断是否属于指定的注解类型 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; } //如果没有为"",那就把当前类的类名作为beanName annotationClassMap.put(clazz.getSimpleName(),clazz); } } } catch (Exception exception) { System.out.println(exception); } return annotationClassMap; } public static void main(String[] args) { Map<String, Class> stringClassMap = scanMyComponentAnnotation("beans"); System.out.println(stringClassMap); } }创建注解MyComponentpackage anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyComponent { String value(); }创建类并且添加MyComponent注解，类位于beans目录下package beans; import anno.MyComponent; @MyComponent("otherBean") public class OtherBean { }创建新的后工厂处理器类，并且继承BeanDefinitionRegistryPostProcessor接口package processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import utils.BaseClassScanUtils; import java.util.Map; public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { // 使用扫描工具去扫描指定包以及子包下的所有类，收集使用@Mycomponent的注解的类 Map<String, Class> myComponentAnnotation = BaseClassScanUtils.scanMyComponentAnnotation("beans"); // 遍历Map，组装BeanDefinition进行注册 myComponentAnnotation.forEach((beanName,clazz)->{ // 获取类名 String beanClassName = clazz.getName(); // 创建BeanDefinition BeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(beanClassName); // 注册BeanDefinition beanDefinitionRegistry.registerBeanDefinition(beanName ,beanDefinition); }); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } } 在beans.xml中添加后工厂处理器<bean class="processor.MyComponentBeanFactoryPostProcessor"/>主函数调用public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); OtherBean otherBean = applicationContext.getBean(OtherBean.class); System.out.println(otherBean); }执行结果beans.OtherBean@2133814f2. BeanPostProcessor2.1 快速入门Bean被实例化后，到最终缓存到名为singletonObjects单例池之前，中间会经过Bean的初始化过程，例如: 属性的填充、初始方法init的执行等，其中有一个对外进行扩展的点BeanPostProcessor，我们称为Bean后处理。跟上面的Bean工厂后处理器相似，它也是一个接口，实现了该接口并被容器管理的BeanPostProcessor，会在流程节点上被Spring自动调用。BeanPostProcessor类public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }UserDaopackage org.example; public interface UserDao { }UserDaoImplpackage org.example; public class UserDaoImpl implements UserDao { String name; public UserDaoImpl() { System.out.println("UserDaoImpl初始化"); } public String getName() { return name; } public void setName(String name) { System.out.println("设置name的值为" + name); this.name = name; } }创建MyBeanPostProcessor并且继承BeanPostProcessor接口public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("before-" + beanName + ":" + bean.toString()); if (bean instanceof UserDaoImpl){ UserDaoImpl userDao = (UserDaoImpl) bean; userDao.setName("hhhhh"); } return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("after-" + beanName + ":" + bean.toString()); return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }beans.xml<bean class="processor.MyBeanPostProcessor"/> <bean class="org.example.UserDaoImpl" id="userDao"/>主函数执行public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); UserDaoImpl userDao = (UserDaoImpl) applicationContext.getBean("userDao"); System.out.println(userDao.getName()); }执行结果UserDaoImpl初始化 before-userDao:org.example.UserDaoImpl@646be2c3 设置name的值为hhhhh after-userDao:org.example.UserDaoImpl@646be2c3 hhhhh2.2 before和after方法的执行时机基于上述快速入门的文件进行修改UserDaoImpl.javapackage org.example; import org.springframework.beans.factory.InitializingBean; public class UserDaoImpl implements UserDao, InitializingBean { public UserDaoImpl() { System.out.println("UserDaoImpl初始化"); } String name; public String getName() { return name; } public void setName(String name) { System.out.println("设置name的值为" + name); this.name = name; } public void init(){ System.out.println("init初始化方法执行"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("属性设置之后执行"); } }beans.xml<bean class="org.example.UserDaoImpl" id="userDao" init-method="init"/> <bean class="processor.MyBeanPostProcessor" />执行结果UserDaoImpl初始化 before-userDao:org.example.UserDaoImpl@3c0a50da 设置name的值为hhhhh 属性设置之后执行 init初始化方法执行 after-userDao:org.example.UserDaoImpl@3c0a50da hhhhh2.3 对Bean方法进行执行时间日志增强需求：Bean的方法执行之前控制台打印当前时间Bean的方法执行之后控制台打印当前时间分析：对方法进行增强主要就是代理设计模式和包装设计模式由于Bean方法不确定，所以使用动态代理在运行期间执行增强操作在Bean实例创建完毕后，进入到单例池之前，使用Proxy代替真是的目标BeanUserDao.javapackage org.example; public interface UserDao { public void show(); }UserDaoImpl.javapackage org.example; public class UserDaoImpl implements UserDao { public void show(){ System.out.println("执行方法中"); } }TimeLogBeanPostProcessor.javapackage processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Date; public class TimeLogBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Object beanProxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), (Object proxy, Method method, Object[] args) -> { //1.输出开始时间 System.out.println("方法：" + method.getName() + "-开始时间：" + new Date()); //2.执行目标方法 Object result = method.invoke(bean,args); //3.输出结束时间 System.out.println("方法：" + method.getName() + "-结束时间：" + new Date()); return result; }); return beanProxy; } }beans.xml<bean class="processor.TimeLogBeanPostProcessor"/> <bean class="org.example.UserDaoImpl" id="userDao"/>主函数调用 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); UserDao userDao = (UserDao) applicationContext.getBean("userDao"); userDao.show(); }执行结果方法：show-开始时间：Thu Mar 02 16:13:42 CST 2023 执行方法中 方法：show-结束时间：Thu Mar 02 16:13:43 CST 2023
AI摘要 Spring Bean实例化流程如下：首先解析配置文件，将Bean信息封装为BeanDefinition并存入beanDefinitionMap；随后容器遍历该Map，通过反射创建Bean实例并存入singletonObjects集合；最终调用getBean方法时，直接从singletonObjects中获取并返回对应的Bean实例。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring Bean实例化的基本流程Spring容器在进行初始化时，会将xml配置的<bean>的信息封装成一个BeanDefinition对象，所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去，Spring框架在对该Map进行遍历，使用反射创建Bean实例对象，创建好的Bean对象存储在一个名为singletonObjects的Map集合中，当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。流程：加载xml配置文件，解析获取配置中的每个<bean>的信息，封装成一个个的BeanDefinition对象将BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中;ApplicationContext底层遍历beanDefinitionMap，创建Bean实例对象 创建好的Bean实例对象，被存储到一个名为singletonObjects的Map<String,Object>中;当执行applicationContext.getBean(beanName)时，从singletonObjects去匹配Bean实例返回beans.xml <bean class="org.example.UserDaoImpl" id="userDao"/> <bean class="org.example.UserServiceImpl" id="userService"/>主函数断点调试运行 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); }Bean信息定义对象-BeanDefinitionBeanDefinition.class// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.beans.factory.config; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.MutablePropertyValues; import org.springframework.core.AttributeAccessor; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { String SCOPE_SINGLETON = "singleton"; String SCOPE_PROTOTYPE = "prototype"; int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; void setParentName(@Nullable String var1); @Nullable String getParentName(); void setBeanClassName(@Nullable String var1); @Nullable String getBeanClassName(); void setScope(@Nullable String var1); @Nullable String getScope(); void setLazyInit(boolean var1); boolean isLazyInit(); void setDependsOn(@Nullable String... var1); @Nullable String[] getDependsOn(); void setAutowireCandidate(boolean var1); boolean isAutowireCandidate(); void setPrimary(boolean var1); boolean isPrimary(); void setFactoryBeanName(@Nullable String var1); @Nullable String getFactoryBeanName(); void setFactoryMethodName(@Nullable String var1); @Nullable String getFactoryMethodName(); ConstructorArgumentValues getConstructorArgumentValues(); default boolean hasConstructorArgumentValues() { return !this.getConstructorArgumentValues().isEmpty(); } MutablePropertyValues getPropertyValues(); default boolean hasPropertyValues() { return !this.getPropertyValues().isEmpty(); } void setInitMethodName(@Nullable String var1); @Nullable String getInitMethodName(); void setDestroyMethodName(@Nullable String var1); @Nullable String getDestroyMethodName(); void setRole(int var1); int getRole(); void setDescription(@Nullable String var1); @Nullable String getDescription(); ResolvableType getResolvableType(); boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); @Nullable String getResourceDescription(); @Nullable BeanDefinition getOriginatingBeanDefinition(); }
AI摘要 本文介绍了如何在Spring中配置非自定义Bean（第三方类）。通过Druid数据源、JDBC连接、日期对象及MyBatis的SqlSessionFactory等实例，展示了利用无参构造、静态工厂方法及实例工厂方法三种方式，实现第三方Bean的实例化与属性注入，从而将其纳入Spring容器管理。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring 配置非自定义Bean0.介绍实际开发中会使用第三方jar包中的功能类，这些Bean要想让Spring进行管理，也需要对其进行配置两个问题：被配置的Bean实例化方式是什么被配置的Bean是否需要注入必要属性1.配置Druid数据源交由Spring管理maven配置文件导入相应jar包pom.xml <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.23</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>beans.xml <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/java"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>主函数调用 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Object dataSource = applicationContext.getBean("dataSource"); System.out.println(dataSource); }运行结果{ CreateTime:"2023-02-26 17:23:51", ActiveCount:0, PoolingCount:0, CreateCount:0, DestroyCount:0, CloseCount:0, ConnectCount:0, Connections:[ ] }2.配置Connection交由Spring管理Connection的产生是通过DriverManager的静态方法getConnection获取的，所以我们要用静态工厂的方式配置beans.xml（getConnection报红不用管） <bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype"> <constructor-arg name="url" value="jdbc:mysql://localhost:3306/java"/> <constructor-arg name="user" value="root"/> <constructor-arg name="password" value="root"/> </bean>主函数调用 public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Object connection = applicationContext.getBean("connection"); System.out.println(connection);; }执行结果（报错不用管，看到有类字节码地址输出即可）com.mysql.jdbc.JDBC4Connection@42a15bdc3.配置日期对象交由Spring管理原生使用日期对象 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = simpleDateFormat.parse("2023-08-27 12:00:00"); System.out.println(date);结果输出Sun Aug 27 12:00:00 CST 2023配置日期对象交由Spring管理beans.xml <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 12:00:00"/> </bean>主函数执行 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Object date1 = applicationContext.getBean("date"); System.out.println(date1);结果输出Sun Aug 27 12:00:00 CST 20234.配置MyBatis的SqlSessionFactory交由Spring管理导入MyBatis的相关坐标 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>4.1 原生实现 // 静态工厂方法方式 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 无参构造实例化 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 实例工厂方法 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); System.out.println(sqlSessionFactory);执行结果org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@79efed2d4.2 Bean实现resources目录下创建mybatis-config.xml<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/java"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> </configuration>beans.xml <bean id="resources" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream"> <constructor-arg name="resource" value="mybatis-config.xml"/> </bean> <bean id="sqlSessionFactoryBuilder" class="org.apache.ibatis.session.SqlSessionFactoryBuilder"/> <bean id="sqlSessionFactory" factory-bean="sqlSessionFactoryBuilder" factory-method="build"> <constructor-arg name="inputStream" ref="resources"/> </bean>主函数调用 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Object sqlSessionFactory = applicationContext.getBean("sqlSessionFactory"); System.out.println(sqlSessionFactory);执行结果org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@5026735c
AI摘要 本文介绍了在Java Spring框架中获取Bean的三种主要方式：一是通过ApplicationContextAware接口手动获取；二是通过注解@Autowired或@Resource进行自动注入；三是利用BeanFactory或ApplicationContext直接从容器中根据名称或类型检索Bean。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring 获取Bean的三种方式
AI摘要 Spring通过在``标签中使用`profile`属性，可实现不同环境下的Bean配置切换。激活环境可通过命令行参数`-Dspring.profiles.active`或代码中设置`System.setProperty`实现，从而灵活加载特定环境所需的Bean。 此内容根据文章生成，仅用于文章内容的解释与总结 Java Spring beans的profile属性切换环境<beans>标签，除了经常用的作为根标签外，还可以嵌套在根标签内，使用profile属性切换开发环境可以使用以下两种方式指定被激活的环境（dev为环境名）：使用命令行动态参数，虚拟机参数位置加载 -Dspring.profiles.active=dev使用代码的方式设置环境变量System.setProperty("spring.profiles.active","dev")beans.xml <bean class="org.example.UserDaoImpl" id="userDao"/> <bean class="org.example.UserServiceImpl" id="userService"/> <beans profile="dev"> <bean id="userService1" class="org.example.UserServiceImpl"/> </beans> <beans profile="test"> <bean id="userDao1" class="org.example.UserDaoImpl"/> </beans>主函数调用 public static void main(String[] args) { // 指定环境 System.setProperty("spring.profiles.active","dev"); ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); System.out.println(((UserService) applicationContext.getBean("userService"))); System.out.println(((UserDao) applicationContext.getBean("userDao"))); System.out.println(((UserService) applicationContext.getBean("userService1"))); System.out.println(((UserDao) applicationContext.getBean("userDao1"))); }执行结果org.example.UserServiceImpl@589b3632 org.example.UserDaoImpl@45f45fa1 org.example.UserServiceImpl@4c6e276e Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userDao1' available