模仿Spring实现注解注入
写这个极其蛋疼,我一直在想我们用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实现注解注入的更多相关文章
- spring中注解注入 context:component-scan 的使用说明
通常情况下我们在创建spring项目的时候在xml配置文件中都会配置这个标签,配置完这个标签后,spring就会去自动扫描base-package对应的路径或者该路径的子包下面的java文件,如果扫描 ...
- 注解学习(模仿springMvc的注解注入方式)
最近在看springMvc的源码,看到了该框架的注入注解的部分觉的有点吃力,可能还是对注解的方面的知识还认识的不够深刻,所以特意去学习注解方面的知识.由于本人也是抱着学习的态度来阅读源码,若文章在表述 ...
- Spring基于注解注入的两种方式
1.@Autowried 1)默认基于类型查找容器的的Bean进行注入(注入的Bean的实现类是唯一的). 2)当实现类的Bean大于一个的时候,需结合@Qualifier,根据Bean的名称来指定需 ...
- Spring中注解注入bean和配置文件注入bean
注解的方式确实比手动写xml文件注入要方便快捷很多,省去了很多不必要的时间去写xml文件 按以往要注入bean的时候,需要去配置一个xml,当然也可以直接扫描包体,用xml注入bean有以下方法: & ...
- spring 使用注解注入 list 或 map
1.定义一个接口或抽象类AClass 2.定义两个类实现或继承AClass,(BClass,MClass) 3.在第三个类XClass 中注入List 形如: @Autowired private L ...
- Spring通过注解注入有参
1.通过注解方式注入有参的构造函数 把@Autowired注解放在构造函数上方,在构造函数里写上需要注入的形参即可 2.通过XML配置文件方式定义有参构造函数
- Spring 使用注解注入 学习(四)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- Spring使用注解方式注入多例的方式
目前Spring+Netty的开发方式这么火热,想把Netty注册成Spring组件就一定得用多例的方式,我不由得想吐槽明明这么常见的需求网上相关博客都少的很,这里给出Spring使用注解注入多例的方 ...
- Spring 注解注入—@Qualifier 注释
当创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean ...
随机推荐
- ABP从入门到精通(1):aspnet-zero-core项目启动及各项目源码说明
一.ABP的简单介绍 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践 ...
- (转)Linux端口nmap和netstat命令
场景:弄不清楚端口和服务的关系,总觉得这个命令很有用但是还不清楚如何使用 1 linux端口与服务 1.1 安全概述 网络传输安全.操作系统安全.应用软件安全构成了整个网络应用的安全:其中应用软件安全 ...
- (转)浅析CSS——元素重叠及position定位的z-index顺序
多次在项目中遇到html页面元素的非期待重叠错误,多数还是position定位情况下z-index的问题.其实每次解决类似问题思路大致都是一样的,说到底还是对z-index的理解比较模糊,可以解决问题 ...
- CSS样式----盒子模型(图文详解)
盒子模型 盒子中的区域 一个盒子中主要的属性就5个:width.height.padding.border.margin.如下: width:内容的宽度.CSS中 width 指的是内容的宽度,而不是 ...
- Web测试与APP测试有哪些异同?
1.相同点 不管是传统行业的web测试,还是新兴的手机APP测试,都离不开测试的基础知识,即是不管怎么变,测试的原理依然会融入在这两者当中. 1)设计测试用例时,依然都是依据边界值分析法.等价类划分等 ...
- JSON风格指南
中文版:https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md 英文版:https://google.g ...
- 灵玖软件NLPIRParser智能文本聚类
随着互联网的迅猛发展,信息的爆炸式增加,信息超载问题变的越来越严重,信息的更新率也越来越高,用户在信息海洋里查找信息就像大海捞针一样.搜索引擎服务应运而生,在一定程度上满足了用户查找信息的需要.然而互 ...
- Linux常用脚本命令总结
基本操作 通用操作 1. export 显示所有的环境变量,也可以获取到某个变量的详细信息: export # 显示所有 echo $SHELL # 只显示SHELL 2. whereis 使用系统自 ...
- CronJobs
一.概念 CronJobs提供了在特定的时间或者间隔内处理业务逻辑的方法.一般创建一个Cronjob有两种方式,第一种是定义Java类,由Hybris生成脚本并加入数据库.第二种是直接编写gr ...
- MySQL开发指南
数据库开发是数据库管理系统(DBMS)和数据库应用软件设计研发的总称,数据运维.参与数据库生产环境的问题优化和解决等方面的事宜. 1.关于MySQL数据库 2.搭建MySQL环境 3.入门常用SQL. ...