springMvc的注解注入方式
最近在看springMvc的源碼,看到了該框架的注入注解的部分覺的有點吃力,可能還是對注解的方面的知識還認識的不夠深刻,所以特意去學習注解方面的知識。由於本人也是抱着學習的態度來閱讀源碼,若文章在表述和代碼方面如有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!希望能互相學習。
1,首先定義三個常用的注解Service,Autowired,Contrller;(主要的解釋都在代碼中有,在這里就不多陳述)
Service:
package com.lishun.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.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 等。
* */
@Documented
public @interface Service {
/* @interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數。
* 方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。
* 可以通過default來聲明參數的默認值。
*/
String value() default "this is service annotation";
}
Autowired:
package com.lishun.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
public String value() default "no description";
}
Contrller:
package com.lishun.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Contrller {
String value() default "this is contrller annotation";
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2:javaBean數據池-BeanFactory:主要存放含有注解的類;
package com.lishun.factory;
import java.util.HashMap;
import java.util.Map;
/**
* Description:存放所有bean的數據池
* @author lishun
* @since 2015-09-10
*/
public class BeanFactory {
private static Map<String, Object> map = new HashMap<String, Object>();
public static void addBean(String beanName, Object bean) {
map.put(beanName, bean);
}
public static Object getBean(String beanName) throws Exception {
Object o = map.get(beanName);
if (o != null) {
return o;
} else {
throw new Exception("未注入的類型:" + beanName);
}
}
public static Boolean containsBean(String beanName){
return map.containsKey(beanName);
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3:編寫處理注解的核心代碼(這里涉及的主要知識是反射,如果反射知識不夠熟練的話建議先學習反射方面的知識),主要涉及的兩個類是注解驅動(AnnotationDriven)和注解掃描類(PackUtils-這個類主要的是掃描包名下所有的類(如com.lishun,就是掃描該包下所有的類),代碼主要是來自網絡)
AnnotationDriven:
package com.lishun.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import com.lishun.Annotation.Autowired;
import com.lishun.Annotation.Contrller;
import com.lishun.Annotation.Service;
import com.lishun.factory.BeanFactory;
/**
* Description:注入驅動類,所有的注解注入都在這里實現(這里只實現了通過類型來注入值,其他方式沒實現,其實代碼都是差不多了,有興趣的可以自行腦補)
* @author lishun
*
*/
public class AnnotationDriven {
public static void annotationDriven(String packName) throws Exception {
//注入Service和Contrller
List<Class<?>> classSaveServicePaths = PackUtils
.getClassListByAnnotation(packName, Service.class);
List<Class<?>> classSaveContrllerPaths = PackUtils
.getClassListByAnnotation(packName, Contrller.class);
saveBean(classSaveServicePaths);
saveBean(classSaveContrllerPaths);
//注入Autowired
List<Class<?>> classInjectPaths = PackUtils.getClassListByAnnotation(
packName, Autowired.class);
inject(classInjectPaths);
}
private static void saveBean(List<Class<?>> classSavePaths)
throws InstantiationException, 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();
}
}
}
private static void inject(List<Class<?>> classInjectPaths) throws Exception {
Object o = null;
for (Class<?> classInjectPath : classInjectPaths) {
Class c = Class.forName(classInjectPath.getName());
//判斷存放bean的池中是否存在該bean
if (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 (annotation instanceof Autowired) {
Class classField = field.getType();
Object clazz = BeanFactory
.getBean(classField.getName());
field.set(o, clazz);
BeanFactory.addBean(classInjectPath.getName(), o);
}
}
}
}
}
}
PackUtils:
package com.lishun.utils;
import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Description:掃描指定包工具類的注解
* @author lishun
* @since 2015-09-10
*/
public class PackUtils {
public static List<Class<?>> getClassList(String packageName, boolean isRecursive) {
List<Class<?>> classList = new ArrayList<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);
} else if (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();
}
return classList;
}
// 獲取指定包名下的所有類(可根據注解進行過濾)
public static List<Class<?>> getClassListByAnnotation(String packageName, Class<? extends Annotation> annotationClass) {
List<Class<?>> classList = new ArrayList<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);
} else if (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();
}
return classList;
}
private static void addClass(List<Class<?>> classList, String packagePath, String packageName, boolean isRecursive) {
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();
}
}
private static File[] getClassFiles(String packagePath) {
return new File(packagePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
}
private static String getClassName(String packageName, String fileName) {
String className = fileName.substring(0, fileName.lastIndexOf("."));
if (!packageName.equals("")) {
className = packageName + "." + className;
}
return className;
}
private static String getSubPackagePath(String packagePath, String filePath) {
String subPackagePath = filePath;
if (!packagePath.equals("")) {
subPackagePath = packagePath + "/" + subPackagePath;
}
return subPackagePath;
}
private static String getSubPackageName(String packageName, String filePath) {
String subPackageName = filePath;
if (!packageName.equals("")) {
subPackageName = packageName + "." + subPackageName;
}
return subPackageName;
}
private static void addClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName, Class<? extends Annotation> 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
package com.lishun.Dao;
import com.lishun.Annotation.Service;
@Service
public class UserDao {
public void run(){
System.out.println("測試成功");
}
}
Service:
package com.lishun.Service;
import com.lishun.Annotation.Autowired;
import com.lishun.Annotation.Service;
import com.lishun.Dao.UserDao;
@Service
public class UserService {
@Autowired
public UserDao userDao;
public void run(){
userDao.run();
}
}
Controller:
package com.lishun.controller;
import com.lishun.Annotation.Autowired;
import com.lishun.Annotation.Contrller;
import com.lishun.Service.UserService;
@Contrller
public class UserContrller {
@Autowired
public UserService userService;
public void login(){
userService.run();
}
}
測試入口
package com.lishun.t;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import com.lishun.Annotation.Autowired;
import com.lishun.Annotation.Contrller;
import com.lishun.Annotation.Service;
import com.lishun.Dao.UserDao;
import com.lishun.Service.UserService;
import com.lishun.controller.UserContrller;
import com.lishun.factory.BeanFactory;
import com.lishun.utils.AnnotationDriven;
import com.lishun.utils.PackUtils;
public class test {
@Test
public void main() throws Exception {
//啟動時根據需要掃描的包名,來注入含有注解的類的字段值
AnnotationDriven.annotationDriven("com.lishun");
//這里相當於web的訪問一次controller的一次請求
UserContrller user = (UserContrller) BeanFactory
.getBean("com.lishun.controller.UserContrller");
user.login();
}
}
最后運行,
控制台輸出:測試成功

