1. 注解的定义
注解在Java中是无实际意义的,并不能影响程序的运行结果以及逻辑。但是注解在Java中起着标签的作用,为代码运行提供一些特殊的信息,就像为某件事物加标签一样,从标签我们能得知程序的某些信息。注解也是Java类型的一种,可以看做是一个特殊的类,它的定义如下:
public @interface Value {
public String value();
}12345
2. 注解的种类
在Java中,最初始的注解一共有五个,这些注解被称作元注解,用来注解其它的注解或者其它Java信息,下面讲解这5种元注解:
2.1 @Retention
@Retention
注解的作用为标注注解是保留在什么时期,我们知道程序在运行起来有:源码、编译、运行这三种阶段,而@Retention
注解的三种属性恰好对应这三种阶段:
RetentionPolicy.RUNTIME:该属性表明注解会保留到程序运行时的阶段,我们常在框架中(比如Spring)使用的注解,都是标注为该属性,因为框架需要通过反射拿到注解的信息,反射是在运行阶段起作用的,所以必须使用该种属性;
RetentionPolicy.CLASS:该属性表明注解会保留到Java程序编译后的字节码中,但是在运行阶段会被擦除,即不参与jvm。
RetentionPolicy.SOURCE:该属性表明注解只会保留在Java源程序中,而不会参与到字节码的转化中去,只存在源码阶段。
而如果没有特别用该注解标注注解的话,Java注解默认会保留到字节码中,而不会保留到运行阶段,即保留到RetentionPolicy.CLASS
阶段。
2.2 @Target
@Target
注解的作用是表明其它注解该标注在什么类型上,是标注在类级别上,还是字段级别上,亦或者是方法级别上,它有如下属性:
ElementType.TYPE:表明注解标注一个类型,比如在类、接口、枚举;
ElementType.FIELD:表明注解标注一个字段;
ElementType.ANNOTATION_TYPE:表明注解标注一个注解;
ElementType.CONSTRUCTOR:表明注解标注一个构造器;
ElementType.METHOR:表明注解标注一个方法;
ElementType.PARAMETER:表明注解标注一个方法参数;
ElementType.PACKAGE:表明注解标注一个包;
ElementType.LOCAL_VARIABLE:表明注解标注一个局部变量;
ElementType.TYPE_PARAMETER:表明注解用来标注类型参数的,即标注泛型的参数的;
ElementType.TYPE_USE:表明注解可以用来标注以上所有可以标注的地方。
2.3 @Documented
该元注解的作用为将注解中的元素包含到Java doc文档中去。
2.4 @Inherited
该元注解的作用是标明注解可以被继承,即子类会继承父类被@Inherited
注解的注解。比如下面这个例子:
@Inherited@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Share {}@Sharepublic class Father {}public class Son extends Father {
public static void main(String []args){
boolean isAnotatio= Son.class.isAnnotationPresent(Share.class);
if (isAnotatio){
System.out.println("已被@Share注解标注");
}
}}//打印:已被@Share注解标注1234567891011121314151617181920
该元注解注解的注解一般用于类与接口上,方便注解被子类继承,简化了代码的幅度。
2.5 @Repeatable
该元注解表明该注解可以被赋值多次,该注解实质上指定了一个容器注解,如下面的代码:
public @interface Likes {
Like [] value();}@Repeatable(Likes.class)public @interface Like {
String role();}@Like(role = "蕾姆")@Like(role = "拉姆")public class RepeatTest {}12345678910111213
3.自定义注解
自定义注解一般都会标注@Retention
与@Target
,表明注解的保留时期与标注的目标是谁,而@Retention
注解一般会选择保留到运行期,因为一般用注解来标注一些信息,并在运行时期用反射的方式获取到这些信息,比如:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ZhuJie {
public int check() default 1;}public class ZhuJieTest {
@ZhuJie(check = 50)
private int value;
public static void main(String []args){
Field[] fields=ZhuJieTest.class.getDeclaredFields();
boolean isAnotatio= fields[0].isAnnotationPresent(ZhuJie.class);
if (isAnotatio){
ZhuJie zhuJie=fields[0].getAnnotation(ZhuJie.class);
System.out.println(zhuJie.check());//打印:50
}
}}1234567891011121314151617181920
4.利用注解来实现简单的Spring框架
为了加深对注解的使用以及理解,下面带大家用注解简单的实现一个小型的Spring框架,首先,我们定义常用的三个注解:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE_USE)public @interface Value {
public String value();}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Componet {
public String name() default "";}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Autowired{}1234567891011121314151617181920
定义了@Value
、@Componet
、@Autowired
这三个注解用来实现赋值、自动装配以及将类注册成Bean类,接着我们需要一个Bean类以及Bean的工厂类来存放所有的Bean类:
public class Bean<T> {
private T object;
private String beanName;
public Bean(T o,String beanName){
this.object=o;
this.beanName=beanName;
}
public T getObject() {
return object;
}
public String getBeanName() {
return beanName;
}}public class BeanFactory {
private static Map<String,Bean> map=new HashMap<String, Bean>();
private BeanFactory(){}
public static void addBean(Bean o){
map.put(o.getBeanName(),o);
}
public static boolean isExsisBean(Bean o){
if (map.containsKey(o.getBeanName())){
return true;
}
return false;
}
public static Bean getBeanByName(String beanName){
return map.get(beanName);
}
public static Object getBean(String beanName){
return map.get(beanName).getObject();
}
public static int getBeanSize(){
return map.size();
}}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
在Bean
类中用了泛型来存放真正的bean
实例,里面拥有bean
的名字,在这里我们使用首字符小写的方法为Bean
类命名,符合Spring的命名规范。而BeanFactory
利用一个Map
来存放Bean
名字到Bean
实例的映射,提供获取Bean
实例的方法。
接着就是对Ioc
容器的初始化了,首先应当找到所有Bean
实例所在的包,将该包下的所有类都找出来,然后利用反射判断该类有无@Componet
注解,有则代表这个类需要实例化成一个Bean
类,然后再接着判断@Value
、@Autowired
注解,为其相应的属性赋值,代码如下:
public class IocUtil {
/**
* 传入bean实例所在包的相对引用
* 例:com.suyeq.bean,
* @param pakageReference
*/
public void initIoc(String pakageReference) {
try {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
URL url=classLoader.getResource(pakageReference);
File file=new File(url.toString().substring(6));
if (file.isDirectory()){
File [] fils=file.listFiles();
for (File file1:fils){
String []strings=file1.getName().replace(".","/").split("/");
loadBean(pakageReference+"."+strings[0]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将Bean实例化
* @param beanLimitedName 类的相对引用
*/
public void loadBean(String beanLimitedName){
try {
Class aclass=ClassLoader.getSystemClassLoader().loadClass(beanLimitedName);
Object object=aclass.newInstance();
//判断是否有Componet注解
if (aclass.isAnnotationPresent(Componet.class)){
Field [] fields=aclass.getDeclaredFields();
for (int i=0;i<fields.length;i++){
fields[i].setAccessible(true);
if (fields[i].isAnnotationPresent(Value.class)){
String value=fields[0].getDeclaredAnnotation(Value.class).value();
fields[i].set(object,value);
}
if (fields[i].isAnnotationPresent(Autowired.class)){
Class bClass=fields[i].getType();
String className=bClass.getSimpleName();
//bean类实例首字母小写
className=className.toLowerCase().charAt(0)+""+className.substring(1);
Bean bean=BeanFactory.getBeanByName(className);
if (bean != null){
fields[i].set(object,bean.getObject());
}
}
}
String nowBeanName=aclass.getSimpleName();
Bean bean=new Bean<>(object,nowBeanName.toLowerCase().charAt(0)+""+nowBeanName.substring(1));
BeanFactory.addBean(bean);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String []args) {
new IocUtil().initIoc("bean");
//System.out.println(BeanFactory.getBeanSize());
UserService userService=(UserService) BeanFactory.getBean("userService");
userService.showData();
}}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
我们定义两个Bean
类,来测试代码:
@Componetpublic class UserDao {
public void findUserById(String id){
System.out.println("已查询id为"+id+"的数据");
}}@Componetpublic class UserService {
@Value("10")
String id;
@Autowired
UserDao userDao;
public void showData(){
userDao.findUserById(id);
}}123456789101112131415161718192021
运行程序后得结果:已查询id为10的数据
。例外,整个项目的图示: