写这个极其蛋疼,我一直在想我们用SSM写项目时,写Service和Controller的时候,会给Service和Controller私有属性,比如Service需要dao,Controller需要Service,但是我们没有写过setter方法,而且也没有写带参构造器。那么它是怎么注入的呢?
我绞尽脑汁,用了Field类的 set(Object,Object)办法,发现不能对private修饰的私有属性进行注入,其实我已经很接近答案了。但是!我辗转了一个晚上,才知道setAccessible(boolean)这个方法可以强行干进去。。。
不说了,一会儿还要睡觉,直接上代码。我用的是注解注入的,而且稍微修改了昨晚mapper的代码,最后目录被我改成这样了。。。
把昨晚的DaoProxy改成了DaoFactory。我其实是想写工厂模式的,但是我没有这个经验,现在先把基础功能写起来,后面再看看设计模式改造一下吧。。。
下面这些接受困难的可以先移步
模仿Mybatis用mapper.xml实现Dao层接口的功能
最后DaoProxy被我改成了这样。。。暴露出来的getBean方法里的参数是在xml文件中读取的。。。
package com.yck.yebatis; import java.io.File; import java.io.FileFilter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.yck.bean.Function; import com.yck.bean.MapperBean; import com.yck.exception.NoConfigFileException; import com.yck.util.DataUtil; public class DaoFactory implements BeanFactory { private static Map<String,Object> beans; static { configDao(); } @Override public Object getBean(String beanName) //暴露获取Bean的方法 { return beans.get(beanName); } private static void configDao() //初始化 { Map<String,Object> map = new HashMap<String,Object>(); try { File[] files = getAllFiles(); for(File f:files) { MapperBean mapper = readMapper(f.getPath()); Object obj = implDao(mapper); map.put(mapper.getProxyName(), obj); } } catch (NoConfigFileException e) { // TODO Auto-generated catch block e.printStackTrace(); } beans = map; } /** * 得到所有的mapper.xml文件 * @return * @throws NoConfigFileException */ private static File[] getAllFiles() throws NoConfigFileException { File configPath = new File("src/mapper"); //为了简单起见,规定写在该目录下 FileFilter fileFilter = new FileFilter() //写过滤器,筛选后缀为.xml的文件 { @Override public boolean accept(File pathname) { String str = pathname.getName().toLowerCase(); if(str.endsWith(".xml")) return true; return false; } }; File[] files = configPath.listFiles(fileFilter); if(files == null || files.length == 0) //如果没有这样的文件,抛出异常 { files = null; throw new NoConfigFileException(); } return files; } /** * 通过读取配置文件实现dao接口 * @param path * @return */ private static Object implDao(MapperBean mapper) { ClassLoader classLoader = DaoFactory.class.getClassLoader(); Class<?> interfaze = null; try { interfaze = classLoader.loadClass(mapper.getInterfaceName()); //加载一个接口类 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } /** * 下面这几句相当重要了,是用xml文件实现dao接口的核心代码,因为数据库相关的大量工作我之前都写过了,所以这个看起来代码量很少 * 我也不太懂下面这个东西,我查API查了相当久,一开始写总是错,因为我理解错了InvocationHandler接口下那个方法的Object数组参数 * 它应该理解为一个可变长数组,而不是必须为数组 */ Object instanze = Proxy.newProxyInstance(classLoader, new Class[]{interfaze}, new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { List<Function> list = mapper.getList(); Object obj = null; for(Function f:list) { if(f.getFuncName().equals(method.getName())) { /** * 判断是不是select语句,是则调用DateUtil的select方法 * 否则调用update的方法 */ if(f.getSqltype().equals("select")) { if("java.util.ArrayList".equals(f.getResultType())) { if(f.isParameter()) obj = DataUtil.selectForBeanList(Class.forName(f.getResultOf()), f.getSql(), args); else obj = DataUtil.selectForBeanList(Class.forName(f.getResultOf()), f.getSql()); } else { if(f.isParameter()) obj = DataUtil.selectForBean(Class.forName(f.getResultType()), f.getSql(), args); else obj = DataUtil.selectForBean(Class.forName(f.getResultType()), f.getSql()); } } else { if(f.isParameter()) obj = DataUtil.updata(f.getSql(), args); else obj = DataUtil.updata(f.getSql()); } } } return obj; } }); return instanze; //返回这个接口,即mapper.getInterfaceName()这个接口 } /** * 读取xml文件的信息 * @param path * @return */ private static MapperBean readMapper(String path) { File file = new File(path); SAXReader reader = new SAXReader(); MapperBean mapper = new MapperBean(); try { Document doc = reader.read(file); Element root = doc.getRootElement(); //读取根节点 即dao节点 mapper.setInterfaceName(root.attributeValue("class").trim()); //把dao节点的class值存为接口名 mapper.setProxyName(root.attributeValue("id").trim()); //把id值设为代理名 List<Function> list = new ArrayList<Function>(); //用来存储方法的List for(Iterator<?> rootIter = root.elementIterator();rootIter.hasNext();) //遍历根节点下所有子节点 { Function fun = new Function(); //用来存储一条方法的信息 Element e = (Element) rootIter.next(); String sqltype = e.getName().trim(); String funcName = e.attributeValue("id").trim(); String sql = e.getText().trim(); String resultType = e.attributeValue("resultType").trim(); String resultOf = ""; if("java.util.ArrayList".equals(resultType)) resultOf = e.attributeValue("resultOf").trim(); String parameter = e.attributeValue("parameter"); fun.setSqltype(sqltype); fun.setFuncName(funcName); fun.setResultType(resultType); fun.setSql(sql); fun.setResultOf(resultOf); fun.setParameter("true".equals(parameter)); list.add(fun); } mapper.setList(list); } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } return mapper; }
下面写ServiceFactory我自己写了个注解
package com.yck.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface SelfInject { String value(); }
在Service类中使用了这个注解
package com.yck.service; import com.yck.annotation.SelfInject; import com.yck.bean.User; import com.yck.dao.IUserDao; public class UserService { @SelfInject(value="userdao") private IUserDao userdao; public User find(Integer id) { return userdao.selectById(id); } }
下面是ServiceFactory,先贴配置文件
<?xml version="1.0" encoding="UTF-8"?> <services> <service id="userService" class="com.yck.service.UserService"/> </services>
package com.yck.yebatis; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.yck.annotation.SelfInject; import com.yck.bean.ServiceBean; public class ServiceFactory implements BeanFactory { private static DaoFactory daoFactory = new DaoFactory(); private static Map<String,Object> beans; static { configService(); } /** * 暴露获取方法 */ @Override public Object getBean(String beanName) { return beans.get(beanName); } /** * 静态初始化方法 */ private static void configService() { Map<String,Object> map = new HashMap<String,Object>(); List<ServiceBean> list= getServiceBeans(); for(ServiceBean service:list) { map.put(service.getProxyName(), implService(service)); } beans = map; } /** * 读取所有的Service配置 * @return */ private static List<ServiceBean> getServiceBeans() { SAXReader reader = new SAXReader(); List<ServiceBean> list = new ArrayList<ServiceBean>(); try { Document doc = reader.read(new File("src/service/service.xml")); Element root = doc.getRootElement(); for(Iterator<?> iter = root.elementIterator("service");iter.hasNext();) { Element e = (Element) iter.next(); String proxyName = e.attributeValue("id"); String className = e.attributeValue("class"); ServiceBean bean = new ServiceBean(proxyName, className); list.add(bean); } } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } return list; } /** * 实例化Service并注入所需依赖 * @param service * @return */ public static Object implService(ServiceBean service) { Object obj = null; try { obj = Class.forName(service.getClassName()).newInstance(); Class<?> c = obj.getClass(); Field[] fields = c.getDeclaredFields(); for(Field f:fields) { Annotation[] annotations = f.getAnnotations(); for(Annotation a:annotations) { if(a instanceof SelfInject) { Object o = daoFactory.getBean(((SelfInject) a).value()); try { f.setAccessible(true); f.set(obj,o); f.setAccessible(false); } catch (IllegalArgumentException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return obj; } }
最后测试有没有注入
package com.yck.test; import com.yck.bean.User; import com.yck.dao.IUserDao; import com.yck.service.UserService; import com.yck.yebatis.DaoFactory; import com.yck.yebatis.ServiceFactory; public class Test2 { public static void main(String[] args) { DaoFactory bf = new DaoFactory(); IUserDao userdao = (IUserDao) bf.getBean("userdao"); User u = userdao.selectById(2); System.out.println(u); ServiceFactory sf = new ServiceFactory(); UserService userService = (UserService) sf.getBean("userService"); User us = userService.find(2); System.out.println(us); } }
最后查出结果,说明已经通过注解注入了
<*******************************************************************一条分割线*************************************************************************>
我发现还有个功能没有测试。。。我在写这个功能的时候是有考虑到多个dao的实现,所以我读取配置的时候写的是读取src/mapper目录下所有的以.xml结尾的文件,写依赖注入的时候也是读取<Services>下所有<service>子节点,所以我再加了一个表测试这个表如下和一些初始数据如下。
写了个Fruit的bean,我这就不贴了。。。。
下面是dao接口和service
package com.yck.dao; import java.util.List; import com.yck.bean.Fruit; public interface IFruitDao { int insert(String name,Integer numbers); int deleteById(Integer id); Fruit selectById(Integer id); Fruit updateNum(Integer nums,Integer id); List<Fruit> selectAll(); }
package com.yck.dao; import java.util.List; import com.yck.bean.Fruit; public interface IFruitDao { int insert(String name,Integer numbers); int deleteById(Integer id); Fruit selectById(Integer id); Fruit updateNum(Integer nums,Integer id); List<Fruit> getAll(); }
fruitdao的配置文件如下fruitdao.xml
<?xml version="1.0" encoding="UTF-8"?> <dao id="fruitdao" class="com.yck.dao.IFruitDao"> <select id="selectById" resultType ="com.yck.bean.Fruit" parameter="true"> select * from t_fruit where id = ? </select> <update id="updateNum" resultType = "java.lang.Integer" parameter="true"> update t_fruit set name=? where id=? </update> <delete id="deleteById" resultType = "java.lang.Integer" parameter="true"> delete from t_fruit where id=? </delete> <insert id="insert" resultType = "java.lang.Integer" parameter="true"> insert into t_fruit values(null,?,?) </insert> <select id="getAll" resultType = "java.util.ArrayList" resultOf="com.yck.bean.Fruit" parameter="false"> select * from t_fruit; </select> </dao>
在service.xml中注册FruitService教给ServiceFactory管理;(写到这里我突然想写一个配置只配置包的名称,然后自己去扫描生成Bean类的factory)
<?xml version="1.0" encoding="UTF-8"?> <services> <service id="userService" class="com.yck.service.UserService"/> <service id="fruitService" class="com.yck.service.FruitService"/> </services>
然后测试我们的Service,这里我就不测试Dao有没有实现了,因为只要service实现了,dao一定是没问题的
package com.yck.test; import com.yck.service.FruitService; import com.yck.service.UserService; import com.yck.yebatis.ServiceFactory; public class Test { public static void main(String[] args) { ServiceFactory sf = new ServiceFactory(); UserService us = (UserService) sf.getBean("userService"); FruitService fs = (FruitService) sf.getBean("fruitService"); System.out.println(us.findAll()); System.out.println(us.find(2)); System.out.println(fs.findAll()); System.out.println(fs.findById(2)); } }
测试结果如下,控制台输出
[User [id=, name=二蛋, age=], User [id=, name=王八蛋, age=], User [id=, name=李四, age=], User [id=, name=小明, age=], User [id=, name=小红, age=], User [id=, name=小张, age=], User [id=, name=李三, age=], User [id=, name=大牛, age=], User [id=, name=二牛, age=], User [id=, name=三牛, age=], User [id=, name=四牛, age=], User [id=, name=大牛, age=], User [id=, name=二牛, age=], User [id=, name=三牛, age=], User [id=, name=四牛, age=], User [id=, name=大牛, age=], User [id=, name=二牛, age=], User [id=, name=三牛, age=], User [id=, name=四牛, age=]] User [id=, name=王八蛋, age=] [Fruit [id=, name=apple, counts=], Fruit [id=, name=orange, counts=], Fruit [id=, name=葡萄, counts=], Fruit [id=, name=banana, counts=]] Fruit [id=, name=orange, counts=]
也就是说我之前写的功能完全没有问题,往后我们写的Dao接口只要在src/mapper下面添加一个.xml配置文件,
写Service 只要在 src/service/service.xml里面添加一个<service>子节点就能自动注册到Factory进行管理。这个功能目前为止就这样了
以上内容都是原创,如果转载请标注并标明来源于
大王让我写代码:http://www.cnblogs.com/yeyeck/p/7468644.html