写这个极其蛋疼,我一直在想我们用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

模仿Spring实现注解注入的更多相关文章

  1. spring中注解注入 context:component-scan 的使用说明

    通常情况下我们在创建spring项目的时候在xml配置文件中都会配置这个标签,配置完这个标签后,spring就会去自动扫描base-package对应的路径或者该路径的子包下面的java文件,如果扫描 ...

  2. 注解学习(模仿springMvc的注解注入方式)

    最近在看springMvc的源码,看到了该框架的注入注解的部分觉的有点吃力,可能还是对注解的方面的知识还认识的不够深刻,所以特意去学习注解方面的知识.由于本人也是抱着学习的态度来阅读源码,若文章在表述 ...

  3. Spring基于注解注入的两种方式

    1.@Autowried 1)默认基于类型查找容器的的Bean进行注入(注入的Bean的实现类是唯一的). 2)当实现类的Bean大于一个的时候,需结合@Qualifier,根据Bean的名称来指定需 ...

  4. Spring中注解注入bean和配置文件注入bean

    注解的方式确实比手动写xml文件注入要方便快捷很多,省去了很多不必要的时间去写xml文件 按以往要注入bean的时候,需要去配置一个xml,当然也可以直接扫描包体,用xml注入bean有以下方法: & ...

  5. spring 使用注解注入 list 或 map

    1.定义一个接口或抽象类AClass 2.定义两个类实现或继承AClass,(BClass,MClass) 3.在第三个类XClass 中注入List 形如: @Autowired private L ...

  6. Spring通过注解注入有参

    1.通过注解方式注入有参的构造函数 把@Autowired注解放在构造函数上方,在构造函数里写上需要注入的形参即可 2.通过XML配置文件方式定义有参构造函数

  7. Spring 使用注解注入 学习(四)

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  8. Spring使用注解方式注入多例的方式

    目前Spring+Netty的开发方式这么火热,想把Netty注册成Spring组件就一定得用多例的方式,我不由得想吐槽明明这么常见的需求网上相关博客都少的很,这里给出Spring使用注解注入多例的方 ...

  9. Spring 注解注入—@Qualifier 注释

    当创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean ...

随机推荐

  1. ionic时间插件ion-datetime-picker

    https://github.com/katemihalikova/ion-datetime-picker

  2. 白话ASP.NET MVC之一:Url 路由

    好久没有写关于ASP.NET MVC的东西了,虽然<ASP.NET MVC4框架揭秘>已经完完整整的看完一遍,但是感觉和一锅粥差不多,没什么可写的,因为我自己不理解,也就写不出来.现在开始 ...

  3. PHPsthdy+xdebug

    PHPsthdy下载后查看phpinfo后会发现没有xdebug这一项: 1.phpStudy集成了XDebug扩展,所以不用单独下载XDebug. 2.打开XDebug扩展:右击PHPstudy的图 ...

  4. iOS耗电量测试

    iOS耗电量测试 本文主要介绍的是使用Energy Diagnostics Instruments来进行iOS耗电量测试. 一.使用方法: 1)iOS 设置选项 ->开发者选项 ->log ...

  5. ExtJs的expand和collapse

    最近在研究ExtJs的窗口组件(Ext.window),关于扩展显示expand和折叠显示collapse的一点心得记录一下,以便后查. var win2 = new Ext.window({ id ...

  6. Python爬虫初学(三)—— 模拟登录知乎

    模拟登录知乎 这几天在研究模拟登录, 以知乎 - 与世界分享你的知识.经验和见解为例.实现过程遇到不少疑问,借鉴了知乎xchaoinfo的代码,万分感激! 知乎登录分为邮箱登录和手机登录两种方式,通过 ...

  7. 抵制克苏恩[Lydsy2017年4月月赛]

    题目描述 小Q同学现在沉迷炉石传说不能自拔.他发现一张名为克苏恩的牌很不公平.如果你不玩炉石传说,不必担心,小Q同学会告诉你所有相关的细节.炉石传说是这样的一个游戏,每个玩家拥有一个 30 点血量的英 ...

  8. LAP+mysql-主从+redis

    Redis是一个开源的,内存中的数据结构存储系统,他可以用作数据库,缓存和消息中间介.支持多种类型数据库结构,如字符串(strings),散列(hashes),列表(lists),集合(sets),有 ...

  9. Git时光机穿梭

    我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容: Git is a distributed version c ...

  10. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...