【Spring学习29】cglib的Lookup方法

本文介绍如何使用Spring的<lookup-method>解决长生命周期bean引用短生命周期bean的问题,通过cglib生成代理类实现在每次调用时都能获取新的短生命周期bean实例。

在长生命周期的bean引用短生命周期bean时,会有一个问题。 比如singleton 类依赖了prototype类,容器会在singleton 类初始化时,就根据依赖关系将prototype类注入。以后的每一次调用singleton bean都是同一个对象,里面的prototype bean也是最初注入的那个,容器再也不会为singleton bean产生新的prototype bean。
《作用域:使用代理aop:scoped-proxy》中我们通过在短生命周期的bean定义中加入<aop:scoped-proxy/>就轻松解决了这个问题。

但是,有些业务情况要求在增加新的bean时,不允许改动以前的bean定义和旧的代码。简单的说:被调用者(短生命周期bean)不允许修改,只能修改调用者(新增的长生命周期bean),所以这篇就讲讲如何利用Lookup达到我们的要求。

Lookup方法的工作机制是使用cglib在产生字节码这个阶段生成一个代理类,因此我们先要在工程中引入cglib组件。

程序代码如下:

接口:

package twm.spring.lookup;
public interface UserSession {
    Object getInstance();
}

接口的实现类:

package twm.spring.lookup;
public class UserSessionImpl implements UserSession{
    @Override
    public Object getInstance() {
        return this;
    }
}

业务类ServiceBean依赖UserSession:
因为setUserSession只是声明,并没有实现,所以是个抽象方法。声明这个抽象方法是为了让Lookup注入代理对象的。
当然也可以不声明为抽象方法(并不是强制要求),不过最终Lookup会覆盖原方法里的实现内容。

package twm.spring.lookup;
public abstract class ServiceBean {
    UserSession userSession;
    //原来的setter注入不要了
    //public void setUserSession(UserSession userSession) {
    //  this.userSession = userSession;
    //}
    public Object Perform(){
        this.userSession=setUserSession();
        return this.userSession.getInstance();
    }
    //抽象方法,由lookup-method注入具体bean
    abstract UserSession setUserSession();
}

beans.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:c="http://www.springframework.org/schema/c" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

<bean id="usession" class="twm.spring.lookup.UserSessionImpl" scope="prototype">
</bean> 

<bean id="servicebean" class="twm.spring.lookup.ServiceBean">
    <!-- <property name="UserSession" ref="usession"/> -->
    <!-- name是类中返回代理对象的方法名,bean是lookup-method要代理的对象 -->
    <lookup-method name="setUserSession" bean="usession"/>
</bean> 
</beans>

调用代码:

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
ServiceBean us=ctx.getBean("servicebean",ServiceBean.class);
System.out.println(us.Perform());
System.out.println(us.Perform());

运行输出:

twm.spring.lookup.UserSessionImpl@13838e4
twm.spring.lookup.UserSessionImpl@1f34a6

打印出的两个usession对象的地址不一样,说明我们实现了每次调用都能获得新的短生命周期的对象。
<lookup-method>标签中的name属性就是servicebean Bean中获取UserSession实例的方法,即:setUserSession()方法;bean属性是要注入的bean对象,这里是usession。

java.lang.IllegalStateException: Cannot load configuration class: org.example.SpringBootDemo at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:414) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:254) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:284) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:128) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.example.SpringBootDemo.main(SpringBootDemo.java:10) ~[classes/:na] Caused by: java.lang.ExceptionInInitializerError: null at org.springframework.context.annotation.ConfigurationClassEnhancer.newEnhancer(ConfigurationClassEnhancer.java:122) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:110) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:403) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] ... 12 common frames omitted Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @4b1c1ea0 at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:464) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na] at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.KeyFactory.create(KeyFactory.java:174) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.KeyFactory.create(KeyFactory.java:153) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] ... 15 common frames omitted Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @4b1c1ea0 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na] at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na] at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) ~[na:na] at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) ~[na:na] at org.springframework.cglib.core.ReflectUtils$1.run(ReflectUtils.java:61) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at java.base/java.security.AccessController.doPrivileged(AccessController.java:569) ~[na:na] at org.springframework.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:52) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:243) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE] ... 27 common frames omitted
10-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值