版权声明:仅供学习交流使用 https://blog.csdn.net/drxRose/article/details/84938810
方法注入解决的需求:
若bean A依赖bean B,在两者生命周期不同的情况下,若bean A每次使用bean B的实例的时候,都需要拥有不同状态的bean B的实例的话,就需要使用方法注入的功能,来实现此需求.(需要使用cglib技术)
注意,bean A中定义依赖bean B的方法,要遵循以下的格式:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
方式 | 说明 |
实现ApplicationContextAware接口 | 耦合度较高,不建议使用 实现其具体方法,自定义注入依赖bean的方法即可 |
使用标签`lookup-method` | 只需在该标签内指定方法名和依赖的bean之间的映射即可 |
使用注解@Lookup | 在bean内部自定义依赖bean的方法,配置文件开启注解扫描 |
官方文档中对使用方法注入(方法查询)的提醒事项:spring4.3.20版本
- For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.
- Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
- Concrete methods are also necessary for component scanning which requires concrete classe to pick up.
- A further key limitation is that lookup methods won’t work with factory methods and in particular not with @Bean methods in configuration classes, since the container is not in charge of creating the instance in that case and therefore cannot create a runtime-generated subclass on the fly.
方法替换解决的需求:
在使用某bean的时候,因原有bean中的某一行为,无法满足需求,需要针对此行为进行自定制.此时可以使用方法替换的功能,来对原bean的某一行为进行重写.(需要使用cglib技术)
重写方法的类对象需要实现MethodReplacer接口,在接口规定的方法内重写方法的过程即可.
配置文件标签或属性 | 说明 |
replaced-method(标签) | 对原bean需要重写的方法进行定义 |
name(属性) | 需要重写原有bean中某方法的方法名 |
replacer(属性) | 实现MethodReplacer接口实例的bean的id或name值 |
arg-type(标签) | 重写方法的参数类型 |
具有依赖关系的类对象
package siye;
public class Person
{
}
package siye;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class UserByImpl implements ApplicationContextAware
{
private ApplicationContext applicationContext;
public Person createPerson()
{
return this.applicationContext.getBean("person", Person.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.applicationContext = applicationContext;
}
}
package siye;
/*
* 对于此类需要注意的事项.
* 1,类和方法不能使用final修饰.
* 2,要定义抽象的方法和类.
* 3,look-up对注解@Bean的支持度不高.
* 4,需要cglib,spring4.3已内置cglib.
*/
public abstract class UserByTag
{
/*
* 此抽象方法有格式要求.
* 需使用如下的格式.
* <public|protected> [abstract] <return-type> theMethodName(no-arguments);
*/
public abstract Person createPerson();
}
package siye;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component("userByAnno")
public abstract class UserByAnno
{
/*
* 注解中value值的说明:
* This annotation attribute may suggest a target bean name to look up.
* If not specified, the target bean will be resolved based on the
* annotated method's return type declaration.
*/
@Lookup("person")
public abstract Person createPerson();
}
重写bean某行为的类对象和实现类
package siye;
public class TargetObj
{
public void work(int num)
{
System.out.println("work..." + num + 12);
}
public void eat()
{
System.out.println("eat");
}
}
package siye;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.beans.factory.support.MethodReplacer;
public class TargetObjReplaceImpl implements MethodReplacer
{
/*
* 此方法参数的说明.
*
* param obj the instance we're reimplementing the method for
* param method the method to reimplement
* param args arguments to the method
* return return value for the method
*/
@Override
public Object reimplement(Object obj, Method method, Object[] args)
throws Throwable
{
System.out.println(obj);
System.out.println(method.getName());
System.out.println(Arrays.toString(args));
return null;
}
}
配置文件,config.xml
<!--test_lookup -->
<bean id="person" class="siye.Person" scope="prototype" />
<bean id="userByImpl" class="siye.UserByImpl" />
<bean id="userByTag" class="siye.UserByTag">
<lookup-method name="createPerson" bean="person" />
</bean>
<context:component-scan base-package="siye" />
<!--test_methodReplace -->
<bean id="targetObjReplace" class="siye.TargetObjReplaceImpl" />
<bean id="targetObj" class="siye.TargetObj">
<replaced-method name="work" replacer="targetObjReplace">
<arg-type>java.lang.Integer</arg-type>
</replaced-method>
</bean>
测试类
package siye;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UnitTest
{
private ClassPathXmlApplicationContext context;
@Before
public void prepare()
{
String path = "classpath:/siye/config.xml";
context = new ClassPathXmlApplicationContext(path);
}
// 在beanA中每次需要beanB的时候,保证获取的是新的实例beanB.
/**
* 第一种方式,实现指定接口.(不推荐的方式)
* 实现ApplicationContextAware接口的方式
* 来进行方法的注入.
*/
@Test
public void testByImpl()
{
UserByImpl obj = context.getBean("userByImpl", UserByImpl.class);
System.out.println(obj.createPerson());
System.out.println(obj.createPerson());
System.out.println(obj.createPerson());
}
/**
* 第二种方式,使用标签`look-up`来实现.
*/
@Test
public void testByTag()
{
// 初始化的是cglib生成的子类,然后向上造型指向父类.
UserByTag obj = context.getBean("userByTag", UserByTag.class);
// siye.UserAbs$$EnhancerBySpringCGLIB$$1c841af2@17b7b6
System.out.println(obj);
System.out.println(obj.createPerson());
System.out.println(obj.createPerson());
System.out.println(obj.createPerson());
}
/**
* 第三种方式使用注解@Lookup来实现.
*/
@Test
public void testByAnno()
{
UserByAnno obj = context.getBean("userByAnno", UserByAnno.class);
System.out.println(obj);
System.out.println(obj.createPerson());
System.out.println(obj.createPerson());
System.out.println(obj.createPerson());
}
/*
* bean类方法的替换.
* 借用的也是cglib技术.
*/
@Test
public void testMethodReplace()
{
TargetObj obj = context.getBean("targetObj", TargetObj.class);
// 此时调用的是自己重写的方法,在MethodReplacer的实现类中.
obj.work(12);
}
@After
public void destroy()
{
if (context != null)
{
context.close();
}
}
}