1、注解
1.1、注解的作用
1)编译指导,比如java内置的三个注解,(@Override,@Deprecated,@SuppressWarnings)可以对编译器辅助使用。
@Override对父类或者接口的某个方法进行重写的时候使用,如果方法名出错,会有相应的编译异常提示;如果没有加这个注解,那么就算是写错了名称,也不会有异常提示。
@Deprecated 过期提示,当被改注解标注的方法,类等被使用时,会在有删除线提示,表示不推荐使用,后续版本会进行移除,一般点入api,都会有相关替代的方法可以找到。
@SuppressWarnings镇压警告,比如过期出现删除线的方法,一类类型检查(出现黄色的),比如:
将@SuppressWarnings("all")加入到上述的类中:警告就会消失
不过一般不这样做,过期的api,点进去总会有替代的api使用,比如版本为:1.16.12的lombok的@Builder注解,lombok.experimental.Builder这个包下的注解提示是过期的,如下:
在idea中,这个会有警告提示,但是不影响代码运行,不过看着也很别扭,可以加一个
@SuppressWarnings("all")注解,不过不推荐这样使用,一般点进去,都会有相应的替代方法:里面发现注释:
@deprecated {@link lombok.Builder} has been promoted to the main package, so use that one instead.
就是说使用:lombok.Builder 替代lombok.experimental.Builder,引入lombok.Builder这个注解即可。lombok.experimental.Builder这个注解在1.18.12的版本中已经被移除了的。
2)编译时生成代码,比如一些构建工具生成代码,xml文件等
3)运行时通过反射进行处理,可以用于生成对象,初始化,生成代码等
1.2、元注解
元注解就是修饰注解的注解:
@Target 注解作用的位置,可以在class, method, field等上面使用
ElementType:
TYPE: 可以在类,接口上使用
FIELD: 属性上使用
METHOD: 方法上使用
PARAMETER: 方法参数上使用
...
@Retention 作用的范围: source (源码)< class(编译后产生的class文件) < runtime(代码运行的时候)
@Document 将注解的信息生成放到Javadoc中
package cn.yishijie.test;
import java.lang.annotation.*;
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyDocument {
String value() default "javaDoc annotation";
String author() default "jeff.chan";
}
package cn.yishijie.test;
@MyDocument(value = "this is a person class", author = "jeff.chan")
public class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
如下图:
如果注解没有被@Document修饰,那么将不会出现在javadoc中
@Inherited 子类可以继承父类的注解
如果一个类的父类(接口不行)被某个注解修饰,而这个注解里有@Inherited修饰,那么子类将会继承父类的注解。
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherit {
}
@MyInherit
public class Father {
}
public class Son extends Father{
}
public class Client {
public static void main(String[] args) {
Class<?> sonClass = Son.class;
MyInherit annotation = sonClass.getAnnotation(MyInherit.class);
System.out.println(annotation);
}
}
上述的客户端,会打印出@MyInherit注解,说明子类Son继承了父类的注解
2、反射
反射可以动态获取一个类的所有信息
Class类用于表示字节码文件.class,在使用反射之前,需要得到Class对象,只要类型和维度一样,就是同一个Class对象,jvm在加载类到内存时,会生成Class对象,用于表示该类信息,并且只有一份。
2.1、获取Class对象的三种方式:
//第一种方式
Class<?> aClass = Person.class;
//第二种方式
Class<? extends Person> aClass1 = new Person().getClass();
//第三种方式,如果对应的全类名不存在,那么会抛出ClassNotFoundException
Class<?> aClass2 = Class.forName("reflect.Person");
2.2、Class对象的使用:
// 获取类名
String simpleName = aClass.getSimpleName();
// 获取全类名
String name = aClass.getName();
// 获取所有注解
Annotation[] annotations = aClass.getAnnotations();
// 获取指定的注解
MyAnnotation annotation = aClass.getAnnotation(MyAnnotation.class);
// 获取所有的属性
Field[] declaredFields = aClass.getDeclaredFields();
// 获取public类型的属性
Field[] fields = aClass.getFields();
// 获取public指定的属性名
// Field field = aClass.getField("name");
// 获取定义的属性名
Field field1 = aClass.getDeclaredField("name");
// 创建对象
Object obj = aClass.newInstance();
// 使用私有属性的时候要做以下操作
// 否则会报错
field1.setAccessible(true);
// 设置实列的属性值
field1.set(obj,"jeffchan");
// 获取所有的public方法
Method[] methods = aClass.getMethods();
// 获取public指定的方法名
Method setName = aClass.getMethod("setName", String.class);
// 获取所有声明的方法
Method[] declaredMethods = aClass.getDeclaredMethods();
// 获取指定的声明的方法
Method setName1 = aClass.getDeclaredMethod("setName", String.class);
// 如果是调用私有的方法,要关闭java访问权限的检查
setName.setAccessible(true);
// 调用方法
setName.invoke(obj, "caraliu");
// 获取所有public的构造器
Constructor<?>[] constructors = aClass.getConstructors();
// 获取所有声明的构造器
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
// 获取public指定的构造器
Constructor<?> constructor = aClass.getConstructor(String.class, String.class);
// 创建实列
Object obj1 = constructor.newInstance("jeffchan", "23");
上述的方法,属性,构造器获取都是有public,声明,具体指定的public,具体指定的声明这四种方式
2.3、反射性能测试
package cn.yishijie;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime) // 测试方法平均执行时间
@OutputTimeUnit(TimeUnit.MICROSECONDS) // 输出结果的时间粒度为微秒
@State(Scope.Thread) // 每个测试线程一个实例
public class FirstBenchMark {
@Benchmark
public void normal() {
Person p =new Person();
for (int i = 0; i< 1000000; i++) {
p.setName(""+i);
}
}
@Benchmark
public void reflectWithAccessible() throws Exception {
Class<?> aClass = Class.forName("cn.yishijie.Person");
Object obj = aClass.newInstance();
Method name = aClass.getMethod("setName", String.class);
// 去掉访问控制校验
name.setAccessible(true);
for (int i = 0; i< 1000000; i++) {
name.invoke(obj,""+i);
}
}
@Benchmark
public void reflect() throws Exception {
Class<?> aClass = Class.forName("cn.yishijie.Person");
Object obj = aClass.newInstance();
Method name = aClass.getMethod("setName",String.class);
for (int i = 0; i< 1000000; i++) {
name.invoke(obj,""+i);
}
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(FirstBenchMark.class.getSimpleName())
.forks(1) // 一个线程程执行测试
.warmupIterations(5) // 执行5遍warmup
.measurementIterations(5) // 执行5遍测试
.build();
new Runner(opt).run();
}
}
结果输出:
Result "cn.yishijie.FirstBenchMark.normal":
15743.863 ±(99.9%) 96.377 us/op [Average]
(min, avg, max) = (15724.352, 15743.863, 15785.170), stdev = 25.029
CI (99.9%): [15647.486, 15840.240] (assumes normal distribution)
Result "cn.yishijie.FirstBenchMark.reflect":
16343.323 ±(99.9%) 126.123 us/op [Average]
(min, avg, max) = (16321.565, 16343.323, 16400.679), stdev = 32.754
CI (99.9%): [16217.201, 16469.446] (assumes normal distribution)
Result "cn.yishijie.FirstBenchMark.reflectWithAccessible":
15812.856 ±(99.9%) 183.979 us/op [Average]
(min, avg, max) = (15787.033, 15812.856, 15898.016), stdev = 47.779
CI (99.9%): [15628.878, 15996.835] (assumes normal distribution)
普通的平均耗时: 15743.863 微秒
加访问控制的反射耗时: 16343.323 微秒
不加访问控制的反射耗时:15812.856 微秒
普通的平均耗时 < 不加访问控制校验的反射耗时 < 加访问控制校验的反射耗时
说明反射会一定的性能损耗,尤其是实际工作中比较复杂的场景,这种损耗会更加明显,同时访问权限的校验过程也会耗一定时间。
2.4、反射框架Reflects介绍
创建Reflections实列的三种方式:
Reflections reflections = new Reflections("my.package.prefix");
//or
Reflections reflections = new Reflections(ClasspathHelper.forPackage("my.package.prefix"),
new SubTypesScanner(), new TypesAnnotationScanner(), new FilterBuilder().include(...), ...);
//or using the ConfigurationBuilder
new Reflections(new ConfigurationBuilder()
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("my.project.prefix")))
.setUrls(ClasspathHelper.forPackage("my.project.prefix"))
.setScanners(new SubTypesScanner(), new TypeAnnotationsScanner().filterResultsBy(optionalFilter), ...));
// 扫描当前包和子包下的文件的实列对象
Reflections reflections = new Reflections(Client.class.getPackage().getName()
, new SubTypesScanner() // 子类型扫描器
, new FieldAnnotationsScanner() // 属性注解扫描器
, new TypeAnnotationsScanner()); // 注解类型扫描器
// 在当前包和子包下获取所有MyInterface的实现类
Set<Class<? extends MyInterface>> subTypes = reflections.getSubTypesOf(MyInterface.class);
//subTypes.forEach(x -> System.out.println(x));
// 在当前包和子包下获取所有被@MyDocument修饰的类
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(MyDocument.class);
//annotated.forEach(x -> System.out.println(x));
// honorInherited这个参数是作用于@Inherited这个注解,如果为true的话,那么跟@Inherited的作用一样
// 如果为false,那么注解都会被子类继承,不管是不是接口,默认为false
Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(MyInherit.class,false);
typesAnnotatedWith.forEach(x -> System.out.println(x));
// 获取被@Id注解修饰的属性
Set<Field> fieldsAnnotatedWith = reflections.getFieldsAnnotatedWith(Id.class);
fieldsAnnotatedWith.forEach(x -> System.out.println(x));
还有很多API,参考文献地址: http://ronmamo.github.io/reflections/index.html?org/reflections/Reflections.html
3、简易IOC容器
自定义注解:(Autowired Compenent Repository Service)
package cn.yishijie.annotation;
import java.lang.annotation.*;
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
package cn.yishijie.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
String value() default "";
}
package cn.yishijie.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Repository {
String value() default "";
}
package cn.yishijie.annotation;
import java.lang.annotation.*;
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
String value() default "";
}
后置处理器接口定义:
package cn.yishijie.processor;
public interface BeanPostProcessor {
Object postProcessAfterInitialization(Object bean, String beanName);
}
反射封装的工具类:
package cn.yishijie.util;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Set;
@Slf4j
public class ReflectionsUtil {
/**
* 获取某个包下的注解
* @param packageName
* @param annotation
* @return
*/
public static Set<Class<?>> scanAnnotatedClass(String packageName, Class<? extends Annotation> annotation){
Reflections reflections = new Reflections(packageName,new SubTypesScanner(), new TypeAnnotationsScanner());
Set<Class<?>> annotatedClass = reflections.getTypesAnnotatedWith(annotation);
return annotatedClass;
}
/**
* 实列化
* @param cls
* @return
*/
public static Object newInstance(Class<?> cls) {
Object instance = null;
try {
instance = cls.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
log.error("new instance failed", e);
}
return instance;
}
/**
* 设置属性
* @param obj
* @param field
* @param value
*/
public static void setField(Object obj, Field field, Object value) {
// 忽略java访问权限的校验
field.setAccessible(true);
try {
field.set(obj, value);
} catch (IllegalAccessException e) {
log.error("set field failed", e);
e.printStackTrace();
}
}
/**
* 获取某个包下的某个类的所有子类
* @param packageName
* @param interfaceClass
* @param <T>
* @return
*/
public static <T> Set<Class<? extends T>> getSubClass(String packageName, Class<T> interfaceClass) {
Reflections reflections = new Reflections(packageName,new SubTypesScanner());
return reflections.getSubTypesOf(interfaceClass);
}
}
注解扫描和后置处理器列表:
package cn.yishijie.core.ioc;
import cn.yishijie.IocApplication;
import cn.yishijie.annotation.Component;
import cn.yishijie.annotation.Repository;
import cn.yishijie.annotation.Service;
import cn.yishijie.processor.BeanPostProcessor;
import cn.yishijie.util.ReflectionsUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class AnnotationScanner {
public static final Map<String, Class<?>> annotationClassMap = new ConcurrentHashMap<>();
// 后置处理器列表
public static final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
public static void scanAnnotationClasses(String packageName){
Set<Class<?>> componentClass = ReflectionsUtil.scanAnnotatedClass(packageName, Component.class);
Set<Class<?>> serviceClass = ReflectionsUtil.scanAnnotatedClass(packageName, Service.class);
Set<Class<?>> repositoryClass = ReflectionsUtil.scanAnnotatedClass(packageName, Repository.class);
componentClass.forEach(x -> {
annotationClassMap.put(BeanFactory.getBeanName(x), x);
});
serviceClass.forEach(x -> {
annotationClassMap.put(BeanFactory.getBeanName(x), x);
});
repositoryClass.forEach(x -> {
annotationClassMap.put(BeanFactory.getBeanName(x), x);
});
Set<Class<? extends BeanPostProcessor>> subClass = ReflectionsUtil.getSubClass(IocApplication.class.getPackage().getName(), BeanPostProcessor.class);
subClass.forEach(x -> beanPostProcessors.add((BeanPostProcessor) ReflectionsUtil.newInstance(x)));
}
}
Bean工厂:
package cn.yishijie.core.ioc;
import cn.yishijie.IocApplication;
import cn.yishijie.annotation.Autowired;
import cn.yishijie.annotation.Component;
import cn.yishijie.annotation.Repository;
import cn.yishijie.annotation.Service;
import cn.yishijie.processor.BeanPostProcessor;
import cn.yishijie.util.ReflectionsUtil;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class BeanFactory {
private static BeanFactory beanFactory = new BeanFactory();
public final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(128);
public final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(128);
private BeanFactory(){
}
public static BeanFactory beanFactory(){
return beanFactory;
}
/**
* 获取容器中的beanName
* @param aClass
* @return
*/
public static String getBeanName(Class<?> aClass) {
String beanName = aClass.getName();
if (aClass.isAnnotationPresent(Component.class)) {
Component component = aClass.getAnnotation(Component.class);
beanName = "".equals(component.value()) ? aClass.getName() : component.value();
}
if(aClass.isAnnotationPresent(Service.class)){
Service service = aClass.getAnnotation(Service.class);
beanName = "".equals(service.value()) ? aClass.getName() : service.value();
}
if(aClass.isAnnotationPresent(Repository.class)){
Repository repository = aClass.getAnnotation(Repository.class);
beanName = "".equals(repository.value()) ? aClass.getName() : repository.value();
}
return beanName;
}
public Object getBean(String beanName) throws Exception{
Object obj = singletonObjects.get(beanName);
if(obj == null){
Class<?> aClass = AnnotationScanner.annotationClassMap.get(beanName);
if(aClass == null){
return obj;
}
if(!earlySingletonObjects.keySet().contains(beanName)){
obj = aClass.newInstance();
// 用于解决循环依赖问题,提前暴露初步实列化的对象
earlySingletonObjects.put(beanName, obj);
}
Field[] fields = aClass.getDeclaredFields();
if(fields.length > 0){
for(Field field : fields){
if(field.isAnnotationPresent(Autowired.class)){
Class<?> fieldClass = field.getType();
if (fieldClass.isInterface()){
Set<Class<?>> subClasses = ReflectionsUtil.getSubClass(IocApplication.class.getPackage().getName(), (Class<Object>)fieldClass);
if (subClasses.size() == 0) {
throw new Exception(field.getName() + " is interface and do not have implemented class exception");
}
if (subClasses.size() == 1) {
fieldClass = subClasses.iterator().next();
}
if (subClasses.size() > 1) {
throw new Exception(field.getName() + " is interface more than one implement");
}
}
String fieldBeanName = getBeanName(fieldClass);
Object existObj = singletonObjects.get(fieldBeanName);
if(existObj != null){
// 设置注入属性
ReflectionsUtil.setField(obj,field,existObj);
}else{
Object earlyObjects = earlySingletonObjects.get(fieldBeanName);
if(earlyObjects != null){
// 设置注入属性
ReflectionsUtil.setField(obj,field,earlyObjects);
}else {
// 设置注入属性
ReflectionsUtil.setField(obj,field,getBean(fieldBeanName));
}
}
}
}
earlySingletonObjects.remove(beanName);
}
if(!singletonObjects.keySet().contains(beanName)){
singletonObjects.put(beanName, obj);
// 实列化完成后,调用后置处理器的方法
postProcess(obj, beanName);
}
}
return obj;
}
/**
* 执行后置处理器
* @param bean
* @param beanName
* @return
*/
public Object postProcess(Object bean, String beanName){
for (BeanPostProcessor beanPostProcessor : AnnotationScanner.beanPostProcessors) {
bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
}
return bean;
}
}
ApplicationContext:
package cn.yishijie.core.ioc;
public class ApplicationContext {
public void refresh(String packageName) {
AnnotationScanner.scanAnnotationClasses(packageName);
}
public Object getBean(String beanName) throws Exception{
BeanFactory beanFactory = BeanFactory.beanFactory();
return beanFactory.getBean(beanName);
}
}
客户端调用:
package cn.yishijie;
import cn.yishijie.core.ioc.ApplicationContext;
public class IocApplication {
public static void main(String[] args) throws Exception{
ApplicationContext applicationContext = new ApplicationContext();
applicationContext.refresh(IocApplication.class.getPackage().getName());
Object useDao = applicationContext.getBean("userDao");
System.out.println(useDao);
}
}
上述功能中,没有实现@Qualified等注解的功能,也没有提供Bean的init()方法,并且都是懒加载的方式,要实际使用到某个对象的时候,才会创建实例,并且放入到Map中,下次拿的时候,是同一个bean。这里简单的使用到了注解的扫描解析和反射功能。实际可以通过这些来完成更复杂的功能,比如增加netty充当服务器,mvc的url映射功能可以制作一个简单的springboot项目。
作者 : jeff.chan