1. 程式人生 > >模仿Spring實現註解註入

模仿Spring實現註解註入

setresult fun mpls void tar 實例 attribute beans 配置文件

寫這個極其蛋疼,我一直在想我們用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);

        
        
    }

}

技術分享

最後查出結果,說明已經通過註解註入了

以上內容都是原創,如果轉載請標註並標明來源於

大王讓我寫代碼:http://www.cnblogs.com/yeyeck/p/7468644.html

模仿Spring實現註解註入