辛苦写作,觉得还可以的朋友赏个赞吧! 这里补上一篇文章反射破坏单例模式的案例
class SingleExample{
private static SingleExample instance;
public SingleExample() {
//加了这个判断才避免单例模式被反射破坏
if(instance!=null){
throw new RuntimeException("instance must be single");
}
}
public static SingleExample getInstance(){
if(instance==null){
instance = new SingleExample();
}
return instance;
}
}
//单例模式下这两个实例相同
SingleExample example = SingleExample.getInstance();
SingleExample example1 = SingleExample.getInstance();
System.out.println(example == example1);//true
//利用反射方式调用默认构造函数创建对象
Class<? extends SingleExample> clazz = example.getClass();
Constructor<? extends SingleExample> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingleExample example2 = declaredConstructor.newInstance();
System.out.println(example == example2);//false
注解的概念及其作用
JDK1.5引入的新特性,其主要作用是 编写文档、代码分析、编译检查
例如 @Override 就对实现的方法名进行了编译检查
注释中的@Param @Return等说明信息 可以通过javadoc命令生成文档
像源码中一些过时的早期版本的api 通过 @Deprecated 注解实现方法被划横线 不建议使用效果
@SuppressWarnings 忽略警告
jdk8引入预定义注解,@FunctionalInterface 约束一个接口为函数式接口,该接口有且仅有一个抽象方法
注解的本质实际上就是一个接口,可以通过javap命令查看生成的class文件验证。
注解中定义的方法称为属性,可以是int类型 string类型 数组类型 枚举类型 class类型 或者 注解类型
元注解
@Retention(RetentionPolicy.RUNTIME) //作用域 编译期 SOURCE CLASS 运行期 RUNTIME @Target(ElementType.type) //使用目标表示注解用于 类 方法 属性 接口 @Documented //是否可以生成文档 @Inherited //是否可以被继承
注解解析器
编译器工作,将.java文件生成语法树,解析注解处理器,生成字节码,最终输出.class文件
java中提供了 Abstractrocessor 以用于程序员自定义注解 只需要继承这个抽象类并重写方法
首先看初始化方法
public synchronized void init(ProcessingEnvironment var1) {
if (this.initialized) {
throw new IllegalStateException("Cannot call init more than once.");
} else {
Objects.requireNonNull(var1, "Tool provided null ProcessingEnvironment");
this.processingEnv = var1;
this.initialized = true;
}
}
然后看支持的注解类型
public Set<String> getSupportedAnnotationTypes() {
SupportedAnnotationTypes var1 = (SupportedAnnotationTypes)this.getClass().getAnnotation(SupportedAnnotationTypes.class);
if (var1 == null) {
if (this.isInitialized()) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedAnnotationTypes annotation found on " + this.getClass().getName() + ", returning an empty set.");
}
return Collections.emptySet();
} else {
return arrayToSet(var1.value());
}
}
起源版本
public SourceVersion getSupportedSourceVersion() {
SupportedSourceVersion var1 = (SupportedSourceVersion)this.getClass().getAnnotation(SupportedSourceVersion.class);
SourceVersion var2 = null;
if (var1 == null) {
var2 = SourceVersion.RELEASE_6;
if (this.isInitialized()) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedSourceVersion annotation found on " + this.getClass().getName() + ", returning " + var2 + ".");
}
} else {
var2 = var1.value();
}
return var2;
}
注解处理器的核心方法 这是一个抽象方法需要继承实现
public abstract boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2);
自定义注解实践
模拟实现lombok
首先自定义一个注解 并添加上源注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface FiledAnno {
String value() default "hello marx";
}
然后编写注解处理器 重写 process方法
@SupportedAnnotationTypes("com.zld.cloud.FiledAnno") //需要解析的注解
@SupportedSourceVersion(SourceVersion.RELEASE_1)
public class FiledAnnotationProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
/**
* 扫描实体类上有@FieldAnno 的属性
* @param annotations
* @param roundEnv
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//扫描所有带@FiledAnno的类
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(FiledAnno.class);
for (Element element : elementsAnnotatedWith) {
VariableElement varField = (VariableElement) element;//这里强转是为了取值
TypeElement typeElement = (TypeElement) varField.getEnclosingElement();
String clssName = typeElement.getQualifiedName().toString();
Name fieldName = varField.getSimpleName();
//拿到类名 拿到属性名 接下来可以为所欲为了
...
}
return false;
}
}
接下来需要将注解处理器注册spring 需要遵循java的spi扩展规范,将注解处理器的完整 路径名 写入到 resource > META-INF > service ;
此方法有替代方案, 导入google的自动注册依赖包
<!-- 自动注册注解库 -->
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-cr5</version>
</dependency>
@AutoService(Processor.class) //使用google的自动注册meta-info的注解
@SupportedAnnotationTypes("com.zld.cloud.FiledAnno") //需要解析的注解
@SupportedSourceVersion(SourceVersion.RELEASE_1)
public class FiledAnnotationProcessor extends AbstractProcessor {
...
}
注解处理器实现了代码增强和动态扩展的功能,除此之外也可以利用反射实现,实际上 jdk的动态代理(Proxy.newProxyInstance())和cglib 底层的实现原理就是反射
//使用代理方式实现代码增强
ChildClass target = new ChildClass();//待增强目标类
//JDK动态代理
ChildClass myClass = (ChildClass) Proxy.newProxyInstance(
target.getClass().getClassLoader(),//类加载器
target.getClass().getInterfaces(),//类所有接口
(proxy, method, params) -> { //反射方式实现代码增强
Class<? extends ChildClass> clazz1 = target.getClass();//获取目标类
Method[] declaredMethods = clazz1.getDeclaredMethods();//获取目标类中的方法
for (Method declaredMethod : declaredMethods) {
// 查找方法中加了自定义注解的
if (declaredMethod.getAnnotation(FiledAnno.class) != null) {
System.out.println("开始执行代码增强逻辑!");
}
}
return method.invoke(target, params);
}
);
myClass.customerDefine("代理类调用方法 ");
工厂设计模式简单实现 实际上是面向过程编程不算设计模式
public class BreakFastBars {
public static void main(String[] args) throws IOException {
BreakFastBars breakFastBars = new BreakFastBars();
BreakFast breakFast = breakFastBars.buy(readCustomerIn());
System.out.println("Please pay for $"+breakFast.getPrice());
}
private BreakFast buy(String breakfast) {
if(StringUtils.isEmpty(breakfast)){
throw new RuntimeException("The food will not be served for the time being!");
}
if(breakfast.equals("Dumplings"))
return new Dumplings();
if(breakfast.equals("Noodles"))
return new Noodles();
if(breakfast.equals("Milk"))
return new Milk();
return null;
}
private static String readCustomerIn() throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("what can i help u!");
String input = bufferedReader.readLine();
return input;
}
}
利用反射实现工厂设计模式,不够灵活,性能消耗
/**
* 利用反射 和 类名创建对应的实例
* @param className
* @return
*/
private BreakFast create(String className) throws Exception{
if(className == null){
throw new RuntimeException("The food will not be served for the time being!");
}
Class<?> clazz = Class.forName("xxxxxxxx.xxxx.xx." + className);
return (BreakFast) clazz.newInstance();
}
利用注解处理器实现工厂模式
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Factory { //自定义Factory注解
String instanceName();
Class instanceType();
}
@AutoService(Processor.class) //谷歌的自动配置spi 省的在META-INFO写路径
@SupportedAnnotationTypes("com.zld.cloud.Factory")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class FactoryAnnotationProcessor extends AbstractProcessor {
private Types typeUtils;//类型处理工具
private Elements elementUtils;//数据元素对象
private Filer filer;//文件
private Messager messager;//警告||异常提示消息
private Map<String,FactoryGroupedClasses> factoryAnnotatedClassMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {//初始化构造函数注入
typeUtils = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(Factory.class);
//从上下文中获取所有加了@Factory注解的元素
try{
for (Element element : elementsAnnotatedWith) {
if(element.getKind() != ElementKind.CLASS){
throw new CustomerProcessorException(element,"only class could be annotated with @%s",Factory.class.getSimpleName());
}
TypeElement typeElement = (TypeElement) element;
//封装注解为注解类
FactoryAnnotatedClass factoryAnnotatedClass = new FactoryAnnotatedClass(typeElement);
//factoryAnnotatedClassMap<@Factory,Map<"Milk",FactoryAnnotatedClass>>
FactoryGroupedClasses factoryGroupedClasses = factoryAnnotatedClassMap.get(factoryAnnotatedClass.getQualifiedFactoryGroupName());
if(factoryGroupedClasses == null){
String qualifiedFactoryGroupName = factoryAnnotatedClass.getQualifiedFactoryGroupName();
factoryGroupedClasses = new FactoryGroupedClasses(qualifiedFactoryGroupName);
factoryAnnotatedClassMap.put(qualifiedFactoryGroupName,factoryGroupedClasses);
}
factoryGroupedClasses.add(factoryAnnotatedClass);
factoryGroupedClasses.generateCode(elementUtils,filer);
factoryAnnotatedClassMap.clear();
}
}catch (CustomerProcessorException e){
error(e.getElement(),e.getMessage());
} catch (IOException e) {
throw new RuntimeException(e);
}
return false;
}
public void error(Element e,String message){
messager.printMessage(Diagnostic.Kind.ERROR,message,e);
}
}
class CustomerProcessorException extends Exception{
Element element;
public CustomerProcessorException(Element element, String message, Object ... args) {
super(String.format(message,args));
this.element = element;
}
public Element getElement(){
return element;
}
}
@Data
class FactoryAnnotatedClass{
private TypeElement typeElement; //被注解的元素
private String qualifiedFactoryGroupName; //指定类型类合法全名
private String simpleFactoryGroupName; //指定类型类简单名
private String instanceName;//注解中指定的ID
/**
* aClass是一个真正的class对象
* @param typeElement
*/
public FactoryAnnotatedClass(TypeElement typeElement) {
try { //增加过@Factory注解的类已经编译过
Factory annotation = typeElement.getAnnotation(Factory.class);
instanceName = annotation.instanceName();
Class<? extends Annotation> aClass = annotation.annotationType();
qualifiedFactoryGroupName = aClass.getCanonicalName();
simpleFactoryGroupName = aClass.getSimpleName();
}catch (MirroredTypeException e){ // 如果还未编译会抛出编译异常
DeclaredType declaredType = (DeclaredType)e.getTypeMirror();
TypeElement element = (TypeElement) declaredType.asElement();
qualifiedFactoryGroupName = element.getQualifiedName().toString();
simpleFactoryGroupName = element.getSimpleName().toString();
}
}
}
class FactoryGroupedClasses{
private static final String SUFFIX = "Factory";
private String qualifiedClassName;
private Map<String, FactoryAnnotatedClass> itemsMap = new LinkedHashMap<String, FactoryAnnotatedClass>();
public FactoryGroupedClasses(String qualifiedClassName) {
this.qualifiedClassName = qualifiedClassName;
}
public void add(FactoryAnnotatedClass factoryAnnotatedClass) {
FactoryAnnotatedClass existing = itemsMap.get(factoryAnnotatedClass.getInstanceName());
if (existing != null) {
// Already existing
throw new CustomerProcessingException(factoryAnnotatedClass.getTypeElement(),
"Conflict: The class %s is annotated with @%s with id ='%s' but %s already uses the same id",
factoryAnnotatedClass.getTypeElement().getQualifiedName().toString(), Factory.class.getSimpleName(),
factoryAnnotatedClass.getInstanceName(), existing.getTypeElement().getQualifiedName().toString());
}
itemsMap.put(factoryAnnotatedClass.getInstanceName(), factoryAnnotatedClass);
}
//java poet 优雅生成java代码 参照 https://www.jianshu.com/p/d0686ce07f4f
public void generateCode(Elements elementUtils, Filer filer) throws IOException {
TypeElement superClassName = elementUtils.getTypeElement(qualifiedClassName);
String factoryClassName = superClassName.getSimpleName() + SUFFIX;
String qualifiedFactoryClassName = qualifiedClassName + SUFFIX;
PackageElement pkg = elementUtils.getPackageOf(superClassName);
String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();
MethodSpec.Builder method = MethodSpec.methodBuilder("create")
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "id")
.returns(TypeName.get(superClassName.asType()));
// check if id is null
method.beginControlFlow("if (id == null)")
.addStatement("throw new IllegalArgumentException($S)", "id is null!")
.endControlFlow();
// Generate items map
for (FactoryAnnotatedClass item : itemsMap.values()) {
method.beginControlFlow("if ($S.equals(id))", item.getInstanceName())
.addStatement("return new $L()", item.getTypeElement().getQualifiedName().toString())
.endControlFlow();
}
method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");
TypeSpec typeSpec = TypeSpec.classBuilder(factoryClassName).addModifiers(Modifier.PUBLIC).addMethod(method.build()).build();
// Write file
JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
}
}