实现Spring框架中的IOC和AOP


实现IOC

为什么要用IOC

我们知道Java 是一门面向对象的语言,在 Java 中 Everything is Object,我们的程序就是由若干对象组成的。当我们的项目越来越大,合作的开发者越来越多的时候,我们的类就会越来越多,类与类之间的引用就会成指数级的增长。如下图所示:

image.png

这样的工程简直就是灾难,如果我们引入 Ioc 框架。由框架来维护类的生命周期和类之间的引用。我们的系统就会变成这样:

image.png

这个时候我们发现,我们类之间的关系都由 IoC 框架负责维护类,同时将类注入到需要的类中。也就是类的使用者只负责使用,而不负责维护。把专业的事情交给专业的框架来完成。大大的减少开发的复杂度。

IOC的实现步骤

  1. 初始化 IoC 容器。
  2. 读取配置文件。
  3. 将配置文件转换为容器识别对的数据结构(这个数据结构在Spring中叫做 BeanDefinition
  4. 利用数据结构依次实例化相应的对象
  5. 注入对象之间的依赖关系

bean 定义了框架的数据结构

定义了bean的数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@ToString
public class BeanDefinition {

private String name;

private String className;

private String[] interfaceName;

private List<ConstructorArg> constructorArgs;

private List<PropertyArg> propertyArgs;

}

包含了对象的 name,class的名称。如果是接口的实现,还有该对象实现的接口。以及构造函数的传参的列表 constructorArgs 和需要注入的参数列表 propertyArgs

Utils工具包

ClassUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ClassUtils {

public static ClassLoader getDefultClassLoader(){
return Thread.currentThread().getContextClassLoader();
}

public static Class<?> loadClass(String className){
try {
return getDefultClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

return null;
}

}

这段代码是一个工具类 ClassUtils,提供了两个静态方法:

  1. getDefultClassLoader() 方法返回当前线程的上下文类加载器(Context Class Loader);
  2. loadClass(String className) 方法用于根据类名加载对应的 Class 对象,首先调用 getDefultClassLoader() 方法获取默认的类加载器,然后使用该类加载器加载指定的类。如果指定的类名不存在,该方法将会打印异常栈信息,并返回 null。

这个工具类可以方便地加载任何类,尤其在使用反射技术时非常有用,因为你可以通过类名动态地获取 Class 对象。同时,由于使用了线程的上下文类加载器,也可以避免一些类加载器隔离的问题。

BeanUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BeanUtils {

public static <T> T instanceByCglib(Class<T> clz,Constructor ctr,Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clz);
enhancer.setCallback(NoOp.INSTANCE);

if(ctr == null){
return (T) enhancer.create();
}else {
return (T) enhancer.create(ctr.getParameterTypes(),args);
}
}

}

这段代码是一个工具类 BeanUtils,提供了一个静态方法 instanceByCglib,用于创建对象。具体而言:

  1. Class 参数表示要创建的对象的类;
  2. Constructor 参数表示要使用的构造方法,可以为 null;
  3. Object[] 参数表示构造方法的参数数组,如果 Constructor 参数为 null,则此参数也应为 null。

该方法使用 CGLIB 库创建对象。首先创建一个 Enhancer 对象,并设置其父类为 clz,然后设置回调为 NoOp.INSTANCE,这意味着创建的对象不会被代理。如果 ctr 为 null,则直接通过 enhancer.create() 创建对象,否则通过 enhancer.create(ctr.getParameterTypes(), args) 使用指定的构造方法创建对象。

CGLIB 是一个基于 ASM 库的代码生成库,可以用于创建动态代理、增强类等。在本代码中,CGLIB 用于创建对象并调用构造方法。这种方式相对于使用 new 运算符创建对象,具有更高的灵活性和动态性。

用法举例:AOP

这个也是aop的用法
在cglib包下,Enhancer这个类的作用是为指定的类创建代理类。具体来说,Enhancer类可以动态地生成一个指定类的子类,该子类可以用来拦截指定类中的方法调用,从而实现代理模式。
Enhancer类的使用方法类似于Java动态代理中的Proxy类。首先,需要创建一个Enhancer对象,并设置要代理的目标类和回调方法。然后,通过调用Enhancer对象的create方法,生成代理类的实例。这个代理类会继承目标类,同时实现回调方法,从而实现对目标类方法的拦截和处理。
Enhancer类的使用比较灵活,可以代理任意的类,包括没有实现任何接口的类。但是,由于Enhancer是通过生成目标类的子类来实现代理的,所以目标类必须有默认的构造函数,并且不能是final类。
假设我们有一个简单的UserService接口,其中定义了一个getUser方法:

1
2
3
public interface UserService {
User getUser(int id);
}

现在我们想为该接口创建一个代理类,记录getUser方法的调用次数。我们可以使用cglib的Enhancer类来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class UserServiceProxy implements MethodInterceptor {

private UserService target;
private int count = 0;

public UserServiceProxy(UserService target) {
this.target = target;
}

public static UserService createProxy(UserService target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new UserServiceProxy(target));
return (UserService) enhancer.create();
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().equals("getUser")) {
count++;
}
return proxy.invoke(target, args);
}

public int getCount() {
return count;
}
}

在上面的代码中,我们定义了一个UserServiceProxy类,它实现了MethodInterceptor接口,用于拦截目标对象的方法调用。在构造方法中,我们传入了目标对象target,并使用Enhancer类生成一个UserService的代理对象。在intercept方法中,我们记录了getUser方法的调用次数,并使用MethodProxy类来调用目标对象的对应方法。
接下来,我们可以使用该代理类来调用getUser方法,并检查调用次数:

1
2
3
4
UserService userService = UserServiceProxy.createProxy(new UserServiceImpl());
userService.getUser(1);
userService.getUser(2);
System.out.println(userService.getCount()); // output: 2

上面的代码中,我们首先使用createProxy方法创建了UserService的代理对象,并调用了getUser方法两次。最后,我们打印了代理对象的调用次数,发现它的确记录了两次getUser方法的调用。
它为什么要这么做呢,直接new难道不行吗?
使用代理模式的一个主要目的是在不改变原有代码的情况下,为现有对象添加额外的行为或功能。在上面的例子中,我们通过使用cglib的Enhancer类创建了一个代理对象,使得我们可以在不修改原有UserServiceImpl类的情况下,为getUser方法添加了记录调用次数的功能。
如果直接new一个新的对象,虽然可以实现类似的功能,但是需要对原有代码进行修改,将原有的对象替换成新的对象,这样就不太符合“开闭原则”,即对扩展开放,对修改关闭的设计原则。同时,在某些情况下,我们可能无法直接访问到原有对象的构造函数,这就导致了不能直接创建新对象的限制。
使用代理模式,可以通过创建代理对象来扩展原有对象的行为,而不需要修改原有对象的代码。同时,代理对象还可以在原有对象的基础上增加额外的行为,提高代码的可扩展性和可维护性。

ReflectionUtils

1
2
3
4
5
6
7
8
9
public class ReflectionUtils {

public static void injectField(Field field,Object obj,Object value) throws IllegalAccessException {
if(field != null) {
field.setAccessible(true);
field.set(obj, value);
}
}
}

这段代码是一个工具类 ReflectionUtils,提供了一个静态方法 injectField,用于设置某个对象的属性值。具体而言:

  1. Field 参数表示要设置的属性的字段;
  2. Object 参数表示要设置属性值的对象;
  3. Object 参数表示要设置的属性值。

该方法首先判断 field 是否为空,如果不为空则将其设置为可访问,然后使用 field.set(obj, value) 方法设置对象的属性值。如果属性为 final 修饰的静态变量,则此方法将无法修改其值。

这个工具类可以方便地在代码中设置对象的属性值,尤其在使用反射技术时非常有用,因为你可以通过反射动态修改对象的属性。但是,使用反射技术也有一些缺点,例如性能较低、容易引起安全问题等,需要谨慎使用。

JsonUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*JsonUtils 的作用就是为了解析我们的json配置文件。代码比较长,与我们的 IoC 原理关系不大*/
public class JsonUtils {

private static final ObjectMapper mapper = new ObjectMapper();

private JsonUtils() {
}

public static ObjectMapper getObjectMapper() {
return mapper;
}

public static <T> T readValue(String json, Class<T> cls) {
try {
return mapper.readValue(json, cls);
} catch (Exception var3) {
return null;
}
}

public static <T> T readValue(InputStream is,Class<T> cls){
try{
return mapper.readValue(is,cls);
}catch (Exception e){
return null;
}
}

public static <T> T readValue(byte[] bytes, Class<T> cls) {
try {
return mapper.readValue(bytes, cls);
} catch (Exception var3) {
return null;
}
}

public static <T> T readValue(String json, TypeReference valueTypeRef) {
try {
return mapper.readValue(json, valueTypeRef);
} catch (Exception var3) {
return null;
}
}

public static <T> T readValue(byte[] bytes, TypeReference valueTypeRef) {
try {
return mapper.readValue(bytes, valueTypeRef);
} catch (Exception var3) {
return null;
}
}

public static <T> T readValue(InputStream is, TypeReference valueTypeRef) {
try {
return mapper.readValue(is, valueTypeRef);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public static String writeValue(Object entity) {
try {
return mapper.writeValueAsString(entity);
} catch (Exception var2) {
return null;
}
}

public static byte[] writeByteValue(Object entity) {
try {
return mapper.writeValueAsBytes(entity);
} catch (Exception var2) {
return null;
}
}

static {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
mapper.getDeserializationConfig().withoutFeatures(new DeserializationFeature[]{DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES});
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

}

这段代码是一个工具类,封装了基于Jackson库的Json序列化和反序列化功能。具体作用如下:

  • 定义了一个ObjectMapper对象,用于将Java对象转化为JSON格式,或将JSON格式转化为Java对象。
  • 提供了多个readValue()和writeValue()方法,用于不同类型之间的转化。其中,readValue()方法支持将JSON格式的字符串、输入流或字节数组反序列化成Java对象,而writeValue()方法则将Java对象序列化成JSON格式的字符串或字节数组。
  • 设置了一些序列化和反序列化的配置,如格式化输出、忽略空值、支持单引号、允许注释等。
  • 如果反序列化失败,则返回null

BeanFactory

先定义一个接口:

1
2
3
4
5
public interface BeanFactory {

Object getBean(String name) throws Exception;

}

有对应的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public class BeanFactoryImpl implements BeanFactory{

private static final ConcurrentHashMap<String,Object> beanMap = new ConcurrentHashMap<>();

private static final ConcurrentHashMap<String, BeanDefinition> beanDefineMap= new ConcurrentHashMap<>();

private static final Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());

private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

@Override
public Object getBean(String name) throws Exception {
//查找对象是否已经实例化过
Object bean = beanMap.get(name);
if(bean != null){
return bean;
}
//循环依赖问题
Object earlyBean = earlySingletonObjects.get(name);
if (earlyBean != null) {
System.out.println("发生了循环依赖,提前返回尚未加载完成的bean:" + name);
return earlyBean;
}
//如果没有实例化,那就需要调用createBean来创建对象
BeanDefinition beanDefinition = beanDefineMap.get(name);
bean = createBean(beanDefinition);

if(bean != null) {
//为了解决循环依赖,先添加到早期单例中
earlySingletonObjects.put(name, bean);

//对象创建成功以后,注入对象需要的参数
populateBean(bean,beanDefinition);

//再吧对象存入Map中方便下次使用。
beanMap.put(name,bean);

//从早期单例Map中移除
earlySingletonObjects.remove(name);
}

//结束返回
return bean;
}

protected void registerBean(String name, BeanDefinition bd){
beanDefineMap.put(name,bd);
beanNameSet.add(name);
}

private Object createBean(BeanDefinition beanDefinition) throws Exception {
String beanName = beanDefinition.getClassName();
//获取类
Class<?> clz = ClassUtils.loadClass(beanName);
if(clz == null) {
throw new Exception("can not find bean by beanName");
}
//获取构造器参数
List<ConstructorArg> constructorArgs = beanDefinition.getConstructorArgs();
if(constructorArgs != null && !constructorArgs.isEmpty()){
List<Object> objects = new ArrayList<>();
for (ConstructorArg constructorArg : constructorArgs) {
//放入Map
objects.add(getBean(constructorArg.getRef()));
}
return BeanUtils.instanceByCglib(clz,clz.getConstructor(),objects.toArray());
}else {
return BeanUtils.instanceByCglib(clz,null,null);
}
}

private void populateBean(Object bean, BeanDefinition bd) throws Exception {
List<PropertyArg> propertyArgs = bd.getPropertyArgs();
if (propertyArgs != null && !propertyArgs.isEmpty()) {
//遍历在json中配置的属性
for (PropertyArg arg : propertyArgs) {
String propertyName = arg.getName();
String value = arg.getValue();
String ref = arg.getRef();
Object injectValue = null;
if (value != null) {
//直接赋值
injectValue = value;
} else if (ref != null && !"".equals(ref)) {
//获取bean
injectValue = getBean(ref);
}
Method method = getPropertySetter(bd, propertyName, injectValue);
method.invoke(bean, injectValue);
}
}
}

/**
* 获取具体某个属性的setter方法
* 此处做法比较简单粗暴
* 实际上Spring在读取配置文件时就已经将各属性,方法,getter/setter都读取好了。
* 在这就只需要调用BeanWrapper的方法来为属性赋值就可以了。
*/
private Method getPropertySetter(BeanDefinition bd, String propertyName, Object injectValue) throws Exception {
Class<?> beanClass = Class.forName(bd.getClassName());
Class<?> injectClazz = injectValue.getClass();
Class<?> supClass = injectValue.getClass().getSuperclass();
if (supClass != null && supClass != Object.class) {
injectClazz = supClass;
}
propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
Method setter = beanClass.getMethod("set" + propertyName, injectClazz);
return setter;
}
}

这段代码实现了一个简单的Bean工厂,主要功能是通过Bean的名字获取Bean的实例,实现了Bean的创建、注入属性和循环依赖的处理。

其中,BeanFactoryImpl类实现了BeanFactory接口,定义了三个ConcurrentHashMap类型的变量,分别用来存储Bean实例、Bean定义和Bean名称集合。它还包含了一个earlySingletonObjects成员变量,用来处理循环依赖的问题。

getBean

getBean 方法是 Spring 框架中的一个核心方法,用于从 BeanFactory 或 ApplicationContext 中获取一个已经创建好的 Bean 实例。该方法接收一个参数 name,表示需要获取的 Bean 的名称。该方法会根据给定的名称先在 BeanFactory 中查找是否已经有该名称对应的 Bean 实例,如果没有找到,则尝试创建一个新的实例并将其注册到 BeanFactory 中。
在实现类 BeanFactoryImpl 中,该方法会先从 beanMap 中查找是否已经有该名称对应的 Bean 实例,如果找到了则直接返回该实例。如果没有找到,则需要通过 createBean 方法创建一个新的实例。在创建实例之前,该方法会检查 earlySingletonObjects 中是否已经有该名称对应的早期单例对象,如果有则说明存在循环依赖的问题,此时会返回早期单例对象,避免出现死锁的情况。
getBean 方法会调用 createBean 方法创建 Bean 实例,然后调用 populateBean 方法为实例注入需要的属性。在注入属性之后,该方法会将创建好的 Bean 实例注册到 beanMap 中,以备下次获取时直接返回。
总之,getBean 方法的作用是从 BeanFactory 或 ApplicationContext 中获取一个已经创建好的 Bean 实例,如果找不到则创建一个新的实例,并且可以解决循环依赖的问题。

createBean

createBean 方法的作用是通过传入的 BeanDefinition 对象,使用反射来实例化一个 Java 对象,并返回该对象的实例。该方法首先通过 BeanDefinition 对象中的类名获取该类的 Class 对象,然后根据该对象获取相应的构造函数,如果有构造函数的参数,则递归调用 getBean 方法获取构造函数的参数,最后使用 CGLIB 库的 BeanUtils.instanceByCglib 方法实例化一个 Java 对象并返回。如果构造函数没有参数,则直接使用 BeanUtils.instanceByCglib 方法实例化一个 Java 对象并返回。如果获取 Class 对象失败,会抛出异常。

populateBean

populateBean方法的作用是为一个JavaBean对象注入它所依赖的其他对象或属性,这些对象或属性在BeanDefinition中被定义,并且在配置文件中被配置好。populateBean方法会根据BeanDefinition中的属性列表,将对应的属性值设置到JavaBean对象中。属性的值可能是简单类型的值,也可能是其他JavaBean对象的引用。
具体而言,populateBean方法会遍历BeanDefinition中的属性列表,对于每一个属性,它会根据属性的名称、值或引用,找到JavaBean对象中对应的setter方法,然后调用该方法将属性值设置到JavaBean对象中。需要注意的是,setter方法的名称是根据属性名生成的,例如,如果属性名是foo,那么setter方法的名称就是setFoo。
populateBean方法的实现比较简单粗暴,实际上,Spring框架在读取配置文件时已经将各属性、方法、getter/setter都读取好了,并且使用了更加高效和灵活的方式来为JavaBean对象注入属性值。

这两行代码的作用是为一个Java Bean对象的属性赋值。

1
2
Method method = getPropertySetter(bd, propertyName, injectValue);
method.invoke(bean, injectValue);

第一行代码使用getPropertySetter方法获取一个指定属性的Setter方法。getPropertySetter方法根据属性名称、注入值类型和Bean定义,动态地获取该属性的Setter方法。这里使用反射机制,构造出要调用的方法,以便在第二行代码中使用。
第二行代码使用反射机制调用Java Bean对象的Setter方法,并把该属性的值作为参数传入。这里的bean是要赋值的Java Bean对象,injectValue是要注入的值。

getPropertySetter

getPropertySetter方法的作用是根据传入的BeanDefinition、属性名和属性值,获取对应的setter方法,并返回该方法。在populateBean方法中,会根据BeanDefinition中配置的属性名、属性值以及对应的setter方法,将属性值设置到对象中,实现对对象属性的注入。getPropertySetter方法通过反射获取类的setter方法,并根据属性值的类型确定setter方法的参数类型,最终返回setter方法,供populateBean方法调用。

其他属性

这段代码实现了一个简单的 Bean 工厂。主要功能包括:

  1. 实现了 BeanFactory 接口,提供了 getBean() 方法来获取指定名称的 Bean 实例。
  2. 实现了注册 BeanDefinition 的方法 registerBean(),用来将 BeanDefinition 存储到 Map 中。
  3. 使用 ConcurrentHashMap 来存储 Bean 实例对象和 BeanDefinition 对象。
  4. 支持循环依赖。通过使用 ConcurrentHashMap 存储早期的 Bean 实例对象,解决了循环依赖问题。
  5. 支持注入属性。通过在 BeanDefinition 中配置 PropertyArg 属性参数列表,来对 Bean 实例对象进行属性注入。
  6. 支持构造器注入。通过在 BeanDefinition 中配置 ConstructorArg 构造器参数列表,来对 Bean 实例对象进行构造器注入。
  7. 支持通过反射创建 Bean 实例对象,使用 CGLib 动态代理技术生成代理对象。

总体来说,这段代码实现了一个简单的 IoC 容器,能够解决基本的 Bean 创建、管理和依赖注入的问题。但是这个容器还有一些限制,例如只支持单例模式、只支持 setter 注入、只能通过 BeanDefinition 来配置 Bean 等等。真正的 IoC 容器,例如 Spring,还有更多更复杂的功能和特性。

ApplicationContext

所谓的容器,就是对BeanFactory的扩展,负责管理 BeanFactory。我们的这个IoC 框架使用 Json 作为配置文件,所以我们容器就命名为 JsonApplicationContext。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class JsonApplicationContext extends BeanFactoryImpl{

private final String fileName;

public JsonApplicationContext(String fileName) {
this.fileName = fileName;
}

public void init(){
loadFile();
}

private void loadFile(){

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);

List<BeanDefinition> beanDefinitions = JsonUtils.readValue(is,new TypeReference<List<BeanDefinition>>(){});

if(beanDefinitions != null && !beanDefinitions.isEmpty()) {

for (BeanDefinition beanDefinition : beanDefinitions) {
registerBean(beanDefinition.getName(), beanDefinition);
}
}

}
}

这段代码定义了一个名为JsonApplicationContext的类,继承了BeanFactoryImpl类。JsonApplicationContext类的作用是通过从Json文件中读取bean定义来初始化IoC容器。它包含了一个构造方法和两个私有方法:init()和loadFile()。
构造方法JsonApplicationContext(String fileName)接收Json文件的名称,并将其存储在fileName变量中。
方法init()用于初始化IoC容器,它调用了loadFile()方法从Json文件中加载bean定义。
方法loadFile()用于加载Json文件并注册bean定义到IoC容器中。它首先通过当前线程的类加载器获取Json文件的输入流,然后通过JsonUtils.readValue()方法将输入流转换成BeanDefinition对象列表。最后,它遍历BeanDefinition列表,并调用registerBean()方法注册bean定义到IoC容器中。
总之,这段代码是一个简单的IoC容器实现,它可以从Json文件中读取bean定义,并将其注册到容器中,以供应用程序使用。

测试结果

image.png



实现AOP

Aop 是什么

面向切面的程序设计(aspect-oriented programming,AOP)。通过预编译方式和运行期动态代理实现程序功能统一维护的一种技术。

为什么需要使用Aop

面向切面编程,实际上就是通过预编译或者动态代理技术在不修改源代码的情况下给原来的程序统一添加功能的一种技术。我们看几个关键词,第一个是“动态代理技术”,这个就是Spring Aop实现底层技术。第二个“不修改源代码”,这个就是Aop最关键的地方,也就是我们平时所说的非入侵性。。第三个“添加功能”,不改变原有的源代码,为程序添加功能。

举个例子:如果某天你需要统计若干方法的执行时间,如果不是用Aop技术,你要做的就是为每一个方法开始的时候获取一个开始时间,在方法结束的时候获取结束时间。二者之差就是方法的执行时间。如果对每一个需要统计的方法都做如上的操作,那代码简直就是灾难。如果我们使用Aop技术,在不修改代码的情况下,添加一个统计方法执行时间的切面。代码就变得十分优雅。具体这个切面怎么实现?
Spring Aop实现的代码非常非常的绕。也就是说 Spring 为了灵活做了非常深层次的抽象。同时 Spring为了兼容 @AspectJ 的Aop协议,使用了很多 Adapter (适配器)模式又进一步的增加了代码的复杂程度。
Spring 的 Aop 实现主要以下几个步骤:

  1. 初始化 Aop 容器。
  2. 读取配置文件。
  3. 将配置文件装换为 Aop 能够识别的数据结构 – Advisor。这里展开讲一讲这个advisor。Advisor对象中包又含了两个重要的数据结构,一个是 Advice,一个是 Pointcut。Advice的作用就是描述一个切面的行为,pointcut描述的是切面的位置。两个数据结的组合就是”在哪里,干什么“。这样 Advisor 就包含了”在哪里干什么“的信息,就能够全面的描述切面了。
  4. Spring 将这个 Advisor 转换成自己能够识别的数据结构 – AdvicedSupport。Spirng 动态的将这些方法拦截器织入到对应的方法。
  5. 生成动态代理代理。
  6. 提供调用,在使用的时候,调用方调用的就是代理方法。也就是已经织入了增强方法的方法

Invocation

先定义好方法调用接口和代理方法调用接口:

1
2
3
4
5
6
7
8
9
public interface MethodInvocation {

Method getMethod();

Object[] getArguments();

Object proceed() throws Throwable;

}
1
2
3
4
5
6
7
8
9
/**
* 代理方法的调用
*/

public interface ProxyMethodInvocation extends MethodInvocation {

Object getProxy();

}

CglibMethodInvocation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CglibMethodInvocation extends ReflectioveMethodeInvocation {

private final MethodProxy methodProxy;

public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, List<AopMethodInterceptor> interceptorList, MethodProxy methodProxy) {
super(proxy, target, method, arguments, interceptorList);
this.methodProxy = methodProxy;
}

@Override
protected Object invokeOriginal() throws Throwable {
return methodProxy.invoke(target,arguments);
}
}

这段代码是定义了一个基于CGLIB动态代理的方法调用类CglibMethodInvocation,它继承了ReflectiveMethodeInvocation类,其中包含了目标对象、目标方法、方法参数以及一组方法拦截器,它们一起构成了一个完整的方法调用链。CglibMethodInvocation在父类的基础上增加了一个MethodProxy类型的属性methodProxy,并重写了invokeOriginal()方法,该方法使用methodProxy对象调用目标方法,实现了方法的调用。该类主要在AOP中使用,用于执行目标方法,并将方法拦截器应用于目标方法之前或之后的操作。

ReflectioveMethodeInvocation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class ReflectioveMethodeInvocation implements ProxyMethodInvocation {

public ReflectioveMethodeInvocation(Object proxy, Object target, Method method, Object[] arguments, List<AopMethodInterceptor> interceptorList) {
this.proxy = proxy;
this.target = target;
this.method = method;
this.arguments = arguments;
this.interceptorList = interceptorList;
}

protected final Object proxy;

protected final Object target;

protected final Method method;

protected Object[] arguments = new Object[0];

protected final List<AopMethodInterceptor> interceptorList;

private int currentInterceptorIndex = -1;

@Override
public Object getProxy() {
return proxy;
}

@Override
public Method getMethod() {
return method;
}

@Override
public Object[] getArguments() {
return arguments;
}

@Override
public Object proceed() throws Throwable {

//执行完所有的拦截器后,执行目标方法
if(currentInterceptorIndex == this.interceptorList.size() - 1) {
return invokeOriginal();
}

//迭代的执行拦截器。回顾上面的讲解,我们实现的拦击都会执行 im.proceed() 实际上就在调用这个方法。
AopMethodInterceptor interceptor = interceptorList.get(++currentInterceptorIndex);
return interceptor.invoke(this);

}

protected Object invokeOriginal() throws Throwable{
return ReflectionUtils.invokeMethodUseReflection(target,method,arguments);
}

}

这段代码定义了一个基础的反射方法调用的实现类 ReflectiveMethodInvocation,实现了 ProxyMethodInvocation 接口。在 Spring 的 AOP 中,每个被拦截的方法都会被包装成一个 MethodInvocation,这个包装类里包含了目标对象、方法、参数等信息,并提供了一个 proceed 方法用来执行下一个拦截器或目标方法。

ReflectiveMethodInvocation 中的 proceed 方法主要用于按照拦截器链依次执行 AopMethodInterceptor 的拦截逻辑。如果拦截器都执行完成,最后就会调用 invokeOriginal 方法来执行目标方法。另外,这个类也提供了获取代理对象、获取方法、获取参数等方法。

Interceptor

1
2
3
4
5
6
7
8
9
10
11
public class BeforeMethodAdviceInterceptor implements AopMethodInterceptor {
private BeforeMethodAdvice advice;
public BeforeMethodAdviceInterceptor(BeforeMethodAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
advice.before(mi.getMethod(),mi.getArguments(),mi);
return mi.proceed();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AfterRunningAdviceInterceptor implements AopMethodInterceptor {
private AfterRunningAdvice advice;

public AfterRunningAdviceInterceptor(AfterRunningAdvice advice) {
this.advice = advice;
}

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object returnVal = mi.proceed();
advice.after(returnVal,mi.getMethod(),mi.getArguments(),mi);
return returnVal;
}
}

实际上 mi.proceed()才是执行原有的方法。而advice我们上文就说过,是描述增强的方法”干什么“的数据结构,所以对于这个before拦截器,我们就把advice对应的增强方法放在了真正执行的方法前面。而对于after拦截器而言,就放在了真正执行的方法后面。
这里主要是定义拦截器,一个before,一个after。

Advisor

1
2
3
4
5
6
7
8
@Data
public class Advisor {
//干什么
private Advice advice;
//在哪里
private Pointcut pointcut;

}

数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
public class AdvisedSupport extends Advisor {

private TargetSource targetSource;

private List<AopMethodInterceptor> list = new LinkedList<>();

public void addAopMethodInterceptor(AopMethodInterceptor interceptor){
list.add(interceptor);
}

public void addAopMethodInterceptors(List<AopMethodInterceptor> interceptors){
list.addAll(interceptors);
}

}

这个AdvisedSupport就是我们Aop框架能够理解的数据结构,这个时候问题就变成了对于哪个目标,增加哪些拦截器。这里的AopMethodInterceptor 是一个接口,before拦截器和after拦截器都实现了这个接口。都能够使用这个功能。

Core

CglibAopProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Data
public class CglibAopProxy implements AopProxy{

private AdvisedSupport advised;

private Object[] constructorArgs;

private Class<?>[] constructorArgTypes;

public CglibAopProxy(AdvisedSupport config){
this.advised = config;
}

@Override
public Object getProxy() {
return getProxy(null);
}

@Override
public Object getProxy(ClassLoader classLoader) {

Class<?> rootClass = advised.getTargetSource().getTagetClass();

if(classLoader == null){
classLoader = ClassUtils.getDefultClassLoader();
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(rootClass.getSuperclass());
//增加拦截器的核心方法
Callback callbacks = getCallBack(advised);
enhancer.setCallback(callbacks);
enhancer.setClassLoader(classLoader);
if(constructorArgs != null && constructorArgs.length > 0){
return enhancer.create(constructorArgTypes,constructorArgs);
}

return enhancer.create();
}
private Callback getCallBack(AdvisedSupport advised) {
return new DynamicAdvisedInterceptor(advised.getList(),advised.getTargetSource());
}
}

这段代码是实现 Cglib AOP 代理的核心逻辑。其中 CglibAopProxy 类实现了 AopProxy 接口,用于创建代理对象,而 AdvisedSupport 是 AOP 配置信息的载体。
具体来说,getProxy() 方法返回代理对象,getProxy(ClassLoader classLoader) 方法返回通过指定类加载器加载的代理对象。在这两个方法中,通过使用 CGLIB 提供的 Enhancer 类,实现代理对象的创建和设置拦截器。
在 getCallBack(AdvisedSupport advised) 方法中,通过将 AdvisedSupport 中的拦截器链和目标对象传递给 DynamicAdvisedInterceptor,最终得到一个 Callback 对象,它是 Enhancer 的回调函数。
回到 getProxy() 和 getProxy(ClassLoader classLoader) 方法,它们的核心是通过 Enhancer 的 setSuperclass 方法,设置代理对象的父类,然后通过 setCallback 方法,将 Callback 对象设置为拦截器。
在这个过程中,如果 constructorArgs 不为空,则使用带参数的 enhancer.create 方法创建代理对象。否则,使用不带参数的 enhancer.create 方法创建代理对象。
最后,通过调用 enhancer.create 方法返回代理对象。
看看核心方法:
:::info
//增加拦截器的核心方法
Callback callbacks = getCallBack(advised);
:::

private Callback getCallBack(AdvisedSupport advised) {
return new DynamicAdvisedInterceptor(advised.getList(),advised.getTargetSource());
}

DynamicAdvisedInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DynamicAdvisedInterceptor implements MethodInterceptor {

protected final List<AopMethodInterceptor> interceptorList;
protected final TargetSource targetSource;

public DynamicAdvisedInterceptor(List<AopMethodInterceptor> interceptorList, TargetSource targetSource) {
this.interceptorList = interceptorList;
this.targetSource = targetSource;
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);
return invocation.proceed();
}
}

这段代码实现了CGLib动态代理中的拦截器,它实现了MethodInterceptor接口。在intercept方法中,它接收了被代理的对象(obj)、目标方法(method)、方法参数(args)以及方法代理(proxy),然后使用这些参数创建一个CglibMethodInvocation对象,然后调用该对象的proceed()方法,实现拦截器的链式执行。这个拦截器类将所有的AOP拦截器封装成了一个拦截器链,同时传入了目标对象的引用(TargetSource),以便执行目标方法。在实际调用的时候,这个拦截器会按照拦截器列表依次调用所有的拦截器,并在最后调用目标方法,实现了AOP拦截器的动态织入。

而其方法:

1
2
3
4
5
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);
return invocation.proceed();
}

调用的方法正是:CglibMethodInvocation

AopBeanFactoryImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class AopBeanFactoryImpl extends BeanFactoryImpl{

private static final ConcurrentHashMap<String, AopBeanDefinition> aopBeanDefinitionMap = new ConcurrentHashMap<>();

private static final ConcurrentHashMap<String,Object> aopBeanMap = new ConcurrentHashMap<>();

@Override
public Object getBean(String name) throws Exception {

Object aopBean = aopBeanMap.get(name);

if(aopBean != null){
return aopBean;
}

if(aopBeanDefinitionMap.containsKey(name)){
AopBeanDefinition aopBeanDefinition = aopBeanDefinitionMap.get(name);
AdvisedSupport advisedSupport = getAdvisedSupport(aopBeanDefinition);
aopBean = new CglibAopProxy(advisedSupport).getProxy();
aopBeanMap.put(name,aopBean);
return aopBean;
}

return super.getBean(name);
}

protected void registerBean(String name, AopBeanDefinition aopBeanDefinition){
aopBeanDefinitionMap.put(name,aopBeanDefinition);
}

private AdvisedSupport getAdvisedSupport(AopBeanDefinition aopBeanDefinition) throws Exception {

AdvisedSupport advisedSupport = new AdvisedSupport();
List<String> interceptorNames = aopBeanDefinition.getInterceptorNames();
if(interceptorNames != null && !interceptorNames.isEmpty()){
for (String interceptorName : interceptorNames) {

Advice advice = (Advice) getBean(interceptorName);

Advisor advisor = new Advisor();
advisor.setAdvice(advice);

if(advice instanceof BeforeMethodAdvice){
AopMethodInterceptor interceptor = BeforeMethodAdviceAdapter.getInstants().getInterceptor(advisor);
advisedSupport.addAopMethodInterceptor(interceptor);
}

if(advice instanceof AfterRunningAdvice){
AopMethodInterceptor interceptor = AfterRunningAdviceAdapter.getInstants().getInterceptor(advisor);
advisedSupport.addAopMethodInterceptor(interceptor);
}

}
}

TargetSource targetSource = new TargetSource();

Object object = getBean(aopBeanDefinition.getTarget());

targetSource.setTagetClass(object.getClass());
targetSource.setTagetObject(object);

advisedSupport.setTargetSource(targetSource);


return advisedSupport;

}

}

这段代码定义了一个AOP Bean Factory,它是一个扩展了普通Bean Factory的类,用于创建和管理AOP bean。该类继承了BeanFactoryImpl,并重写了getBean方法,用于检查所请求的Bean是否已经是AOP bean,如果是则返回AOP bean。如果不是AOP bean,则继续通过调用父类的getBean方法获取普通bean。AopBeanFactoryImpl还有registerBean方法,用于将定义的AOP bean注册到AOP Bean Factory的aopBeanDefinitionMap中。

在getAdvisedSupport方法中,它会创建一个AdvisedSupport对象,将目标对象和增强器(Advice)封装到Advisor对象中,并根据Advice类型将它们转化为对应的AopMethodInterceptor对象,然后将所有的AopMethodInterceptor对象添加到AdvisedSupport对象中。最后将目标对象封装到TargetSource对象中,并设置到AdvisedSupport对象中。然后返回AdvisedSupport对象。

当getBean方法检测到所请求的Bean是AOP bean时,它会从aopBeanDefinitionMap中获取Bean的定义,并创建相应的AdvisedSupport对象。然后创建一个CglibAopProxy对象,并使用该对象创建代理对象,最后将代理对象添加到aopBeanMap中。最终返回代理对象。

测试

TestClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class StartTimeBeforeMethod implements BeforeMethodAdvice{
@Override
public void before(Method method, Object[] args, Object target) {
long startTime = System.currentTimeMillis();
System.out.println("开始计时");
ThreadLocalUtils.set(startTime);
}
}

public class ProcessTimeBeforeMethod implements BeforeMethodAdvice {
@Override
public void before(Method method, Object[] args, Object target) {
System.out.println("打印随机数值: "+ UUID.randomUUID());
}
}

public class EndTimeAfterMethod implements AfterRunningAdvice {
@Override
public Object after(Object returnVal, Method method, Object[] args, Object target) {
long endTime = System.currentTimeMillis();
long startTime = ThreadLocalUtils.get();
ThreadLocalUtils.remove();
System.out.println("方法耗时:" + (endTime - startTime) + "ms");
return returnVal;
}
}
1
2
3
4
5
6
public class TestService {
public void testMethod() throws InterruptedException {
System.out.println("this is a test method");
Thread.sleep(1000);
}
}

Json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[
{
"name":"beforeMethod",
"className":"StartTimeBeforeMethod"
},
{
"name":"processMethod",
"className":"ProcessTimeBeforeMethod"
},
{
"name":"afterMethod",
"className":"EndTimeAfterMethod"
},
{
"name":"testService",
"className":"TestService"
},
{
"name":"testServiceProxy",
"className":"core.ProxyFactoryBean",
"target":"testService",
"interceptorNames":[
"beforeMethod",
"processMethod",
"afterMethod"
]
}
]

MainTest

1
2
3
4
5
6
7
8
public class MainTest {
public static void main(String[] args) throws Exception {
AopApplictionContext aopApplictionContext = new AopApplictionContext("application.json");
aopApplictionContext.init();
TestService testService = (TestService) aopApplictionContext.getBean("testServiceProxy");
testService.testMethod();
}
}

截图:

image.png

补充:

Jdk动态代理和Cglib动态代理什么关系

JDK动态代理和Cglib动态代理是两种常用的Java动态代理方式,它们都是用于在运行时动态地创建代理对象,实现对目标对象的增强功能。
JDK动态代理是通过Java反射机制来实现的,它只能为接口创建代理对象,而无法为类创建代理对象。JDK动态代理通过实现目标对象所实现的接口,在运行时生成一个代理类,代理类中包含了目标对象的引用以及拦截器对象的引用,从而可以在调用代理对象的方法时,将方法调用转发给拦截器对象进行处理。
Cglib动态代理则是通过生成目标对象的子类来实现的,它可以为类创建代理对象。Cglib动态代理在运行时通过生成目标对象的子类,重写目标对象的方法,并将目标对象的引用注入到代理对象中。当调用代理对象的方法时,代理对象会先调用目标对象的方法,然后再调用拦截器对象的方法进行增强处理。
虽然JDK动态代理和Cglib动态代理实现方式不同,但它们都可以实现对目标对象的增强功能,常用于实现AOP(面向切面编程)功能。在实际开发中,根据需要选择不同的动态代理方式,如果目标对象实现了接口,则可以使用JDK动态代理;如果目标对象没有实现接口,则可以使用Cglib动态代理。

那是不是Cglib效率更高?为什么还需要JDK动态代理 ?

一般情况下,使用CGLIB生成代理类的效率确实比使用JDK动态代理更高,因为CGLIB是通过在编译时生成字节码来实现代理的,相对于JDK动态代理而言,不需要在运行时动态生成代理类。因此,在需要频繁生成代理类的场景下,使用CGLIB可能更适合。

然而,需要注意的是,CGLIB生成的代理类通常比原始类的大小要大,且CGLIB代理使用了更多的内存,这在创建大量代理对象的场景下可能会成为性能瓶颈。此外,由于CGLIB使用了底层字节码操作库,因此在某些环境下可能存在兼容性问题,需要额外考虑。

因此,在选择代理方式时,需要考虑实际场景和需求,并根据具体情况选择合适的代理方式。如果需要生成大量代理对象,并且不考虑内存占用问题,CGLIB可能更适合;如果需要更轻量级的代理方式,或者需要支持多态性、继承等特性,则JDK动态代理可能更合适。

(AOP)举一个例子去说明在实现AOP功能上JDK和Cglib的区别

假设我们有一个接口UserService和一个实现类UserServiceImpl,其中UserService提供了一个getUserById()方法用于获取用户信息。我们需要对getUserById()方法进行日志记录,以便监控用户信息查询情况。

使用JDK动态代理实现AOP功能:

我们可以使用JDK动态代理来创建UserService接口的代理对象,并将一个实现了InvocationHandler接口的拦截器对象注入到代理对象中。当调用代理对象的getUserById()方法时,代理对象会自动调用拦截器对象的invoke()方法,在该方法中实现日志记录功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public interface UserService {
User getUserById(int userId);
}

public class UserServiceImpl implements UserService {
public User getUserById(int userId) {
// retrieve user information by userId
return user;
}
}

public class UserServiceProxy implements InvocationHandler {
private UserService userService;

public UserServiceProxy(UserService userService) {
this.userService = userService;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("getUserById() method is called with userId: " + args[0]);

Object result = method.invoke(userService, args);

return result;
}
}

public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy(userService);
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
new Class[]{UserService.class}, proxy);

User user = userServiceProxy.getUserById(1);
}
}

使用Cglib动态代理实现AOP功能:

我们可以使用Cglib动态代理来创建UserServiceImpl类的代理对象,并将一个实现了MethodInterceptor接口的拦截器对象注入到代理对象中。当调用代理对象的getUserById()方法时,代理对象会自动调用拦截器对象的intercept()方法,在该方法中实现日志记录功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class UserServiceImpl {
public User getUserById(int userId) {
// retrieve user information by userId
return user;
}
}

public class UserServiceInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("getUserById() method is called with userId: " + args[0]);

Object result = proxy.invokeSuper(obj, args);

return result;
}
}

public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new UserServiceInterceptor());

UserServiceImpl userServiceProxy = (UserServiceImpl) enhancer.create();

User user = userServiceProxy.getUserById(1);
}
}

通过上述示例代码可以看出,使用JDK动态代理时,需要创建一个实现了InvocationHandler接口的拦截器对象,并将其注入到代理对象中;而使用Cglib动态代理时,需要创建一个实现了MethodInterceptor接口的拦截器对象,并将其注入到代理对象中。在JDK动态代理中,代理对象必须实现目标对象所实现的接口;而在Cglib动态代理中,代理对象

(IOC)使用反射、JDK、Cglib去实现依赖注入的区别

只使用反射与使用Cglib和反射来完成依赖注入有什么区别?

在Spring中,依赖注入可以通过只使用反射或使用Cglib和反射来完成。下面是它们之间的一些区别:

  1. 实现方式:只使用反射时,Spring通过反射机制来动态创建对象并设置属性值。而使用Cglib和反射时,Spring通过Cglib在运行时动态创建代理类,并在代理类中使用反射来设置属性值。
  2. 性能:只使用反射时,每次获取Bean时都需要通过反射机制来动态创建对象和设置属性值,因此在性能上可能存在一定的开销。而使用Cglib和反射时,Spring可以在第一次获取Bean时创建代理类,并缓存代理类实例,之后每次获取Bean时都可以直接使用代理类,从而提高了性能。
  3. 功能扩展:只使用反射时,Spring的功能扩展比较有限,因为只能使用Java反射机制来动态创建对象和设置属性值。而使用Cglib和反射时,Spring可以通过Cglib来实现更多的功能扩展,比如实现AOP切面等。

总的来说,只使用反射相对于使用Cglib和反射来完成依赖注入,性能可能会稍差一些,但是在一些场景下可能会更加灵活,因为不需要引入Cglib这样的第三方库。而使用Cglib和反射来完成依赖注入则能够更好地支持一些功能扩展,比如实现AOP切面等。

举一个简单的例子,假设有一个名为UserService的服务类,它依赖于名为UserDao的DAO类。下面是使用只使用反射和使用Cglib和反射来实现依赖注入的代码示例::

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserService {
private UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}

// 在Spring配置文件中进行配置
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.UserDao"/>

在这种情况下,Spring会通过反射来动态创建UserService和UserDao对象,并在运行时调用setUserDao方法来设置依赖关系。

使用Cglib和反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class UserService {
private UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

public void saveUser(User user) {
userDao.save(user);
}
}

// UserDaoInterceptor拦截器类
public class UserDaoInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在方法执行前做一些事情
System.out.println("Before method " + method.getName() + " called");
Object result = proxy.invokeSuper(obj, args);
// 在方法执行后做一些事情
System.out.println("After method " + method.getName() + " called");
return result;
}
}

// 在Spring配置文件中进行配置
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDaoProxy"/>
</bean>
<bean id="userDao" class="com.example.UserDao"/>
<bean id="userDaoProxy" class="org.springframework.cglib.proxy.Enhancer"
factory-method="create">
<constructor-arg value="com.example.UserDao"/>
<property name="interceptor"
ref="userDaoInterceptor"/>
</bean>
<bean id="userDaoInterceptor"
class="com.example.UserDaoInterceptor"/>

在这种情况下,Spring使用Cglib创建一个名为userDaoProxy的代理类,这个代理类继承自UserDao类,并在运行时动态生成。代理类中有一个拦截器UserDaoInterceptor,它会在UserDao类的方法执行前后做一些额外的处理,比如日志记录等。当Spring需要注入UserDao依赖时,会注入userDaoProxy代理类,代理类会使用反射来设置依赖关系。此外,当调用UserService的saveUser方法时,userDaoProxy代理类会通过反射调用UserDao的save方法,并在方法执行前后调用UserDaoInterceptor的相关方法。

只使用反射与使用JDk动态代理和反射来完成依赖注入有什么区别?

使用反射和使用JDK动态代理和反射来完成依赖注入的区别在于生成的代理对象不同。使用反射生成的对象是原始对象的副本,而使用JDK动态代理生成的对象是原始对象的代理对象。
举个例子,假设有一个UserService接口和UserService实现类,实现类需要注入一个UserDao对象。下面分别使用反射和JDK动态代理来完成依赖注入:
使用反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UserServiceImpl implements UserService {
private UserDao userDao;

public UserServiceImpl() {
try {
Class<?> clazz = Class.forName("com.example.UserDaoImpl");
Constructor<?> constructor = clazz.getConstructor();
userDao = (UserDao) constructor.newInstance();
} catch (Exception e) {
// 异常处理
}
}

// ...
}

在上述代码中,我们使用Class.forName方法获取UserDaoImpl类的Class对象,然后使用反射获取构造函数,并创建UserDaoImpl对象。

使用JDK动态代理和反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class UserServiceProxy implements InvocationHandler {
private Object target;

public UserServiceProxy(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().startsWith("set")) {
Class<?> clazz = Class.forName("com.example.UserDaoImpl");
Constructor<?> constructor = clazz.getConstructor();
Object dao = constructor.newInstance();
method.invoke(target, dao);
return null;
}
return method.invoke(target, args);
}
}

public class UserServiceImpl implements UserService {
private UserDao userDao;

public UserServiceImpl() {
UserServiceProxy proxy = new UserServiceProxy(this);
ClassLoader classLoader = getClass().getClassLoader();
userDao = (UserDao) Proxy.newProxyInstance(classLoader, new Class[] { UserDao.class }, proxy);
}

// ...
}

在上述代码中,我们创建了一个UserServiceProxy代理类,实现了InvocationHandler接口,并重写了invoke方法。在invoke方法中,如果是set方法,我们使用反射获取UserDaoImpl类的Class对象和构造函数,创建UserDaoImpl对象,并通过反射调用set方法来完成依赖注入。否则,我们通过反射调用目标对象的方法。

然后,在UserServiceImpl构造方法中,我们创建了UserServiceProxy代理对象,并使用Proxy.newProxyInstance方法创建了一个UserDao的代理对象。这个代理对象会拦截所有UserDao的方法调用,并通过InvocationHandler的invoke方法进行处理。

需要注意的是,由于代理对象实现了UserDao接口,因此在使用代理对象时,需要将其转换为UserDao类型。

总的来说,使用反射和使用JDK动态代理和反射来完成依赖注入的区别在于生成的代理对象不同。使用反射生成的对象是原始对象的副本,而使用JDK动态代理生成的对象是原始对象的代理对象,可以对其方法进行拦截和处理。

对比

Cglib

Cglib是通过字节码生成技术来实现动态代理的,它会在运行时动态生成被代理类的子类,并在子类中覆盖被代理类的方法,从而实现方法拦截和增强。

在Spring中,当使用Cglib代理时,Spring会在运行时动态生成被代理类的子类,并在子类中实现依赖注入。Cglib会通过反射机制获取被代理类的构造函数和参数,然后通过Constructor.newInstance方法创建被代理类的实例。

具体来说,Spring会在运行时动态生成一个类,继承被代理类,并重写其中需要拦截和增强的方法。同时,它还会在生成的类中添加一个无参构造函数,并在其中通过反射机制实例化被代理类,并完成依赖注入。在这个过程中,Cglib会使用asm字节码操作库来生成字节码,完成类的定义和加载。

因此,使用Cglib代理时,它会通过字节码生成技术来实例化Bean,并通过反射机制完成依赖注入。这种方式相对于使用JDK动态代理,不需要被代理类实现接口,可以拦截和增强非public方法,但是会比JDK动态代理更消耗内存和CPU资源。

JDK动态代理

JDK动态代理是通过反射机制来实现动态代理的,它会在运行时动态生成一个实现了被代理接口的代理类,并在代理类中实现方法拦截和增强。

在Spring中,当使用JDK动态代理时,Spring会使用Java提供的Proxy类来生成代理类,并在代理类中实现依赖注入。具体来说,Spring会使用Proxy类的newProxyInstance方法来生成代理类,该方法需要传入一个ClassLoader对象、一组被代理的接口、和一个InvocationHandler对象。在生成代理类的过程中,JDK动态代理会通过反射机制获取被代理接口的方法,然后通过InvocationHandler对象的invoke方法来进行方法的拦截和增强。

因此,使用JDK动态代理时,它会通过反射机制来实例化Bean,并通过反射机制完成依赖注入。这种方式相对于使用Cglib代理,需要被代理类实现接口,不能拦截和增强非public方法,但是会比Cglib代理更节省内存和CPU资源。

Cglib相对于JDK动态代理

Cglib相对于JDK动态代理来说,有以下两个方面的优势,导致它在一些情况下可能比JDK动态代理更快:

  1. Cglib代理不需要被代理类实现接口,可以拦截和增强非public方法,而JDK动态代理只能代理实现了接口的类。因此,在需要拦截非public方法的情况下,Cglib代理可能比JDK动态代理更适合。

  2. 在拦截方法调用的时候,Cglib代理不需要调用被代理类的方法,而JDK动态代理需要通过反射调用被代理类的方法。因此,在需要进行大量方法调用的情况下,Cglib代理可能比JDK动态代理更快。

总的来说,Cglib代理和JDK动态代理各有优缺点,在具体的应用场景中需要根据实际情况选择合适的代理方式。

运行过程

Cglib代理

Cglib代理是基于继承来实现代理,它会在运行时动态生成目标对象的子类,并在子类中重写目标对象的非final方法,从而实现对目标对象的代理。这个子类会被当作代理类,被加载到JVM中,并用来完成方法调用。

具体来说,Cglib会使用ASM等字节码操作库生成代理类的字节码,然后通过ClassLoader将其加载到JVM中。生成的代理类会继承目标对象的类,并重写目标对象的非final方法。在生成代理类后,Cglib会创建代理对象,这个代理对象实际上就是生成的代理类的实例。当代理对象调用目标对象的方法时,实际上是调用了生成的代理类中重写的方法,从而实现了代理的功能。

因此,Cglib生成的子类会替代原始的目标对象,成为代理对象的实现。这个子类重写了目标对象的方法,从而实现了对目标对象方法的增强和拦截。

JDK动态代理

JDK动态代理是基于接口来实现代理的,它会在运行时动态生成一个代理接口的代理类,并将这个代理类实例化成代理对象。这个代理类会实现目标对象实现的接口,并且会拦截所有接口中定义的方法调用。

具体来说,JDK动态代理会在运行时使用Java反射机制动态生成代理类的字节码,并将其加载到JVM中。生成的代理类会实现目标对象实现的接口,并在代理类中实现对目标对象方法的拦截和增强。当代理对象调用目标对象的方法时,实际上是通过反射机制调用了代理类中的方法,从而实现了代理的功能。

因此,JDK动态代理实现了对接口的代理,可以通过实现一个代理接口来实现代理功能,而且可以在运行时动态生成代理类,并将其加载到JVM中,实现了对目标对象的动态代理。同时,由于是基于接口来实现代理的,所以在类型上更加灵活,但也因为这个原因,只能代理实现了接口的类。