spring实例化二:SimpleInstantiationStrategy

本文深入剖析了Spring框架中类的实例化策略,通过InstantiationStrategy接口及其默认实现SimpleInstantiationStrategy,展示了如何根据不同场景选择合适的实例化方法,包括直接实例化、方法注入实例化及工厂方法实例化。

 
      spring对类的实例化,定义了接口InstantiationStrategy,同时先做了个简单实现类SimpleInstantiationStrategy。采用实现部分,抽象部分的策略。为了更好的扩展性,把一部分再次抽象,后面可以采用多种实现方式。
 
     下面具体代码分析:
public class SimpleInstantiationStrategy implements InstantiationStrategy {
 
    // FactoryMethod的ThreadLocal对象,线程所有的变量
    private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<Method>();
 
    // 返回当前线程所有的FactoryMethod变量值
    public static Method getCurrentlyInvokedFactoryMethod() {
        return currentlyInvokedFactoryMethod.get();
    }
 
    // 第一种实例化方法,实现部分,部分抽象
    @Override
    public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        // bd对象定义里,是否包含MethodOverride列表;spring有两个标签参数会产生MethodOverrides ,分别是 lookup-method,replaced-method 
        // 没有MethodOverride对象,可以直接实例化
        if (bd.getMethodOverrides().isEmpty()) {
            // 实例化对象的构造方法
            Constructor<?> constructorToUse;
            // 锁定对象,使获得实例化构造方法线程安全
            synchronized (bd.constructorArgumentLock) {
                // 查看bd对象里是否含有
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                // 没有就生成
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                @Override
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor((Class[]) null);
                        }
                        // 生成成功后,赋值给bd对象,后面使用
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Exception ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            // 反射生成对象
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // 有MethodOverride对象,需要使用另一种实现方式,之类实现
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }
 
    // 第一种实例化方法的抽象部分
    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
    }
 
    // 第二种实例化方法,实现部分,抽象部分
    @Override
    public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner,
            final Constructor<?> ctor, Object... args) {
        // 查看bd对象是否有MethodOverride对象
        // 没有MethodOverride,则直接实例化对象
        if (bd.getMethodOverrides().isEmpty()) {
            if (System.getSecurityManager() != null) {
                // use own privileged to change accessibility (when security is on)
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        ReflectionUtils.makeAccessible(ctor);
                        return null;
                    }
                });
            }
            // 反射实例化对象
            return BeanUtils.instantiateClass(ctor, args);
        }
        else {
            // 有MethodOverride,之类实现实例化方法
            return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
        }
    }
 
    // 第二种实例化方法的抽象部分
    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner,
            Constructor<?> ctor, Object... args) {
        throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
    }
 
    // 第三种实例化方法,全部实现
    @Override
    public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner,
            Object factoryBean, final Method factoryMethod, Object... args) {
 
        try {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        ReflectionUtils.makeAccessible(factoryMethod);
                        return null;
                    }
                });
            }
            else {
                ReflectionUtils.makeAccessible(factoryMethod);
            }
 
            // currentlyInvokedFactoryMethod,这块暂时还没看到在哪个地方用到了
            // 先取出原有的 Method
            Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
            try {
                // 设置当前的Method
                currentlyInvokedFactoryMethod.set(factoryMethod);
                // 使用factoryMethod实例化对象
                return factoryMethod.invoke(factoryBean, args);
            }
            finally {
                // 实例化完成,恢复现场
                if (priorInvokedFactoryMethod != null) {
                    currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
                }
                else {
                    currentlyInvokedFactoryMethod.remove();
                }
            }
        }
        catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(factoryMethod.getReturnType(),
                    "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
                    "args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
        }
        catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(factoryMethod.getReturnType(),
                    "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
        }
        catch (InvocationTargetException ex) {
            String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
            if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
                    ((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
                msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
                        "declaring the factory method as static for independence from its containing instance. " + msg;
            }
            throw new BeanInstantiationException(factoryMethod.getReturnType(), msg, ex.getTargetException());
        }
    }
 
}

 

 
注:lookup-method, replaced-method的使用,可以看之前的文章。 
 

转载于:https://www.cnblogs.com/sten/p/5762516.html

<think>好的,我现在需要帮助用户解决Spring Data中因JDBC连接失败导致Bean实例化异常的问题,特别是关于jdbcDialect无法获取JDBC连接的情况。首先,我需要回忆一下相关的Spring Data JDBC的知识,并结合用户提供的引用内容来分析可能的原因和解决方案。 首先,用户提到的异常是BeanInstantiationException,具体是创建LettuceConnectionFactory失败。不过用户的问题是关于JDBC连接失败导致的问题,这可能涉及不同的组件,但可能有一些共通点。根据引用[4],用户可能在升级依赖版本后遇到了这个问题,可能版本不兼容或配置错误。 接下来,用户的问题是关于jdbcDialect无法获取JDBC连接,这通常涉及到数据源的配置是否正确。根据引用[3]中提到的,spring-data-jdbc需要正确配置数据源和连接池,比如HikariCP。如果依赖版本升级后,可能存在配置不兼容的情况,比如驱动类名、URL格式等是否发生了变化。 首先,我应该检查数据源配置是否正确。包括数据库的URL、用户名、密码是否正确,驱动类是否加载。例如,如果使用的是MySQL,驱动类应该是com.mysql.cj.jdbc.Driver,而URL格式是否正确,比如是否有SSL或时区参数的问题。 其次,检查连接池的配置。引用[3]提到HikariCP是通过starter引入的,但可能在升级后版本变化导致默认配置需要调整。例如,连接池的最大连接数、超时时间是否合理,或者是否有泄漏的情况。 另外,数据库服务是否可用,比如网络是否通畅,数据库是否运行,端口是否开放,防火墙设置是否正确。这些基础问题也可能导致连接失败。 版本兼容性也是一个关键点。用户可能在升级Spring Boot或Spring Data版本后,相关依赖的版本不匹配。比如,某些Spring Boot版本可能需要特定版本的JDBC驱动或HikariCP,如果版本不匹配,可能导致连接问题。 查看日志信息是解决问题的关键步骤。用户需要检查完整的异常堆栈,查看是否有更具体的错误信息,比如连接超时、认证失败、驱动类找不到等。例如,ClassNotFoundException可能意味着驱动未正确引入;SQLException可能提示认证问题或URL格式错误。 另外,用户提到的jdbcDialect可能与数据库方言有关。不同的数据库需要不同的方言配置,尤其是在处理SQL生成时。如果spring-data-jdbc无法自动检测到正确的方言,可能需要手动配置。例如,在配置文件中指定spring.jdbc.dialect=org.springframework.data.relational.core.dialect.MySqlDialect(假设使用MySQL)。 还有,检查依赖是否完整。例如,是否遗漏了JDBC驱动的依赖,比如对于MySQL,需要引入mysql-connector-java;或者版本冲突,比如多个数据源依赖被错误引入。 最后,事务管理配置是否正确,虽然引用[2]提到JPA的事务管理,但在JDBC中事务的配置也可能影响连接获取。例如,是否启用了事务管理器,或者配置有误导致无法获取连接。 总结下来,解决步骤可能包括: 1. 检查数据源配置是否正确。 2. 验证数据库服务可访问。 3. 检查依赖版本兼容性。 4. 查看详细日志定位具体错误。 5. 手动配置JDBC方言。 6. 确保驱动依赖正确引入。 7. 调整连接池参数。 8. 处理可能的网络或权限问题。</think>### 解决Spring Data JDBC中BeanInstantiationException的步骤 #### 1. **验证数据源配置** - 检查`application.properties`或`application.yml`中的数据库连接参数: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=secret spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver ``` - **关键点**:确保URL格式、驱动类名与数据库类型匹配[^3]。 #### 2. **检查数据库服务状态** - 确认数据库服务已启动,端口可访问: ```bash telnet localhost 3306 # 测试MySQL端口 ``` - 验证防火墙或云安全组规则是否放行数据库端口。 #### 3. **排查依赖版本兼容性** - 检查`pom.xml`或`build.gradle`中依赖版本: ```xml <!-- Spring Boot Starter JDBC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <version>2.7.0</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> ``` - **问题示例**:Spring Boot 3.x需要JDK 17+和MySQL驱动`8.0.30+`,否则会报错[^4]。 #### 4. **分析日志定位具体错误** - 查看启动日志中完整的异常堆栈,常见错误类型: - `java.sql.SQLException: Access denied`:用户名/密码错误。 - `No suitable driver found`:驱动类未加载或URL格式错误。 - `Connection timed out`:网络不通或数据库服务未启动。 #### 5. **手动配置JDBC方言** - 如果自动检测方言失败,显式配置: ```properties spring.jdbc.dialect=org.springframework.data.relational.core.dialect.MySqlDialect ``` - **适用场景**:使用国产数据库或非标准SQL语法时[^3]。 #### 6. **调整连接池参数** - 在配置文件中优化HikariCP参数: ```properties spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=10 ``` - **作用**:避免因连接泄漏或超时导致获取失败[^3]。 #### 7. **其他可能原因** - **驱动未加载**:确保驱动包已正确引入(如`mysql-connector-java`)。 - **事务管理器冲突**:检查是否配置了多个数据源但未正确标记`@Primary`。 --- ### 示例配置修正 ```yaml # application.yml spring: datasource: url: jdbc:mysql://localhost:3306/test_db?useSSL=false username: admin password: admin123 driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 5 jdbc: dialect: mysql # 部分版本可能需要全限定类名 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值