版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/telrob/article/details/80065524
在使用spring 项目中一般会使用的mybatis,一个接口就可以查询出数据库中的数据,是不是感觉很神奇?好吧,今天就来揭开她神秘的面纱吧。
思路 :
- 项目启动时扫描特定的包或者特定注解的接口。
- 为对应的接口添加动态代理。
- 在接口调用时实现对应的方法。
好了废话少说,将已一个通过接口直接发送http请求的小项目来实现。
/**
* 基于注解发送http请求
* @author acer
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpConnect {
}
/**
* 用于请求参数
* @author acer
*
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpParam {
String name();
}
/**
* 在方法上注解,用于指明请求的方式,请求url,请求头
* @author acer
*
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HtttpHead {
public static final String GET="GET";
public static final String POST="POST";
String url();
String method() default GET;
String[] header() default{};
int timeout() default 5000;
}
/**
* 代理工厂
* @author acer
*
* @param <T>
*/
public class MyProxyFactory<T> implements FactoryBean<T> {
private Class<T> interfaceClass;
public Class<T> getInterfaceClass() {
return interfaceClass;
}
public void setInterfaceClass(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
@Override
public T getObject() throws Exception {
return (T) new MyProxy().bind(interfaceClass);
}
@Override
public Class<?> getObjectType() {
return interfaceClass;
}
@Override
public boolean isSingleton() {
// 单例模式
return true;
}
}
@Component
@Lazy(true)
public class MyRegistryBean implements ApplicationContextAware,BeanDefinitionRegistryPostProcessor{
private ApplicationContext ctx;
//最后执行
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//Map<String, Object> map=beanFactory.getBeansWithAnnotation(AnalysisActuator.class);
//for(String key:map.keySet()) {
// System.out.println("key:"+key);
// }
}
//第二执行
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 需要被代理的接口,可以通过扫描包来查找对应的类
List<Class>classList=new ArrayList<Class>();
ClassPathScanningCandidateComponentProvider p=new ClassPathScanningCandidateComponentProvider(false);
p.addIncludeFilter(new TypeFilter() {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
AnnotationMetadata an=metadataReader.getAnnotationMetadata();
Set<String> string=an.getAnnotationTypes();
String annName=HttpConnect.class.getName();
ClassMetadata classm=metadataReader.getClassMetadata();
if(string.contains(annName)) {
try {
classList.add(Class.forName(classm.getClassName()));
} catch (ClassNotFoundException e) {
}
}
return false;
}
});
p.findCandidateComponents("com.trilink.common");
for(Class<?>cls:classList) {
//Class<?> cls = Hello.class;
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName());
definition.setBeanClass(MyProxyFactory.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
// 注册bean名,一般为类名首字母小写
String name=cls.getName();
name=getClassName(name);
beanDefinitionRegistry.registerBeanDefinition(name, definition);
}
}
//最先执行
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
}
/**
* 将类转换成首字母小写的名字
* @param name
* @return
*/
public String getClassName(String name) {
int index=name.lastIndexOf(".");
if(index>0) {
name=name.substring(index+1,name.length());
}
char[] nameChar=name.toCharArray();
nameChar[0]=(char) (nameChar[0]+32);
name=new String(nameChar);
return name;
}
}
/**
* 代理类
* @author acer
*
*/
public class MyProxy implements InvocationHandler {
private Class<?> interfaceClass;
public Object bind(Class<?> cls) {
this.interfaceClass = cls;
return Proxy.newProxyInstance(cls.getClassLoader(), new Class[] {interfaceClass}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return processURL(method,args);
}
/**
* 处理请求
* @param method
* @param args
* @return
* @throws Exception
*/
private Object processURL( Method method, Object[] args) throws Exception {
//将参数转换成map集合以便转换成json
Map<String,Object>paramMap=new HashMap<String,Object>();
if(args!=null&&args.length>0) {
Annotation[][] ann=method.getParameterAnnotations();
if(ann!=null&&ann.length>0) {
for(int i=0;i<ann.length;i++) {
for(int j=0;j<ann[i].length;j++) {
Annotation a=ann[i][j];
if(a instanceof HttpParam) {
HttpParam an=(HttpParam)a;
paramMap.put(an.name(),args[i]);
}
}
}
}
}
//参数转换成json
Gson gson=new Gson();
String json=gson.toJson(paramMap);
//获取url
HtttpHead head=method.getDeclaredAnnotation(HtttpHead.class);
if(head==null) {
return "not request url";
}
String url=head.url();
String menthod=head.method();
int timeout=head.timeout();
String[] heads=head.header();
URL ul=new URL(url);
HttpURLConnection connect=(HttpURLConnection)(ul.openConnection());
connect.setRequestMethod(menthod);
connect.setDoOutput(true);
connect.setDoInput(true);
connect.setReadTimeout(timeout);
//设置json格式
connect.setRequestProperty("accept","application/json");
if(heads!=null) {
for(String h:heads) {
String[]d=h.split(":");
connect.setRequestProperty(d[0], d[1]);
}
}
if(!paramMap.isEmpty()) {
byte[] pm=json.getBytes();
int length=pm.length;
connect.setRequestProperty("Content-Length", length+"");
OutputStream out=connect.getOutputStream();
out.write(pm);
out.flush();
out.close();
}
if(connect.getResponseCode()==200) {
InputStream in=connect.getInputStream();
int len;
byte[] bb=new byte[1024];
ByteArrayOutputStream byteOut=new ByteArrayOutputStream();
while((len=in.read(bb))>0) {
byteOut.write(bb,0,len);
}
in.close();
byte[] data=byteOut.toByteArray();
byteOut.close();
//开始对结果集处理
Class<?>returnType=method.getReturnType();
if(returnType.equals(Map.class)) {
}else if(returnType.equals(byte[].class)||returnType.equals(Byte[].class)) {
return data;
}else if(returnType.equals(String.class)) {
return new String(data);
}else if(returnType.equals(int.class)||returnType.equals(Integer.class)) {
String rt=new String(data);
return Integer.parseInt(rt);
}else {
//当为一个对象时直接转换成对象
String rt=new String(data);
System.out.println("type:"+returnType.getName());
return gson.fromJson(rt, returnType);
}
}else {
throw new Exception("code:"+connect.getResponseCode());
}
return null;
}
}
@HttpConnect
public interface Users{
@AnalysisActuator
public String say();
@HtttpHead(url="http://localhost:8089/AppGoods/queryGoodsById",method=HtttpHead.POST)
public String hh(@HttpParam(name="goodsId")String oo,@HttpParam(name="li")String hhh);
}
@Controller
@RequestMapping
public class TestController {
@Autowired
private Users users;
@RequestMapping("/test")
@ResponseBody
public Object test() {
return users.hh("551155021248", "bbb");
}
@RequestMapping("/say")
@ResponseBody
public Object say() {
return users.say();
}
}
总结:在编写这个功能之前也不知如何下手,开发过程中也遇到很多问题,一下是个人心的,只有具体实现的类才能够获取到类参数的名称,这也是mybatis接口中为嘛要使用注解的原因,而spring 的controller 中是具体的方法,所以可以不用注解直接根据参数名称注入。