一、反射
1.1 反射机制概述
反射机制是建立在类对象(Class对象)上的。所有的类都存在一个类对象,这个类对象用域提供类本身的信息,比如有几种构造方法,有多少属性,有哪些普通方法······
1.2 反射机制作用
-
Spring的一个核心就是控制反转(Inversion of Control,IoC),其基本原理就是反射机制。如何管理bean,如何由全限定名创建对象······
-
使用反射机制的一个重要目的就是要抵达程度的增强Java的可配置性和可扩展性。
-
使用反射还可以用来破坏单例模式。
package bean; public class Girlfriend { public static final Girlfriend INSTANCE = new Girlfriend("她", 18); private String name; private Integer age; private Girlfriend(String name, Integer age) { this.name = name; this.age = age; } }
public class GirlfriendFactory { public static Girlfriend getGirlfriend(String name, Integer age) { Girlfriend girl = null; try { // 获取Girlfriend类的 Class对象 Class<?> cls = Class.forName("bean.Girlfriend"); // 获取其申明的全部构造方法(包括private的和public的) Constructor<?> constructor = cls.getDeclaredConstructor(String.class, Integer.class); constructor.setAccessible(true); // 调用构造方法创建对象 girl = (Girlfriend) constructor.newInstance(name, age); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { e.printStackTrace(); } return girl; } }
二、注解
2.1 JDK自带注解
-
@Override
声明重写父类方法的注解。
要求编译器帮我们检查是否成功的覆盖,如果没有父类方法,编译器将会进行报错提示 。 -
@Deprecated
声明方法过时了,不在建议使用。
要求编译器在编译的过程中对于这样的方法发出警告,提示方法过时 。 -
@SuppressWarnings()
压制警告。
提示编译器,在编译的过程中对指定类型的警告不再提示 。常用的参数有
unchecked
、unused
、all
等。 -
@FunctionalInterface
约定函数式接口。
在编译时检查接口是否为函数式接口(有且只有一个抽象方法的接口) 。
2.2 四个元注解
元注解用于构成自定义注解。而自定义注解的机会并不是很多,只列举了一些最最最常用的参数。
-
@Target
描述注解的使用范围(即:被修饰的注解可以用在什么地方) 。
常用参数有
ElementType.TYPE
、ElementType.FILED
、ElementType.METHOD
、ElementType.PARAMETER
等。 -
@Retention
描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。
常用参数有
RetentionPolicy.RUNTIME
等。 -
@Documented
描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息 。
-
@Inherited
使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解) 。
2.3 自定义注解
2.3.1 定义
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
String value();
}
2.3.2 使用
package bean;
public class Girlfriend {
@Name(value = "她")
private String name;
@Age
private Integer age;
}
2.3.3 解析
public class GirlfriendFactory {
public static Girlfriend getGirlfriend() {
Girlfriend girl = null;
try {
// 获取Class对象
Class<?> cls = Class.forName("bean.Girlfriend");
// 获取默认public无参构造器
Constructor<?> constructor = cls.getDeclaredConstructor();
// 创建对象
girl = (Girlfriend) constructor.newInstance();
// 遍历所有申明的字段(包括private和public的)
for (Field field : cls.getDeclaredFields()) {
field.setAccessible(true);
// 如果注解修饰则给该字段注入相应的值
if (field.isAnnotationPresent(Name.class)) {
Name annotation = field.getAnnotation(Name.class);
field.set(girl, annotation.value());
} else if (field.isAnnotationPresent(Age.class)) {
Age annotation = field.getAnnotation(Age.class);
field.set(girl, annotation.value());
}
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
return girl;
}
}
三、控制反转
控制反转(Inversion of Control,IoC)是一种设计原则/设计思想,可以用来降低代码之间的耦合度。
IoC的实现方式有依赖注入(Dependency Injection,DI)和依赖查找(Dependency Lookup,DL)两种。而Spring中的IoC使用的是DI。
IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中。
3.1 依赖注入
依赖注入是指自身对象中的内置对象是通过注入的方式创建的。依赖注入有两种实现方式:setter注入和构造器注入。IoC容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(bean的setter方法)或者是构造器传递给需要的对象。相对于IoC而言,DI更加准确大的描述了IoC的设计理念。
所谓依赖注入, 即对象之间的依赖关系由容器在应用系统运行期来决定。也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的对象之中 。
3.2 IoC和DI的区别和联系
- IoC是设计思想/设计原则;而DI是IoC的实现方式。
- DI是后来别的大佬总结出来的能够更加精确描述IoC理念的实现方式。
- 在Spring中IoC基本上等价于DI。
四、面向切面编程
面向切面编程(Aspect Oriented Programming,AOP)的是依靠动态代理模式去实现的。(当然也不仅仅限于动态代理)。所谓的代理就是通过代理对象去调用真实对象,调用代理方法会同时拦截真实方法,并可以加一些别的逻辑。
通常情况下:
- 真实对象的方法通常是核心业务逻辑(如增删改查)——可以称为切点。
- 代理对象加入的是周边功能逻辑(如记录日志)——可以抽取封装为切面。
- 这样代理的过程也就是将切点和切面编制在一起的过程。
五、Spring的主要设计模式
一个优秀成熟的Java框架,必然离不开反射和代理。
推荐博客 Spring框架中的设计模式(二)