依赖注入

控制反转与依赖注入思想

什么是控制反转?什么是依赖注入?有一个文章介绍得很好。说一家公司要选择自来水公司,老板先选择自来水公司A,后来又改成自来水B,这样改来改去浪费老板精力,所以就干脆让手下人全权负责,这样控制权就从老板手中转到手下人。这就是控制反转。依赖注入就是控制反转的另一种说法,意思就是现在老板只要伸手要水,水就注入到他手上,至于选择哪个公司的水由张三来确实,在Spring中其实就是xml文件。依赖注入的方式就是确定调用那个公司自来水的方法。有三种方法
(三种方式摘自: 体验控制反转的理念
1 接口注入
接口注入就是将要注入的内容置入到一个接口中,然后将其注入到它的实现类中,因为实现一个接口必须实现接口定义的所有方法。比如为可以为上面的例子再定义一个接口:
public interface IGetCompany {
//取得一家具体的公司
public IWaterCompany getCompany(IWaterCompany wcp);
}
当一个类实现了这个几接口,就意味着它被注入了 getCompany 这个方法,且必须实现它哪怕是空实现,这是强加给它的实现类的。如下面这个实现类:
public class GetCompanyImpl implements IGetCompany {
//实现 getCompany 方法,且必须实现,它是 IgetCompany 注入给它的一个方法。
public IWaterCompany getCompany(IWaterCompany wcp) {
return wcp;
}
}
这里的 getCompany 只是把从调用者传入的参数原封不动的返回去。
2 set 注入
所谓 set 注入就是注入者通过调用 setter 方法将一个对象注入进去,如上面的 Boss类。
public class Boss {
//以接口为属性
private IWaterCompany wcp;
public void needWater(){
//如愿以偿的得到了水服务。
getWcp().getWater();
}
public IWaterCompany getWcp() {
return wcp;
}
//实现 IwaterCompany 接口的所有类的实例都可以做为参数传递进来
public void setWcp(IWaterCompany wcp) {
this.wcp = wcp;
}
Boss 类中含有一个 wcp 属性,要想给这个属性注入一个具体的对象值,那么就可以通过 setWcp 这个方法做到。具体办法如前面示例中的 TestClient 类:
 TestClient 类:
public class TestClient {
public static void main(String[] args) {
Boss boss=new Boss();
//先实例化一个 WaterCompanyA 对象,然后将它注入进去
boss.setWcp(new WaterCompanyA());
//得到了 A 公司提供的水服务
boss.needWater();
}
}
注意到 TestClient 首先实例化一个 Boss 对象,然后通过调用它的 setWcp 方法将一个 WaterCompanyA 实例注入给了 boss 的属性 wcp,这个过程就叫 set 注入。

//接口的命名一般习惯上以 I 开头
public interface IWaterCompany {
//提供水服务
public Water getWater();
//
}
public class WaterCompanyA implements IWaterCompany{
private Water water;
//这个方法是接口中定义的方法必须实现
public Water getWater() {
return water;
}
}


//实现 IwaterCompany 接口
public class WaterCompanyB implements IWaterCompany{
private Water water;
/
/这个方法是接口中定义的方法必须实现
public Water getWater() {
return water;
}
}
同时体验接口的神奇,如果没有用接口,分别创建公司A和B类,boss类里还要改来改去3 构造注入构造注入是通过一个带参的构造函数将一个对象注入进去。上面的 Boss 类是通过set 方法进行依赖注入的,那么也很容易改为构造注入方法如下:
 
  
public class Boss {
private IWaterCompany wcp;
/
/构造注入
public Boss(IWaterCompany wcp){
this.wcp=wcp;
}
}
可见这个类中不再有 set 方法,而是直接将参数写在了构造函数中。那么在实例化一个 Boss 类的时候就可以将一个 IWaterCompany 实例注入给 Boss。这个过程就叫构造注入。三种注入方式中,构造注入与 set 注入比较类似,那么什么时候用 set 注入,什么时候用构造注入呢?这就取决于注入的先后是否对其它业务逻辑有影响,如果在一个业务逻辑中,属性的值必须在最先时候就要初始化,那么使用构造注入会是一个较好的选择,否则如果是在一种动态的不确定的环境下需要注入一个对象,但又没有必要调用构造函数而造成实例化出一个多余的对象的情况下,使用 set 注入会比较自然。
-----------------------------------------------------------------项目应用-------------------------------------------------------------------------
现在我们可以运用到上一篇文章的结尾,改进它(可以不用看上篇),自动注入(用父类),集合了工厂模式、反射和依赖注入的例子
package com.yan.factory.dao;



import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class DaoUtil {
	public static void diDao(Object obj){
		try {  //获取所有自定义函数
			Method[]ms=obj.getClass().getDeclaredMethods();
		     for(Method m:ms){
			String mn=m.getName();
			if(mn.startsWith("set")){//如果方法是setXXXX()方法,使用set注入,即创建XXXX实例作为参数
			     mn=mn.substring(3, 4).toLowerCase()+mn.substring(4);//比如获取的是UserDao配置文件里是userdao要转化
			     Object o=DaoUtil.createDaoFactory().getDao(mn);//创建XXXX实例o
			     m.invoke(obj, o);//obj调用setXXXX(o);
				}
			}
		} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static IFactoryDao createDaoFactory() {
		IFactoryDao f = null;
		try {
			Properties prop = PropertiesUtil.getDaoProp();
			String fs = prop.getProperty("factory");
			Class clz = Class.forName(fs);
			String mn = "getInstance";
			Method m = clz.getMethod(mn);
			f = (IFactoryDao)m.invoke(clz);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return f;
	}
}

package com.yan.factory.dao;

public class BaseTest {
	public BaseTest(){
		DaoUtil.diDao(this);
	}
}

package com.yan.factory.dao;



import org.junit.Test;

public class testAddressDao extends BaseTest{
	private IAddressDao addressDao;

	public IAddressDao getAddressDao() {
		return addressDao;
	}

	public void setAddressDao(IAddressDao addressDao) {
		this.addressDao = addressDao;
	}
	@Test
	public void test(){
		addressDao.delete(1);
	}
	
}
public class PropertiesFactory implements IFactoryDao {
	private static PropertiesFactory f = new PropertiesFactory();
	
	private PropertiesFactory() {	}
	public static IFactoryDao getInstance() {
		return f;
	}
	@Override
	public Object getDao(String name) {
		try {
			
			Properties prop = PropertiesUtil.getDaoProp();
			String cn = prop.getProperty(name);
			System.out.println(cn);
			Object obj = Class.forName(cn).newInstance();
			
			System.out.println(obj);
			return obj;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
		
		
	}

}


factory=com.yan.factory.dao.PropertiesFactory
userDao=com.yan.factory.dao.UserJDBCDao
addressDao=com.yan.factory.dao.AddressJDBCDao
-----------------------------------------------------基于Annotation-------------------------------------------------------------------
上述StartWith("set")还是有弊端的,因为有可能恰好有一个方法也是set开头,可我们却不想利用它注入,这个时候我们可以用Annotation。创建一个Annotation,然后给要注入的setXXXX方法标志
package com.yan.factory.dao;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface ShopDi {
	String value() default "";
	
	
}
附Annotation知识(可跳过):

public @interface ShopDi{

     //表示为这个Annotation加上一个属性值,如果没有定义default,必须在该annotation中定义这个属性

   //即@ShopDi(abc="ss")

      String abc() default "";

     //value是Annotation默认属性,在定义的时候可以不用value=""定义。而是直接@ShopDi("hello"),特别注意当定义两个属性时,默认属性(刚刚那种形式)就不起作用了

    //即@ShopDi(value="ss",abc="123")

     String value() default "";



}


package com.yan.factory.dao;



import org.junit.Test;

public class testAddressDao extends BaseTest{
	private IAddressDao addressDao;

	public IAddressDao getAddressDao() {
		return addressDao;
	}
	@ShopDi("addressDao")
	public void setAddressDao(IAddressDao addressDao) {
		this.addressDao = addressDao;
	}
	@Test
	public void test(){
		addressDao.delete(1);
	}
	
}

public static void diDao(Object obj) {
		try {
			//获取所有方法
			Method[] ms = obj.getClass().getDeclaredMethods();
			for (Method m : ms) {
				//判断方法上是否有ShopDi的Annotation,有这个才注入
				if (m.isAnnotationPresent(ShopDi.class)) {
					//获取方法上的ShopDi对象
					ShopDi sd=m.getAnnotation(ShopDi.class);
					//获取ShopDi的value值
					String mn=sd.value();
					if(mn==null||"".equals(mn)){
                                        //想根据名字注入,直接截取XXXX部分,这样就可以直接标志@Shop,而不用@ShopDi("addressDao"),反之这样写                                         //类名可以相对随意写
						mn=mn.substring(3, 4).toLowerCase()+mn.substring(4);
					}
					Object o = DaoUtil.createDaoFactory().getDao(mn);
					m.invoke(obj, o);
				}
			}
		} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值