动态代理机制是jdk1.3版本以后引入的,在下面我主要用动态代理来实现接口,并且用注释来代替查询语句注释
DAO方法如下
package com.yf.dao;
import java.util.List;
import com.yf.annotation.Result;
import com.yf.annotation.SQL;
import com.yf.entity.Hero;
import com.yf.enums.HandlerType;
public interface HeroDao {
// 英雄查询总数量
@SQL("select count(*) from hero where power=#{powerId}")
@Result(handlerType = HandlerType.SCARLAR)
int getHeroCountByPowerId(int powerId);
// 根据势力ID查询英雄 #{。。。}为占位符
@SQL("select h.id id,h.name name ,p.name powername from hero h,power p where p.id=h.power and power=#{powerid} limit #{startnum},#{pageSize}")
@Result(handlerType = HandlerType.BEANLIST, javaType = Hero.class)
List selectHero(int powerid, int startnum, int pageSize);
}
定义常量类:在代码中尽量不要出现常量,常量被称为 魔鬼数字,因为在后期对代码进行修改时,会比较麻烦,然后当我们定义常量类以后,可以让我们轻松的修改常量
package constant;
//常量类
public class TypeConstant {
//请求地址后缀名
public static final String URI_SUFFIX=".action";
//类名后缀
public static final String MODEL_SUFFIX="Model";
//包名
public static final String MODEL_PACKAGE="com.yf.model";
//重定向的字符前缀
public static final String REDIRECT_PREFIX="redirect:";
}
定义注解的类
这是定义数据类型的注解类
package com.yf.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.yf.enums.HandlerType;
@Retention(RetentionPolicy.RUNTIME) //指定注解保留到程序运行的时间
@Target(ElementType.METHOD)//给方法进行注解
public @interface Result {
// 定义查询返回封装数据类型
HandlerType handlerType();
// 定义查询中的类类型
Class> javaType() default Object.class;
}
定义sql语句的注解类
package com.yf.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //指定注解保留到程序运行的时间
@Target(ElementType.METHOD)//给方法进行注解
public @interface SQL {
String value();
}
定义数据类型的枚举类
package com.yf.enums;
public enum HandlerType {
BEAN, BEANLIST, COLUMNLIST, ID, ROWS, SCARLAR
}
动态代理的方法
package com.yf.proxy;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import com.yf.annotation.Result;
import com.yf.annotation.SQL;
import com.yf.enums.HandlerType;
import com.yf.util.DBUtil;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import ognl.Ognl;
import ognl.OgnlContext;
public class DaoProxy {
@SuppressWarnings("unchecked")
/*
* 实现所有接口的动态代理
*/
// T为不确定什么类型,然后用T代替
public static T getDaoInstance(Class> calzz) {
// 定义加强器
Enhancer enh = new Enhancer();
// 设置要被代理的接口
enh.setSuperclass(calzz);
enh.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object arg1, Method method, Object[] objs, MethodProxy arg3) throws Throwable {
// method为被代理的方法,objs为方法中的参数值构成的数组
// 获取dao中的sql语句
SQL sqlannotation = method.getDeclaredAnnotation(SQL.class);
String sql = "";
if (sqlannotation != null) {
sql = sqlannotation.value();
}
// 获取result注解的结果类型
Class> javatype = null;
HandlerType handlertype = null;
// 从Dao接口中获取类型
Result resultannotation = method.getDeclaredAnnotation(Result.class);
if (resultannotation != null) {
javatype = resultannotation.javaType();
handlertype = resultannotation.handlerType();
}
QueryRunner qr = new QueryRunner(DBUtil.getDs());
System.out.println("查询前" + sql);
// 可以用Ognl工具来替代的Map集合
OgnlContext ognicontext = new OgnlContext();
// 通过反射获取方法中所有的参数
Parameter[] parameters = method.getParameters();
// 利用循环把参数加入到map集合中
for (int i = 0; i < parameters.length; i++) {
ognicontext.put(parameters[i].getName(), objs[i]);
}
// 寻找需要替换的占位符
Matcher matcher = Pattern.compile("(\\#|\\$)\\{(\\w*\\.)*\\w*\\}").matcher(sql);
// 找到需要替换的字符
while (matcher.find()) {
// 寻找占位符#{}
String paramstr = matcher.group();
Object paramValue = null;
// 判断字符串是不是以#开头
if (paramstr.startsWith("#")) {
// 获取参数值
paramValue = Ognl.getValue(paramstr.replace("#{", "").replace("}", ""), ognicontext);
} else {
paramValue = Ognl.getValue(paramstr.replace("${", "").replace("}", ""), ognicontext);
}
// 获取参数类型
Class> paramtype = paramValue.getClass();
if (paramtype == String.class) {
// 当参数为字符串时,加上''符号
if (paramstr.startsWith("#")) {
sql = sql.replace(paramstr, "'" + paramValue + "'");
} else {
sql = sql.replace(paramstr, String.valueOf(paramValue));
}
} else {
sql = sql.replace(paramstr, String.valueOf(paramValue));
}
}
System.out.println("更改后的语句" + sql);
// 调用DBUtil方法中返回结果
Object result = null;
// 结果类型不同,运用不同的方法,可以实现不同的sql语句
if (handlertype == HandlerType.BEAN) {
result = qr.query(sql, new BeanHandler<>(javatype));
}
if (handlertype == HandlerType.BEANLIST) {
result = qr.query(sql, new BeanListHandler<>(javatype));
}
if (handlertype == HandlerType.SCARLAR) {
result = qr.query(sql, new ScalarHandler<>());
}
if (handlertype == HandlerType.COLUMNLIST) {
result = qr.query(sql, new ColumnListHandler<>());
}
if (handlertype == HandlerType.ID) {
result = qr.insert(sql, new ScalarHandler<>());
}
if (handlertype == HandlerType.ROWS) {
result = qr.update(sql);
}
return result;
}
});
return (T) enh.create();
}
// 定义私有的构造方法
private DaoProxy() {
}
}
动态代理可以在不修改源码的情况下,对内容进行一些增加,因为很多软件或网址,几乎都会进行增加功能,这时动态代理就可以提供很大的方便,但是也有缺点,因为动态代理只能够代理接口,不可以代理类