造一个方形轮子文章目录:造一个方形的轮子
01、先把车正过
在上一篇《造一个方形的轮子4--依赖注入》的最后提出了一个问题,按类型简称(不带包名)以及按注解上设置的Bean名字去初始化Bean的时候都会有覆盖问题,比如不同包下的相同的类,或者在注解上设置了相同Bean名字的类,解决方法不允许重复就可以了,暴力一点有重名的直接抛异常。
BeansInitUtil.java 修改loadClass方法:
//......上略
private static void loadClass(File file, Map<String, BeanObject> map){
....
// 按类设置bean
map.put(beanObject.getClassName(), beanObject);
String simpleName = firstToLowerCase(beanObject.getSimpleName());
// 这里添加判断
if(map.get(simpleName) != null){
throw new SquareBeanInitException("There are duplicate beans ,beanName:"+simpleName);
}
map.put(simpleName, beanObject);
// 按注解输入value设置bean
for (Annotation annotation : annotations) {
String tmp_name = "";
if(annotation instanceof Service){
tmp_name = ((Service)annotation).value();
} else if(annotation instanceof Component) {
tmp_name = ((Component)annotation).value();
}
if(tmp_name != null && !tmp_name.equals("")) {
// 这里添加判断
if(map.get(tmp_name) != null){
throw new SquareBeanInitException("There are duplicate beans ,beanName:"+tmp_name);
}
map.put(tmp_name, beanObject);
}
}
}
} catch (Exception e) {
log.error("init bean error:{}", file.getPath(), e);
}
}
//......下略
复制一个com.jisuye.service.impl.AbcImpl到com.jisuye.service包启动程序查看日志输出:
12:48:17.058 [main] ERROR com.jisuye.core.SquareApplication - There are duplicate beans ,beanName:abcImpl
12:48:17.060 [main] ERROR com.jisuye.core.SquareApplication - Application startup failed...
看日志输出了重复的bean异常提示,现在删除刚复制的AbcImpl.java,将原来com.jisuye.service.impl.DefImpl及com.jisuye.service.impl.DefImpl都使用@Service("def") 注解,然后启动程序查看日志输出:
12:53:53.801 [main] ERROR com.jisuye.core.SquareApplication - There are duplicate beans ,beanName:def
12:53:53.803 [main] ERROR com.jisuye.core.SquareApplication - Application startup failed...
也输出了输出了重复的bean异常提示,验证完毕。
02、数据库支持准备
数据库支持就以mysql为例,添加一个jdbcTemplate类似的功能。
首先引入数据库链接池,这们就不用管链接的问题了,使用的是HikariCP链接池,在pom.xml中添加依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${hikariCP.version}</version>
</dependency>
配置文件中添加数据库相关配置
square:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true
username: root
password: 123456
03、DbUtil工具类
将创建数据库链接池及相关方法封装到统一的工具类DbUtil.java:
package com.jisuye.util;
//import ...
/**
* 数据库操作工具
* @author ixx
* @date 2019-07-01
*/
public class DbUtil {
private static final Logger log = LoggerFactory.getLogger(DbUtil.class);
private static Connection connection;
/** 初始化方法*/
public static void init(){
try {
String url = ApplicationContext.getConf("square.datasource.url").toString();
String username = ApplicationContext.getConf("square.datasource.username").toString();
String password = ApplicationContext.getConf("square.datasource.password").toString();
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url);
ds.setUsername(username);
ds.setPassword(password);
// HikariCP提供的优化设置
ds.addDataSourceProperty("cachePrepStmts", "true");
ds.addDataSourceProperty("prepStmtCacheSize", "250");
ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
connection = ds.getConnection();
} catch (Exception e) {
log.error("mysql connection init error..", e);
throw new SquareBeanInitException("mysql connection init error....");
}
}
/** install/update 带参数占位符方法*/
public static boolean update(String sql, Object... params){
PreparedStatement statement;
try {
statement = connection.prepareStatement(sql);
if(params != null) {
for (int i = 1; i <= params.length; i++) {
statement.setObject(i, params[i - 1]);
}
}
return statement.execute();
} catch (Exception e) {
log.error("install/update exception.", e);
}
return false;
}
/** install/update 无参数占位符方法*/
public static boolean update(String sql){
return update(sql, null);
}
/**
* 通用查询方法
* @param sql sql语句
* @param clazz 返回列表类型
* @param params 参数列表
* @param <T> 返回列表类型
* @return
*/
public static <T> List<T> select(String sql,Class<T> clazz, Object... params){
List<T> list = new ArrayList<>();
PreparedStatement statement;
try {
statement = connection.prepareStatement(sql);
for(int i=1; i<= params.length; i++){
statement.setObject(i, params[i-1]);
}
ResultSet rs = statement.executeQuery();
while(rs.next()){
T t = clazz.newInstance();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(method.getName().startsWith("set")){
String field = BeansInitUtil.firstToLowerCase(method.getName().substring(3));
method.invoke(t, rs.getObject(field));
}
}
list.add(t);
}
} catch (Exception e) {
log.error("select exception.", e);
}
return list.size()>0 ? list : null;
}
}
init方法用来初始化链接池,update方法执行insert/update/delete语句,select处理查询并简单使用反射封装了一下Entity。
04、添加JdbcTemplate
JdbcTemplate类,只是封装了一下DbUtil的方法,提供给其它service使用,代码如下:
package com.jisuye.core;
//import ...
/**
* 添加数据库支持(默认不加载该Bean,有其它Bean引用时再加载)
* @author ixx
* @date 2019-07-05
*/
public class JdbcTemplate {
public int insert(String sql){
return DbUtil.update(sql) ? 1 : 0;
}
public int insert(String sql, Object... params){
return DbUtil.update(sql, params) ? 1 : 0;
}
public int update(String sql, Object... params){
return DbUtil.update(sql, params) ? 1 : 0;
}
public int update(String sql){
return DbUtil.update(sql) ? 1 : 0;
}
public int delete(String sql){
return DbUtil.update(sql) ? 1 : 0;
}
public int delete(String sql, Object... params){
return DbUtil.update(sql, params) ? 1 : 0;
}
public <T> List<T> select(String sql,Class<T> clazz, Object... param){
return DbUtil.select(sql, clazz, param);
}
}
修改依赖注入部分代码,因为做的是框架,要考虑不使用DB的情况,所以jdbcTemplate默认不初始化,如果有其它Service添加了JdbcTemplate的依赖,再去初始化。防止直接初始化时,没有配置DB相关配置报错的情况。
BeansInitUtil.initDI()方法做如下修改(只保留了大体结构,具体查看=======标记中间的部分):
private static void initDI(Map<String, BeanObject> map){
List<Object> beanList = new ArrayList<>();
BeanObject sqlBean = null;
// 循环所有Bean处理依赖
for(Map.Entry entry : map.entrySet()){
// ...
// 先判断是否有Resource注解
for (Field field : beanObject.getFields()) {
if(filterFieldAnnotation(field.getAnnotations())){
String name = getResourceName(field.getAnnotations());
BeanObject bean = null;
// 有指定bean名字按指定去取
if(name != null && !name.equals("")){
bean = map.get(firstToLowerCase(name));
} else {
// ...
// 如果有next说明是有多个实现的接口,则要判断名字
if(bean != null && bean.getNext() != null){
// ...=============================注意下边这部分=============
} else if(fieldClass.getName().equals(JdbcTemplate.class.getName())){
// 如果是JdbcTemplate依赖,则初始化DbUtil并初始化及注入JdbcTemplate
if(sqlBean == null) {
DbUtil.init();
sqlBean = new BeanObject();
sqlBean.setClass(JdbcTemplate.class);
sqlBean.setObject(new JdbcTemplate());
}
bean = sqlBean;
}
// ...=============================注意上边这部分=============
}
if(bean == null){
// ...
}
// 注入依赖
// ...
}
}
}
map.put(JdbcTemplate.class.getName(), sqlBean);
}
05、验证数据库支持
表abc的表结构:
字段名 | 字段类型 | 备注 |
---|---|---|
id | int | 自增id主健 |
name | varchar(255) | 姓名 |
修改DefImpl.java,添加JdbcTemplate操作:
@Service
public class DefImpl implements Def {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public String exe(String name) {
List<AbcEntity> list = jdbcTemplate.select("select * from abc where name=?", AbcEntity.class, name);
System.out.println(list.size());
System.out.println(list.get(0).getId());
System.out.println(list.get(0).getName());
return "Interface DI... "+name;
}
}
修改AbcImpl.java, 添加JdbcTemplate操作:
@Service
public class AbcImpl implements Abc {
// 名字对不上会报异常
@Resource
private Def defImpl;
// 名字对不上可以使用注解中指定bean名字的方式
@Resource(name = "def2Impl")
private Def defByName;
// 添加jdbcTemplate依赖
@Resource
private JdbcTemplate jdbcTemplate;
// 注入Class类实例
@Resource
private ClassDi classDi;
@Override
public int test(String name) {
jdbcTemplate.insert("insert into abc(`name`) values('ixx')");
System.out.println(defImpl.exe(name));
System.out.println(defByName.exe(name));
System.out.println(classDi.exe(name));
return 0;
}
}
添加AbcEntity.java,查询结果使用
package com.jisuye.service;
public class AbcEntity {
private Integer id;
private String name;
// getter and setter...
}
原来的SquareApplication中有查看bean是否注入成功片段, 所以直接启动项目即可验证。
public static void run(Class clzz, String[] args) {
try {
// ...
//查看bean是否注入成功
Abc abc = (Abc)(ApplicationContext.getBean("abcImpl").getObject());
abc.test("ixx");
// ...
} catch (Exception e){
// ...
}
}
查看控制台输出:
18:18:39.761 [main] INFO com.jisuye.core.SquareApplication - beans size is:11
1
1
ixx
Interface DI... ixx
def2 ixx
Class DI ixx
查看数据库记录:
id | name |
---|---|
1 | ixx |
说明插入及查询成功。
06、翻车时间
数据库这还好,虽然没有打包在其实项目引用测试,这个等下一篇添加完web支持后一起验证。
发现一个注入的问题,当时处理注入是循环的整个容器里的bean去做解析,但很明显,容器中的bean是多对一的关系,多个key对应的 都是同一个Bean 如果使用循环的方式,就至少会多一倍解析处理..这个确实不好接受,Bean的数量少还可以,多了会延长程序的启动时间,这是不能接受的。解决方式其实也简单,下一篇再处理一下吧。
本篇代码地址: https://github.com/iuv/square/tree/square5
本文作者: ixx
本文链接: http://jianpage.com/2019/07/08/square5
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!