模仿Spring实现注解注入

来源:互联网 发布:经典电影推荐 知乎 编辑:程序博客网 时间:2024/05/29 17:51

 

写这个极其蛋疼,我一直在想我们用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=1, name=二蛋, age=200], User [id=2, name=王八蛋, age=23], User [id=4, name=李四, age=66], User [id=5, name=小明, age=66], User [id=6, name=小红, age=66], User [id=7, name=小张, age=66], User [id=8, name=李三, age=66], User [id=9, name=大牛, age=88], User [id=10, name=二牛, age=77], User [id=11, name=三牛, age=66], User [id=12, name=四牛, age=55], User [id=13, name=大牛, age=88], User [id=14, name=二牛, age=77], User [id=15, name=三牛, age=66], User [id=16, name=四牛, age=55], User [id=17, name=大牛, age=88], User [id=18, name=二牛, age=77], User [id=19, name=三牛, age=66], User [id=20, name=四牛, age=55]]User [id=2, name=王八蛋, age=23][Fruit [id=1, name=apple, counts=5], Fruit [id=2, name=orange, counts=10], Fruit [id=3, name=葡萄, counts=20], Fruit [id=4, name=banana, counts=25]]Fruit [id=2, name=orange, counts=10]

也就是说我之前写的功能完全没有问题,往后我们写的Dao接口只要在src/mapper下面添加一个.xml配置文件,

写Service 只要在 src/service/service.xml里面添加一个<service>子节点就能自动注册到Factory进行管理。这个功能目前为止就这样了

 

以上内容都是原创,如果转载请标注并标明来源于

大王让我写代码:http://www.cnblogs.com/yeyeck/p/7468644.html

 

原创粉丝点击