注解@Annotion是Java非常重要的一部分,
自从1.5版本推出以来就被非常广泛的运用到各种框架和应用,
已经成为了Java一块重要的拼图了.
而C#的Attribute和@Annoation的功能相近,
同样也是配合二者反射的使用给类/接口/…添加动态的运行时的信息
这里先以Java使用Annoation为例,
Java使用Annoation:山寨一个@Bean@Bean是spring的一个最基础的注解,
只要添加后就可会为类和方法在容器中创建一个实例
这个功能并不难,在不限制生命周期的情况下,现在我们可以自己实现一下
public class MySpringContainer {
public static HashMap theContainer = new HashMap();
public static HashMap getContainer(){
return theContainer;
}
}
2.定义注解@Bean
注解的定义只需要记住4个元注解即可
- @Target({ElementType枚举类,用于规定注解的使用范围})
- @Retention(RetentionPolicy枚举类型,用于指定注解信息的保留,有Souce只保留在编译时,Class保存在编译后的类文件,RUNTIME在(VM)程序运行时也保留)
- @documented
- @Inherited //是否可以被继承,这点很常见,在spring中,@Component继承了@Bean,@Servce,@Controller,@Repository…则继承了@Component
@Target({ElementType.TYPE}) //可以设置在类上
@Retention(RetentionPolicy.RUNTIME) //保留策略
@Inherited
@documented
public @interface Bean {
//注解的定义和接口很像,同样也只能在其中声明方法(但其方法本质是一种属性,不需要实现,这是和接口不一样的)
String O_id();
}
3.随便定义一个pojo类,应用注解
@Bean(O_id = "theOne") //tip:如果方法名为value则可以不用写O_id
public class Pet {
private String id;
private String name;
private String Owner;
public String getId() {
return id;
}
public Pet() {
System.out.println("使用了無參的構造方法");
id = "deffault";
name = "deffault";
Owner = "deffault";
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOwner() {
return Owner;
}
public void setOwner(String owner) {
Owner = owner;
}
}
4.使用反射在运行时获取注解
在完成定义后,需要获取类信息,并在类信息上再获取注解信息,
在这里写个简单的类扫描工具,用于检查指定包下类的@Bean注解信息
public class PojoAnnotionScanner {
public static void Scan(String packageName) throws Exception {
List list = getClazzesInPackage(packageName);
list.forEach(claz->{
//检查Class中是否有Bean标签
if(claz.getAnnotation(Bean.class)!=null) {
Bean bean = (Bean) claz.getAnnotation(Bean.class);
Object obj = new Object();
try {
obj = claz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//有的话就把用Class的反射获取的对象和注解信息放到我们的山寨容器
MySpringContainer.getContainer().put(bean.O_id(), obj);
}
});
}
//获取指定报名下,所有类的Class集合
private static List getClazzesInPackage(String packageName) throws Exception {
ArrayList list = new ArrayList<>();
//獲取包路徑
String packpath = packageName.replace('.', '/');
//獲取包的URL
URL pURL = Thread.currentThread().getContextClassLoader().getResource(packpath);
//獲取包文件夾,準備遍歷下面的.class文件
File dic = new File(pURL.getPath());
if(!dic.exists())
throw new Exception("包路徑不正確!");
File[] files = dic.listFiles(filename->{
if(filename.getName().endsWith(".class"))
return true;
return false;});
for(File item : files ) {
String totalpath = packageName+"."+FilenameUtils.getbaseName(item.getName());
Class claz = Class.forName(totalpath);
list.add(claz);
}
return list;
}
}
运行测试:
public static void main(String[] args) throws Exception {
//扫描后就可以到我们的容器中去查找这个创建的对象
PojoAnnotionScanner.Scan("pojo");
Pet the = (Pet) MySpringContainer.theContainer.get("theOne");
System.out.println(the.getId()+the.getName()+the.getOwner());
}
运行结果:
使用了無參的構造方法 deffaultdeffaultdeffault
当然,你也可以再定义一个@Value注解,给属性赋值,
然后同样去检查方法中再去通过反射获取Class中的所有Field…
public class MySpringContainer
{
public static Dictionary springContainer = new Dictionary();
public static Dictionary GetConainer()
{
return springContainer;
}
}
2.定义Attribute
///3.poco类/// 定義[Bean註解,限定于type類型,可以像Spring一樣創建一個實例對象] /// ///C#的注解需要使用[AttributeUsage]修饰,三个参数分别代表了 ///AttributeTargets注解应用类型,相当于@Target ///AllowMultiple是否可以在一个类/接口/方法....上重复使用 ///Inherited是否允许被继承 [AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = true)] public class Bean:System.Attribute { //C#注解继承自Attribute,像一个普通类一样可以定义属性和方法,正常实例化 //但是当Atrribute作为注解应用在类/接口/方法...上时,只能用它的构造函数接收参数或给属性赋值 //所以可以定义很多构造函数来接收不同形式的参数, //这点和Java的@Annotion方式不同,但目的一样那就是把信息保存到注解里 public string O_ID { get; } public Bean(string O_ID) { this.O_ID = O_ID; } }
[Bean("TheSingle")]
public class Pet
{
public string ID { get; set; }
public string Name { get; set; }
public string OwnerName { get; set; }
public Pet()
{
ID = "DefaultID";
Name = "DefaultName";
OwnerName = "張三";
}
public override string ToString()
{
return "寵物ID:" + ID + "名字:" + Name + "主人名:" + OwnerName;
}
}
4.反射获取注解
public static void Scan(string packageName)
{
//C#获取dll中所有类的Type非常简单
List list = Assembly.Load(packageName).GetTypes().ToList();
//遍历Type集合判断是否有Bean的Attribute,
list.ForEach(type =>
{
if (type.GetCustomAttribute(typeof(Bean))!=null)
{
//如果有的话就在容器里注册一个该类型的实例
Bean bean = (Bean)type.GetCustomAttribute(typeof(Bean));
MySpringContainer.GetConainer().Add(bean.O_ID, Activator.CreateInstance(type));
}
});
}
测试:
static void Main(string[] args)
{
ClassScanner.Scan("AttributeTest");
Pet pet1 = (Pet)MySpringContainer.springContainer["TheSingle"];
Console.WriteLine(pet1);
}
总结:
同:
无论是Attribute还是@Annoation都需要服务于反射
都是在运行时添加的动态信息
异;
Attribute可以本质是一种普通的类,@Annotion不是类也不是借口
Attribute通过其构造函数保存信息,@Annoation通过方法属性



