spring ioc---DI进阶之方法的注入和替换

版权声明:仅供学习交流使用 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();
		}
	}
}

猜你喜欢

转载自blog.csdn.net/drxRose/article/details/84938810