最近在看sPRingMvc的源码,看到了该框架的注入注解的部分觉的有点吃力,可能还是对注解的方面的知识还认识的不够深刻,所以特意去学习注解方面的知识。由于本人也是抱着学习的态度来阅读源码,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。
1,首先定义三个常用的注解Service,Autowired,Contrller;(主要的解释都在代码中有,在这里就不多陈述)
Service:
packagecom.lishun.Annotation;importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/*Description:
* @Target:指定注解的使用范围(指的是,在哪些类型可以使用该注解:Service注解只能在类,接口(包括注解类型)或enum等使用)
* 可选值:
* 可选的值在枚举类 ElemenetType 中,包括:
ElemenetType.CONSTRUCTOR 构造器声明
ElemenetType.FIELD 域声明(包括 enum 实例)
ElemenetType.LOCAL_VARIABLE 局部变量声明
ElemenetType.ANNOTATION_TYPE 作用于注解量声明
ElemenetType.METHOD 方法声明
ElemenetType.PACKAGE 包声明
ElemenetType.PARAMETER 参数声明
ElemenetType.TYPE 类,接口(包括注解类型)或enum声明
**/@Target(ElementType.TYPE)/*Description:
* @Retention :表示在什么级别保存该注解信息
* 可选的参数值在枚举类型 RetentionPolicy 中,包括:
RetentionPolicy.SOURCE 注解将被编译器丢弃
RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃
RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
**/@Retention(RetentionPolicy.RUNTIME)/*@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。
* 在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
**/@Documentedpublic@interfaceService {/*@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。
* 方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。
* 可以通过default来声明参数的默认值。*/String value()default"this is service annotation";
}
Autowired:
packagecom.lishun.Annotation;importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Inherited;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)public@interfaceAutowired {publicString value()default"no description";
}
Contrller:
packagecom.lishun.Annotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)public@interfaceContrller {
String value()default"this is contrller annotation";
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2:javaBean数据池-BeanFactory:主要存放含有注解的类;
packagecom.lishun.factory;importjava.util.HashMap;importjava.util.Map;/*** Description:存放所有bean的数据池
*@authorlishun
*@since2015-09-10*/publicclassBeanFactory {privatestaticMap<String, Object> map =newHashMap<String, Object>();publicstaticvoidaddBean(String beanName, Object bean) {
map.put(beanName, bean);
}publicstaticObject getBean(String beanName)throwsException {
Object o=map.get(beanName);if(o !=null) {returno;
}else{thrownewException("未注入的类型:" +beanName);
}
}publicstaticBoolean containsBean(String beanName){returnmap.containsKey(beanName);
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3:编写处理注解的核心代码(这里涉及的主要知识是反射,如果反射知识不够熟练的话建议先学习反射方面的知识),主要涉及的两个类是注解驱动(AnnotationDriven)和注解扫描类(PackUtils-这个类主要的是扫描包名下所有的类(如com.lishun,就是扫描该包下所有的类),代码主要是来自网络)
AnnotationDriven:
packagecom.lishun.utils;importjava.lang.annotation.Annotation;importjava.lang.reflect.Field;importjava.util.List;importcom.lishun.Annotation.Autowired;importcom.lishun.Annotation.Contrller;importcom.lishun.Annotation.Service;importcom.lishun.factory.BeanFactory;/*** Description:注入驱动类,所有的注解注入都在这里实现(这里只实现了通过类型来注入值,其他方式没实现,其实代码都是差不多了,有兴趣的可以自行脑补)
*@authorlishun
**/publicclassAnnotationDriven {publicstaticvoidannotationDriven(String packName)throwsException {//注入Service和ContrllerList<Class<?>> classSaveServicePaths =PackUtils
.getClassListByAnnotation(packName, Service.class);
List<Class<?>> classSaveContrllerPaths =PackUtils
.getClassListByAnnotation(packName, Contrller.class);
saveBean(classSaveServicePaths);
saveBean(classSaveContrllerPaths);//注入AutowiredList<Class<?>> classInjectPaths =PackUtils.getClassListByAnnotation(
packName, Autowired.class);
inject(classInjectPaths);
}privatestaticvoidsaveBean(List<Class<?>>classSavePaths)throwsInstantiationException, IllegalaccessException {for(Class<?>classPath : classSavePaths) {try{
Class c=Class.forName(classPath.getName());
Object o=c.newInstance();//扫描的到的含有注解的类实例化后保存在池中BeanFactory.addBean(classPath.getName(), o);
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}privatestaticvoidinject(List<Class<?>> classInjectPaths)throwsException {
Object o=null;for(Class<?>classInjectPath : classInjectPaths) {
Class c=Class.forName(classInjectPath.getName());//判断存放bean的池中是否存在该beanif(BeanFactory.containsBean(classInjectPath.getName())) {
o=BeanFactory.getBean(classInjectPath.getName());
}else{
o=c.newInstance();
}
Field[] fields=c.getDeclaredFields();for(Field field : fields) {
Annotation[] annotations=field.getAnnotations();for(Annotation annotation : annotations) {//判断是否是通过类型注解注入if(annotationinstanceofAutowired) {
Class classField=field.getType();
Object clazz=BeanFactory
.getBean(classField.getName());
field.set(o, clazz);
BeanFactory.addBean(classInjectPath.getName(), o);
}
}
}
}
}
}
PackUtils:
packagecom.lishun.utils;importjava.io.File;importjava.io.FileFilter;importjava.lang.annotation.Annotation;importjava.lang.reflect.Field;importjava.net.JarURLConnection;importjava.net.URL;importjava.util.ArrayList;importjava.util.Enumeration;importjava.util.List;importjava.util.jar.JarEntry;importjava.util.jar.JarFile;/*** Description:扫描指定包工具类的注解
*@authorlishun
*@since2015-09-10*/publicclassPackUtils {publicstaticList<Class<?>> getClassList(String packageName,booleanisRecursive) {
List<Class<?>> classList =newArrayList<Class<?>>();try{
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/"));while(urls.hasMoreElements()) {
URL url=urls.nextElement();if(url !=null) {
String protocol=url.getProtocol();if(protocol.equals("file")) {
String packagePath=url.getPath();
addClass(classList, packagePath, packageName, isRecursive);
}elseif(protocol.equals("jar")) {
JarURLConnection jarURLConnection=(JarURLConnection) url.openConnection();
JarFile jarFile=jarURLConnection.getJarFile();
Enumeration<JarEntry> jarEntries =jarFile.entries();while(jarEntries.hasMoreElements()) {
JarEntry jarEntry=jarEntries.nextElement();
String jarEntryName=jarEntry.getName();if(jarEntryName.endsWith(".class")) {
String className= jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");if(isRecursive || className.substring(0, className.lastIndexOf(".")).equals(packageName)) {
classList.add(Class.forName(className));
}
}
}
}
}
}
}catch(Exception e) {
e.printStackTrace();
}returnclassList;
}//获取指定包名下的所有类(可根据注解进行过滤)publicstaticList<Class<?>> getClassListByAnnotation(String packageName, Class<?extendsAnnotation>annotationClass) {
List<Class<?>> classList =newArrayList<Class<?>>();try{
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/"));while(urls.hasMoreElements()) {
URL url=urls.nextElement();if(url !=null) {
String protocol=url.getProtocol();if(protocol.equals("file")) {
String packagePath=url.getPath();
addClassByAnnotation(classList, packagePath, packageName, annotationClass);
}elseif(protocol.equals("jar")) {
JarURLConnection jarURLConnection=(JarURLConnection) url.openConnection();
JarFile jarFile=jarURLConnection.getJarFile();
Enumeration<JarEntry> jarEntries =jarFile.entries();while(jarEntries.hasMoreElements()) {
JarEntry jarEntry=jarEntries.nextElement();
String jarEntryName=jarEntry.getName();if(jarEntryName.endsWith(".class")) {
String className= jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
Class<?> cls =Class.forName(className);if(cls.isAnnotationPresent(annotationClass)) {
classList.add(cls);
}
}
}
}
}
}
}catch(Exception e) {
e.printStackTrace();
}returnclassList;
}privatestaticvoidaddClass(List<Class<?>> classList, String packagePath, String packageName,booleanisRecursive) {try{
File[] files=getClassFiles(packagePath);if(files !=null) {for(File file : files) {
String fileName=file.getName();if(file.isFile()) {
String className=getClassName(packageName, fileName);
classList.add(Class.forName(className));
}else{if(isRecursive) {
String subPackagePath=getSubPackagePath(packagePath, fileName);
String subPackageName=getSubPackageName(packageName, fileName);
addClass(classList, subPackagePath, subPackageName, isRecursive);
}
}
}
}
}catch(Exception e) {
e.printStackTrace();
}
}privatestaticFile[] getClassFiles(String packagePath) {returnnewFile(packagePath).listFiles(newFileFilter() {
@Overridepublicbooleanaccept(File file) {return(file.isFile() && file.getName().endsWith(".class")) ||file.isDirectory();
}
});
}privatestaticString getClassName(String packageName, String fileName) {
String className= fileName.substring(0, fileName.lastIndexOf("."));if(!packageName.equals("")) {
className= packageName + "." +className;
}returnclassName;
}privatestaticString getSubPackagePath(String packagePath, String filePath) {
String subPackagePath=filePath;if(!packagePath.equals("")) {
subPackagePath= packagePath + "/" +subPackagePath;
}returnsubPackagePath;
}privatestaticString getSubPackageName(String packageName, String filePath) {
String subPackageName=filePath;if(!packageName.equals("")) {
subPackageName= packageName + "." +subPackageName;
}returnsubPackageName;
}privatestaticvoidaddClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName, Class<?extendsAnnotation>annotationClass) {try{
File[] files=getClassFiles(packagePath);if(files !=null) {for(File file : files) {
String fileName=file.getName();if(file.isFile()) {
String className=getClassName(packageName, fileName);
Class<?> cls =Class.forName(className);if(cls.isAnnotationPresent(annotationClass)) {
classList.add(cls);
}
Field[] fields=cls.getFields();for(Field field : fields) {if(field.isAnnotationPresent(annotationClass)){
classList.add(cls);
}
}
}else{
String subPackagePath=getSubPackagePath(packagePath, fileName);
String subPackageName=getSubPackageName(packageName, fileName);
addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass);
}
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4 最后编写平时使用的设计模式来测试注解(Dao,Service,Contrller)【这里主要是为测试注解的注入,所以没有使用实际的使用数据库数据,侧重点不在这里】
Dao
packagecom.lishun.Dao;importcom.lishun.Annotation.Service;
@ServicepublicclassUserDao {publicvoidrun(){
System.out.println("测试成功");
}
}
Service:
packagecom.lishun.Service;importcom.lishun.Annotation.Autowired;importcom.lishun.Annotation.Service;importcom.lishun.Dao.UserDao;
@ServicepublicclassUserService {
@AutowiredpublicUserDao userDao;publicvoidrun(){
userDao.run();
}
}
Controller:
packagecom.lishun.controller;importcom.lishun.Annotation.Autowired;importcom.lishun.Annotation.Contrller;importcom.lishun.Service.UserService;
@ContrllerpublicclassUserContrller {
@AutowiredpublicUserService userService;publicvoidlogin(){
userService.run();
}
}
测试入口
packagecom.lishun.t;importjava.lang.annotation.Annotation;importjava.lang.reflect.Field;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.util.Arrays;importjava.util.List;importorg.junit.Test;importcom.lishun.Annotation.Autowired;importcom.lishun.Annotation.Contrller;importcom.lishun.Annotation.Service;importcom.lishun.Dao.UserDao;importcom.lishun.Service.UserService;importcom.lishun.controller.UserContrller;importcom.lishun.factory.BeanFactory;importcom.lishun.utils.AnnotationDriven;importcom.lishun.utils.PackUtils;publicclasstest {
@Testpublicvoidmain()throwsException {//启动时根据需要扫描的包名,来注入含有注解的类的字段值AnnotationDriven.annotationDriven("com.lishun");//这里相当于web的访问一次controller的一次请求UserContrller user =(UserContrller) BeanFactory
.getBean("com.lishun.controller.UserContrller");
user.login();
}
}
最后运行,
控制台输出:测试成功
由于本人水平有限,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。需要源码的留下邮箱