1 JDK动态代理详解

  静态代理、JDK动态代理、Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文

  1.1 JDK代理的基本步骤

    》通过实现InvocationHandler接口来自定义自己的InvocationHandler;

    》通过Proxy.getProxyClass获得动态代理类
    》通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
    》通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
    》通过代理对象调用目标方法

  1.2 JDK动态代理的应用场景

    重要扫盲知识点:利用JDK动态代理获取到的动态代理实例的类型默认是Object类型,如果需要进行类型转化必须转化成目标类的接口类型,因为JDK动态代理是利用目标类的接口实现的

    前提准备:创建一个SpringBoot项目并引入相关依赖

      技巧01:我引入的web依赖时响应式的,可以不引入响应式的web依赖,引入springboot的常规web依赖就可了  
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.xiangxu.com</groupId>
<artifactId>webclient_rest_client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>webclient_rest_client</name>
<description>Demo project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

pom.xml

    1.2.1 在原有业务执行前后执行一些逻辑操作

      》需求:已知一个实例student,该实例有一个studentInfo方法;现在需要在通过student实例调用studentInfo方法的前后插入一些代码

      》思路:创建一个实现了 InvocationHandler 接口的代理类处理类 MyInvocationHandler -> 在 MyInvocationHandler  中创建一个 createProxyInstance 方法用来创建动态代理类的实例,需要一个接收目标类实例的成员变量target -> 在重写的invoke方法中 通过成员变量target去执行studentInfo方法并在执行前后增加其他的业务逻辑

        技巧01:MyInvocationHandler  中需要有一个成员变量用来接收目标类实例,因为在MyInvocationHandler  中的invoke方法中需要用到目标类的实例来执行原来的逻辑

        技巧02:createProxyInstance 返回动态代理类的实例

      》按照实录编写的源代码

/**
* 学生接口
*/
interface IStudent { Object studentInfo(); }

IStudent.java

/**
* 学生接口实现类
*/
@Slf4j
class Student implements IStudent { @Override
public Object studentInfo() {
log.info("学生接口实现类");
return "学生接口实现类";
} }

Student.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 15:30
* @desc 动态代理类的处理类
**/
@Slf4j
public class MyInvocationHandler implements InvocationHandler { private Object target; /**
* 创建代理类实例
* @param target
* @return
*/
public Object createProxyInstance (Object target) {
this.target = target; // 接收目标类实例
// 创建并返回代理类实例
return Proxy.newProxyInstance(
this.target.getClass().getClassLoader(),
this.target.getClass().getInterfaces(),
this
);
} /**
* 代理执行方法:利用代理类实例执行目标类的方法都会进入到invoke方法中执行
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("前置逻辑");
Object result = method.invoke(target, args); // 利用目标类实例执行原有的逻辑
log.info("后置逻辑");
return result;
}
}

MyInvocationHandler.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) { Student student = new Student(); IStudent proxyInstance = (IStudent) new MyInvocationHandler()
.createProxyInstance(student); Object result = proxyInstance.studentInfo();
System.out.println(result); } } /**
* 学生接口
*/
interface IStudent { Object studentInfo(); } /**
* 学生接口实现类
*/
@Slf4j
class Student implements IStudent { @Override
public Object studentInfo() {
log.info("学生接口实现类");
return "学生接口实现类";
} }

源代码

    1.2.2 替换原有的所有业务

      》需求:已知一个实例student,该实例有一个studentInfo方法;现在需要在通过student实例调用studentInfo方法时不执行原有的逻辑,而是执行一些其他的逻辑,而且需要返回studentInfo方法指定的数据类型

      》思路:创建一个实现了 InvocationHandler 接口的代理类处理类 MyInvocationHandler -> 在 MyInvocationHandler  中创建一个 createProxyInstance 方法用来创建动态代理类的实例 -> 在重写的invoke方法中不需要执行studentInfo方法,而是直接执行其他的一些逻辑

      》按照思路进行编写的源代码

/**
* 学生接口
*/
interface IStudent { Object studentInfo(); }

IStudent.java

/**
* 学生接口实现类
*/
@Slf4j
class Student implements IStudent { @Override
public Object studentInfo() {
log.info("学生接口实现类");
return "学生接口实现类";
} }

Student.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* @author 王杨帅
* @create 2018-08-23 15:30
* @desc 动态代理类的处理类
**/
@Slf4j
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("不执行原来的逻辑,执行一些其他的逻辑");
return "利用动态代理对象执行后的返回结果";
}
}

MyInvocationHandler.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) { Student student = new Student(); IStudent proxyInstance = (IStudent) Proxy.newProxyInstance(
student.getClass().getClassLoader(),
student.getClass().getInterfaces(),
new MyInvocationHandler()
); Object result = proxyInstance.studentInfo();
System.out.println(result); } } /**
* 学生接口
*/
interface IStudent { Object studentInfo(); } /**
* 学生接口实现类
*/
@Slf4j
class Student implements IStudent { @Override
public Object studentInfo() {
log.info("学生接口实现类");
return "学生接口实现类";
} }

源代码

      》简便写法

        技巧01:由于不需要执行原来的逻辑,所以在 MyInvocationHandler 类中的 invoke方法就不需要目标类的实例,所以就不需要单独创建一个实现了 InvocationHandler 接口的实现类,直接通过匿名内部类实现就可以啦。

/**
* 学生接口
*/
interface IStudent { Object studentInfo(); }

IStudent.java

/**
* 学生接口实现类
*/
@Slf4j
class Student implements IStudent { @Override
public Object studentInfo() {
log.info("学生接口实现类");
return "学生接口实现类";
} }

Student.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) { Student student = new Student(); IStudent proxyInstance = (IStudent) Proxy.newProxyInstance(
student.getClass().getClassLoader(),
student.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("根据方法信息调用一些逻辑");
return "代理类调用返回结果";
}
}
); Object result = proxyInstance.studentInfo();
System.out.println(result); } } /**
* 学生接口
*/
interface IStudent { Object studentInfo(); } /**
* 学生接口实现类
*/
@Slf4j
class Student implements IStudent { @Override
public Object studentInfo() {
log.info("学生接口实现类");
return "学生接口实现类";
} }

源代码

    1.2.3 为某些接口创建代理类并将代理类注册到IOC容器中  

      》说明:需要创建代理类的接口都标注有@RestApi注解;创建的代理类需要执行额外的逻辑,比如:远程Restrul接口调用(不用实现原有逻辑,因为接口中的方法是有声明,默认方法除外)、将创建的Bean注入到IOC容器中【PS: 注入到IOC的Bean必须支持AOP】

      》所需知识点:扫描指定目录下的类类型、获取指定类型的类类型、如何实现远程调用Restful接口、动态注入Bean

      》注意:本知识点所需知识点比较多,请先阅读下面的所需知识点

2 自定义JDK动态代理工具类

  前面的知识点都是利用了Proxy类去直接创建动态代理类的实例,本小节将按照JDK动态代理的5大步骤去自定义一个JDK动态代理工具类

  2.1 功能说明

    该工具类可以根据 接口数组和代理类处理器 创建出 代理类的类类型和代理类实例

    技巧01:利用 java.lang.reflect.Proxy的getProxyClass方法可以创建代理类的类类型,该方法接收一个类加载器和一个接口数组

    技巧02:利用java.lang.Class的getConstructor方法可以获取到代理类的构造方法,该方法接收一个Class类型的数组;由于这里是针对动态代理类而言,所以动态代理类的构造器的参数都是一些处理器,所以在这里只需要传入InvocationHandler实现类的实例就可以啦;由于getConstructor方法的定义使用了解构,所以可以不传参数或者之传入一个参数,本案例传入的是InovcationHandler的类类型

    技巧03:获取到代理类的Constructor后就可以通过newInstance方法创建代理类的实例了

  2.2 设计思路

    2.2.1 创建一个实现了 InvocationHandler 接口的代理类处理器 MyInvocationHandler

      技巧01:MyInvocationHandler 中的invoke方法不执行原来的业务逻辑,只执行新的业务逻辑

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 15:30
* @desc 动态代理类的处理类
**/
@Slf4j
public class MyInvocationHandler implements InvocationHandler { /**
* 代理执行方法:利用代理类实例执行目标类的方法都会进入到invoke方法中执行
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("前置逻辑");
log.info("可以根据method参数和args参数获取一些信息,再根据这些信息去执行新的业务逻辑");
log.info("后置逻辑");
return "动态代理返回的结果";
}
}

MyInvocationHandler.java

    2.2.2 创建一个代理类创建工具接口ProxyCreator,该接口中定义了两个方法:

      createProxyInstanceInfo -> 创建代理类实例

      getProxyClassInfo -> 获取代理类的类类型

      技巧01:之所以需要创建一个接口的原因是为了以后可以扩展为基于Cglib实现动态代理【PS: 本案例基于JDK动态代理】

package cn.xiangxu.com.webclient_rest_client.proxy;

import java.lang.reflect.InvocationTargetException;

/**
* @author 王杨帅
* @create 2018-08-23 16:35
* @desc 代理工具接口
**/
public interface ProxyCreator {
/**
* 获取代理类实例
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
Object createProxyInstanceInfo() throws IllegalAccessException, InvocationTargetException, InstantiationException; /**
* 获取代理类的类类型
* @return
*/
Class<?> getProxyClassInfo();
}

ProxyCreator.java

    2.2.3 创建JDK动态代理工具类JdkProxyCreator

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 16:37
* @desc JDK代理工具类
**/
@Data
public class JdkProxyCreator implements ProxyCreator { /**
* 需要产生代理类的接口数组
*/
private Class<?>[] interfaces; /**
* 根据interfaces创建的动态代理类的类类型
*/
private Class<?> proxyClass; /**
* 根据interfaces创建的动态代理类的构造器
*/
private Constructor<?> proxyConstructor; /**
* 根据interfaces创建的动态代理类所需的处理器
*/
private InvocationHandler invocationHandler; /**
* 自定义构造器:
* 根据获取到处理器实例创建JDK代理类的类类型
* 并通过JDK代理类的类类型获取代理类的构造器
* @param interfaces 接口数组
* @param invocationHandler 代理类的处理器
* @throws NoSuchMethodException
*/
public JdkProxyCreator(
Class<?>[] interfaces,
InvocationHandler invocationHandler
) throws NoSuchMethodException {
this.interfaces = interfaces;
this.invocationHandler = invocationHandler; // 创建代理类的类类型
this.proxyClass = Proxy.getProxyClass(
this.getClass().getClassLoader(),
this.interfaces
); // 根据代理类的类类型获取代理类的构造器
this.proxyConstructor = this.proxyClass
.getConstructor(InvocationHandler.class); } @Override
public Object createProxyInstanceInfo() throws IllegalAccessException, InvocationTargetException, InstantiationException {
return this.proxyConstructor.newInstance(this.invocationHandler);
} @Override
public Class<?> getProxyClassInfo() {
return this.proxyClass;
}
}

JdkProxyCreator.java

      技巧01:JdkProxyCreator 中定义了几个成员变量

      》JdkProxyCreator中创建一个有参构造器

        技巧01:该构造器接收的参数为

          interfaces -> 需要产生代理类的接口数组

          invocationHandler -> 代理类的处理器

        技巧02:通过接收到的参数创建动态代理的类类型并对成员变量proxyClass完成初始化

          技巧0201:利用java.lang.reflect.Proxy的getProxyClass方法可以根据类加载器和接口数组创建代理对象的类类型

        技巧03:根据已经初始化的proxyClass获取代理类的构造器并对成员变量proxyConstructor完成初始化

      》JdkProxyCreator中创建代理类实例

        在createProxyInstanceInfo中利用已经初始化的proxyConstructor创建代理类实例

        技巧01:Constuctor实例可以利用 newInstance 方法创建实例

      》JdkProxyCreator中返回代理类的类类型

        在 getProxyClassInfo 中直接返回已经初始化的 proxyClass 成员变量即可

  2.3 测试

    2.3.1 需求

      现有一个接口IStudent,该接口中有一个方法,但是这个接口没有实现类;现在要求给这个接口创建一个实现类的实例,并用这个实例去调用studentInfo

      技巧01:由于是给接口创建代理类,所以所有的业务逻辑都是在代理类的处理器的invoke方法中进行定义的

    2.3.2 思路

      》利用创建好的MyInvocationHandler传进一个动态代理处理类实例invocationHandler

      利用封装好的JdkProxyCreator创建一个JDK动态代理创建工具实例jdkProxyCreator

      》利用jdkProxyCreator的createProxyInstanceInfo方法创建动态代理类实例proxyInstance

      》再利用动态代理类实例proxyInstance去调用studentInfo方法

    2.3.3 代码汇总

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { // 01 创建代理类的处理器实例
MyInvocationHandler invocationHandler = new MyInvocationHandler(); // 02 实例化JDK动态代理创建工具
JdkProxyCreator jdkProxyCreator = new JdkProxyCreator(new Class<?>[]{IStudent.class}, invocationHandler); // 03 利用JDK动态代理创建工具创建代理类实例
IStudent proxyInstance = (IStudent) jdkProxyCreator.createProxyInstanceInfo(); // 04 利用代理类实例调用接口方法
Object o = proxyInstance.studentInfo(); // 05 打印代理类的处理器类中invoke方法返回的数据
System.out.println(o); } } /**
* 学生接口
*/
interface IStudent { Object studentInfo(); }

测试代码汇总

3 获取类类型

  3.1 需求

    获取一个指定包下所有类的类类型

  3.2 思路

    利用反射实现,org.reflections依赖可以实现扫描指定包下所有类的类类型

  3.3 reflections使用

    3.3.1 引入reflections依赖

<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>

    3.3.2 reflections可以实现的功能

      详情参见reflections官网

      》获取某个类的所有子类的类类型

      》获取某个接口的所有实现类的类类型

      》获取标注有某个注解的类的类类型  

      ......

    3.3.3 使用案例

      》获取IStudent所有实现类的类类型

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) { // 01 获取指定包下的所有类类型
Reflections reflections = new Reflections(
"cn.xiangxu.com.webclient_rest_client.proxy"
); // 02 获取子类的类类型
Set<Class<? extends IStudent>> subTypesOf = reflections
.getSubTypesOf(IStudent.class); // 03 遍历输出
for (Class clss : subTypesOf) {
System.out.println(clss.getSimpleName());
} } } /**
* 学生接口
*/
interface IStudent { Object studentInfo(); } /**
* IStudent实现类01
*/
class Student01 implements IStudent { @Override
public Object studentInfo() {
return null;
}
} /**
* IStudent实现类02
*/
class Student02 implements IStudent { @Override
public Object studentInfo() {
return null;
} }

测试代码汇总

      》获取标注了@RestApi注解的类的类类型

package cn.xiangxu.com.webclient_rest_client.proxy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestApi {
String value() default "";
}

RestApi.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) { // 01 获取指定包下的所有类类型
Reflections reflections = new Reflections(
"cn.xiangxu.com.webclient_rest_client.proxy"
); // 02 获取标注了@RestApi的类的类类型
Set<Class<?>> typesAnnotatedWith = reflections
.getTypesAnnotatedWith(RestApi.class); // 03 遍历输出
for (Class clss : typesAnnotatedWith) {
System.out.println(clss.getSimpleName());
} } } /**
* 学生接口
*/
interface IStudent { Object studentInfo(); } /**
* IStudent实现类01
*/
@RestApi(value = "http://127.0.0.1:8080/dev")
class Student22 implements IStudent { @Override
public Object studentInfo() {
return null;
}
} /**
* IStudent实现类02
*/
@RestApi(value = "http://127.0.0.1:8080/stu")
class Student implements IStudent { @Override
public Object studentInfo() {
return null;
} }

测试类代码

4 为添加了指定注解的接口创建代理对象

  4.1 需求

    为那些标注有@RestApi注解的所有接口创建代理类实例

  4.2 思路

    》扫描指定包获取所有的类类型

    》筛选出标注了@RestApi注解的类类型

    》创建自定义的JDK动态代理所需的处理器实例

    》创建自定义的JDK动态代理工具类实例

    》利用JDK动态代理工具类实例创建动态代理类实例

    》利用JDK动态代理类实例调用目标接口的方法

  4.3 代码实现

    说明:自定义的处理器类和JDK动态代理工具类请参见第二小节内容(2.2)

package cn.xiangxu.com.webclient_rest_client.proxy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestApi {
String value() default "";
}

RestApi.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.util.CollectionUtils; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set; /**
* @author 王杨帅
* @create 2018-08-23 14:47
* @desc
**/
@Slf4j
public class SimpleJdkProxy { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { // 01 获取指定包下的所有类类型
Reflections reflections = new Reflections("cn.xiangxu.com.webclient_rest_client.proxy"); // 02 获取标注了@RestApi注解的类的类型
Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(RestApi.class);
List<Class> classList = new ArrayList<>(typesAnnotatedWith);
// 获取其中一个类类型
Class aClass = classList.get(0); // 03 创建JDK动态代理类所需的处理类实例
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); // 04 创建JDK动态代理工具类实例
JdkProxyCreator jdkProxyCreator = new JdkProxyCreator(new Class[]{aClass}, myInvocationHandler); // 05 创建动态代理类实例
IStudent proxyInstanceInfo = (IStudent) jdkProxyCreator.createProxyInstanceInfo(); // 06 利用代理类实例调用目标接口方法
proxyInstanceInfo.studentInfo(); } } /**
* 学生接口
*/
@RestApi(value = "http://127.0.0.1:8080/dev")
interface IStudent { Object studentInfo(); }

测试类代码

5 动态注册Bean

  5.1 动态注册Bean应用场景

    在spring的XML配置文件中通过<bean>标签注册的Bean、利用@Bean、@Controller、@Component注册的Bean都是静态的方式注册Bean,在大部分的开发中通过这些静态配置的方式注册Bean就可以啦;但是在某些场合下,需要根据一些静态信息动态向IOC容器中动态注册Bean,此时之前的方法就会失效了。

    5.1.1 需求

      例如:下面有一个IStudent接口,该接口中有若干个方法,但是该接口没有实现类;现在要求根据接口信息动态为该接口创建一个实现类实例并注册到bean中,在要用的地方依赖注入已经注册的Bean,然后调用相应的方法就可以实现远程服务资源的调用。

    5.1.2 思路

      利用动态代理为标注了@RestApi的接口创建代理类并将其动态注册到IOC容器中就可以啦

  5.2 动态注册Bean知识点扫盲

    5.2.1 Spring中可以进行动态注册Bean的API

      BeanDefinitionRegistry:提供了向容器手工注册BeanDefinition对象的方法,在Spring的XML配置文件中每一个<bean>标签都对应一个BeanDefinition对象,最后这些BeanDefinition对象都会通过BeanDefinitionRegistry被注册到IOC容器中。

      SingletonBeanRegistry:提供了向容器中注册单例Bean的方法。

      DefaultListableBeanFactory:同时实现了这两个接口,在实践中通常会使用这个接口。

    5.2.2 不同地方动态注册Bean的区别

      可以在任何可以获得BeanDefinitionRegistry、BeanDefinitionRegistry、BeanDefinitionRegistry中的一个实例的地方进行动态注册Bean,但是,如果Bean不是在BeanFactoryPostProcessor中被注册的话就不会被BeanPostProcessor处理,即:无法对其应用aop、Bean Validation等功能。

      技巧01:在Spring容器的启动过程中,BeanFactory载入bean的定义后会立刻执行BeanFactoryPostProcessor,此时动态注册bean,则可以保证动态注册的bean被BeanPostProcessor处理,并且可以保证其的实例化和初始化总是先于依赖它的bean。

      技巧02:在BeanFactoryPostProcessor中注册Bean指的是在实现了BeanFactoryPostProcessor接口后,在实现类的重写方法中进行Bean的注册,例如

      技巧03:实现了BeanFactoryPostProcessor接口的实现类上标注@Component注解可以保证容器启动时就进行动态Bean的创建,因为@Component注解会在Spring容器启动时把对应的类创建一个Bean注册到Spring容器,所以在标注了@Component注解并且实现了BeanFactoryPostProcessor接口的类中动态注册Bean是最好的方式。

  5.3 动态创建Bean工具类编写

    5.3.1 思路

      创建工具类DynamicRegisterBeanUtil并实现BeanFactoryPostProcessor -> 重写postProcessBeanFactory 方法 -> 在postProcessBeanFactory方法中实现动态Bean的注册 -> 利用@Component注解实现DynamicRegisterBeanUtil被IOC容器管理

    5.3.2 在postProcessBeanFactory方法中动态创建Bean详解

      说明:postProcessBeanFactory是 BeanFactoryPostProcessor 接口中定义的,所以在DynamicRegisterBeanUtil必须实现postProcessBeanFactory方法

      》postProcessBeanFactory 有一个 ConfigurableListableBeanFactory 类型的参数,可以利用这个参数来实现动态注册Bean

        技巧01:由于进行动态注册Bean的API只有BeanDefinitionRegistry、SingletonBeanRegistry、DefaultListableBeanFactory,所以我们先将ConfigurableListableBeanFactory 的参数转化成DefaultListableBeanFactory类型的参数并赋值给DynamicRegisterBeanUtil的成员变量defaultListableBeanFactory。

      》获取所有标注了@RestApi注解的类类型

        技巧01:需要用到org.reflections相关的东西,所以必须先引入reflections的相关依赖

        技巧02:获取标注了@RestApi注解的类类型分为两个步骤,首先需要扫描一个指定的路径得到该路径下所有的类类型,然后从获取到的所有类类型中筛选出标注了@RestApi注解的类类型

      》将获取到的标注了@RestApi注解的类类型注册到IOC容器中

        技巧01:创建一个自定义的动态代理所需的处理器实例 handler

        技巧02:根据类类型和处理器 handler 去创建类类型对应的动态代理类的Bean定义 proxyBeanDefinition

        技巧03:利用defaultListableBeanFactory根据类类型和该类类型的的动态代理类的Bean定义进行Bean的动态注册

    5.3.3 工具类源代码

      说明:需要用到上面提到的处理器类和JDK动态代理工具类

      案例:为添加了指定注解的接口创建代理对象并注入到IOC容器中

      》自定义处理器类

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 15:30
* @desc 动态代理类的处理类
**/
@Slf4j
public class MyInvocationHandler implements InvocationHandler { /**
* 代理执行方法:利用代理类实例执行目标类的方法都会进入到invoke方法中执行
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("前置逻辑");
System.out.println(method.getName());
log.info("可以根据method参数和args参数获取一些信息,再根据这些信息去执行新的业务逻辑");
log.info("后置逻辑");
return Flux.just("动态代理返回的结果");
}
}

MyInvocationHandler.java

      》自定义代理类接口以及JDK代理实现类

package cn.xiangxu.com.webclient_rest_client.proxy;

import java.lang.reflect.InvocationTargetException;

/**
* @author 王杨帅
* @create 2018-08-23 16:35
* @desc 代理工具接口
**/
public interface ProxyCreator {
/**
* 获取代理类实例
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
Object createProxyInstanceInfo() throws IllegalAccessException, InvocationTargetException, InstantiationException; /**
* 获取代理类的类类型
* @return
*/
Class<?> getProxyClassInfo();
}

ProxyCreator.java

package cn.xiangxu.com.webclient_rest_client.proxy;

import lombok.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-23 16:37
* @desc JDK代理工具类
**/
@Getter
public class JdkProxyCreator implements ProxyCreator { /**
* 需要产生代理类的接口数组
*/
private Class<?>[] interfaces; /**
* 根据interfaces创建的动态代理类的类类型
*/
private Class<?> proxyClass; /**
* 根据interfaces创建的动态代理类的构造器
*/
private Constructor<?> proxyConstructor; /**
* 根据interfaces创建的动态代理类所需的处理器
*/
private InvocationHandler invocationHandler; /**
* 自定义构造器:
* 根据获取到处理器实例创建JDK代理类的类类型
* 并通过JDK代理类的类类型获取代理类的构造器
* @param interfaces 接口数组
* @param invocationHandler 代理类的处理器
* @throws NoSuchMethodException
*/
public JdkProxyCreator(
Class<?>[] interfaces,
InvocationHandler invocationHandler
) throws NoSuchMethodException {
this.interfaces = interfaces;
this.invocationHandler = invocationHandler; // 创建代理类的类类型
this.proxyClass = Proxy.getProxyClass(
this.getClass().getClassLoader(),
this.interfaces
); // 根据代理类的类类型获取代理类的构造器
this.proxyConstructor = this.proxyClass
.getConstructor(InvocationHandler.class); } @Override
public Object createProxyInstanceInfo() throws IllegalAccessException, InvocationTargetException, InstantiationException {
return this.proxyConstructor.newInstance(this.invocationHandler);
} @Override
public Class<?> getProxyClassInfo() {
return this.proxyClass;
}
}

JdkProxyCreator.java

      》自定义@RestApi注解

package cn.xiangxu.com.webclient_rest_client.proxy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestApi {
String value() default "";
}

RestApi.java

      》自定义标注了@RestApi注解的接口

package cn.xiangxu.com.webclient_rest_client.rest_server_api;

import cn.xiangxu.com.webclient_rest_client.proxy.RestApi;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; /**
* @author 王杨帅
* @create 2018-08-23 22:18
* @desc
**/
@RestApi(value = "http://127.0.0.1:8080/stu")
public interface IStudentApi { @GetMapping(value = "/")
Flux<Object> listStudent(); @GetMapping(value = "/{id}")
Mono<Object> getStudent(
@PathVariable(value = "id") String studentId
); }

IStudentApi.java

      》自定义动态注册Bean工具类

package cn.xiangxu.com.webclient_rest_client.proxy;

import org.reflections.Reflections;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component; import java.util.Set; /**
* @author 王杨帅
* @create 2018-08-23 22:50
* @desc 动态注册Bean的工具类
**/
@Component
public class DynamicRegisterBeanUtil implements BeanFactoryPostProcessor { /**
* 动态注册Bean所需对象
*/
private DefaultListableBeanFactory defaultListableBeanFactory; @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { this.defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory; // 01 获取指定路径下的所有类类型
Reflections reflections = new Reflections("cn.xiangxu.com.webclient_rest_client.rest_server_api"); // 02 获取标注了@Rest注解的类类型
Set<Class<?>> typesAnnotatedWithRestApi = reflections.getTypesAnnotatedWith(RestApi.class); // 03 为标注了@RestApi 注解的所有类类型创建代理类并将该代理类注册到IOC容器中去
for (Class cls : typesAnnotatedWithRestApi) {
System.out.println(cls.getSimpleName());
// 创建代理类的BeanDefinition并将其注册到IOC容器中去
try {
createProxyClass(cls);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
} } /**
* 创建代理类并将代理类注册到IOC容器中去
* @param cls 需要创建代理类的的类类型
* @throws NoSuchMethodException
*/
private void createProxyClass(Class cls) throws NoSuchMethodException {
// 一、创建代理类对象
MyInvocationHandler handler = getMyInvocationHandler(); // 二、获取代理类的Bean定义
BeanDefinition proxyBeanDefinition = getProxyBeanDefinition(cls, handler); // 三、将代理类的Bean信息注册到Spring容器中
registerBeanDefinition(cls, proxyBeanDefinition);
} /**
* 将代理类的Bean信息注册到Spring容器中:Bean名称为接口名
* @param cls
* @param proxyBeanDefinition
*/
private void registerBeanDefinition(Class cls, BeanDefinition proxyBeanDefinition) {
this.defaultListableBeanFactory.registerBeanDefinition(cls.getSimpleName(), proxyBeanDefinition);
} /**
* 获取JDK代理类的Bean定义:根据目标接口的类型和处理器实例获取目标类接口的JDK代理类的Bean定义
* @param cls
* @param handler
* @return
* @throws NoSuchMethodException
*/
private BeanDefinition getProxyBeanDefinition(Class cls, MyInvocationHandler handler) throws NoSuchMethodException {
// 获取JDK代理类的类型
Class<?> jdkDynamicProxyClass = getJDKDynamicProxyClass(cls, handler); // 获取JDK代理类的Bean定义
BeanDefinition jdkBeanDefinition = getJDKBeanDefinition(jdkDynamicProxyClass, handler); return jdkBeanDefinition;
} /**
* 获取JDK代理类的Bean定义:根据JDK代理类的类型和处理类实例创建JDK代理类的Bean定义
* @param jdkDynamicProxyClass
* @param handler
*/
private BeanDefinition getJDKBeanDefinition(Class<?> jdkDynamicProxyClass, MyInvocationHandler handler) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(jdkDynamicProxyClass)
.addConstructorArgValue(handler)
.getBeanDefinition();
beanDefinition.setAutowireCandidate(true);
return beanDefinition;
} /**
* 获取JDK代理类的类类型
* @param cls
* @param handler
*/
private Class<?> getJDKDynamicProxyClass(Class cls, MyInvocationHandler handler) throws NoSuchMethodException {
JdkProxyCreator jdkProxyCreator = new JdkProxyCreator(new Class[]{cls}, handler);
return jdkProxyCreator.getProxyClassInfo();
} /**
* 获取创建代理时所需的处理类
* @return
*/
private MyInvocationHandler getMyInvocationHandler() {
return new MyInvocationHandler();
}
}

DynamicRegisterBeanUtil.java

      》测试动态注册Bean是否生效

        依赖注入IStudentApi,如果可以注入成功就表示注册成功啦

package cn.xiangxu.com.webclient_rest_client.controller;

import cn.xiangxu.com.webclient_rest_client.WebclientRestClientApplicationTests;
import cn.xiangxu.com.webclient_rest_client.rest_server_api.IStudentApi;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux; import static org.junit.Assert.*; @Component
public class TestControllerTest extends WebclientRestClientApplicationTests { @Autowired
private IStudentApi iStudentApi; @Test
public void test01() {
Flux<Object> objectFlux = iStudentApi.listStudent();
System.out.println("Hello");
objectFlux.toStream().forEach(System.out::println); } }

测试代码

7 基于WebClient的响应式接口调用框架

  具体知识点待更新【其实就是上面知识点的整合使用】

  服务端和客户端源代码

      

    

        

SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架的更多相关文章

  1. JDK动态代理、CGLIB动态代理详解

    Spring的AOP其就是通过动态代理的机制实现的,所以理解动态代理尤其重要. 动态代理比静态代理的好处: 1.一个动态代理类可以实现多个业务接口.静态代理的一个代理类只能对一个业务接口的实现类进行包 ...

  2. JDK、CGlib动态代理详解

    Java动态代理之JDK实现和CGlib实现(简单易懂)      一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...

  3. JDK动态代理详解

    JDK动态代理是代理模式的一种,且只能代理接口.spring也有动态代理,称为CGLib,现在主要来看一下JDK动态代理是如何实现的? 一.介绍 JDK动态代理是有JDK提供的工具类Proxy实现的, ...

  4. 基于SpringBoot实现AOP+jdk/CGlib动态代理详解

    动态代理是一种设计模式.在Spring中,有俩种方式可以实现动态代理--JDK动态代理和CGLIB动态代理. JDK动态代理 首先定义一个人的接口: public interface Person { ...

  5. 【spring基础】AOP概念与动态代理详解

    一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  6. JAVA动态代理详解

    1.什么是代理 代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 2.什么是动态代理 在程 ...

  7. Java技术整理1---反射机制及动态代理详解

    1.反射是指在程序运行过程中动态获取类的相关信息,包括类是通过哪个加载器进行加载,类的方法和成员变量.构造方法等. 如下示例可以通过三种方法根据类的实例来获取该类的相关信息 public static ...

  8. Java 动态代理详解

    package com.at221; //代理设计模式: interface ClothFactory{ void product(); } class NikeFactory implements ...

  9. Spring_AOP动态代理详解(转)

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

随机推荐

  1. [QT][问题记录]发布软件时遇到的问题

    2017-3-9 11:22:13 无法定位程序输入点 _ZdaPvj 于动态链接库 libstdc++-6.dll 上. 这是在自己机器上出现的问题. 使用cmd 进行命令 windeployqt  ...

  2. iOS开发单例模式 dispatch_once

    什么是单例 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源 ...

  3. ActionContext介绍(在Struts2中)

    一种属性的有序序列,它们为驻留在环境内的对象定义环境.在对象的激活过程中创建上下文,对象被配置为要求某些自动服务,如同步.事务.实时激活.安全性等等.多个对象可以存留在一个上下文内.也有根据上下文理解 ...

  4. 剑指offer-第四章解决面试题的思路(包含min函数的栈)

    题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数,在该栈中,调用min,push及pop的时间复杂度都是O(1) 思路:定义两个栈分别为dataStack和minStack ...

  5. ecmall分页

    在Ecmall的二次开发中,分页是必不可少的.这个系统已经自带了分页功能,下面来看看如何使用这个分页. 下面是一个自定义的类,用于查看订单的详细情况.关键在于get_order_data()这个方法, ...

  6. 几个开源faas 框架

    funktion open source event based lambda programming for kubernetes 官方地址: funktion.fabric8.io serverl ...

  7. Android中的基类—抽取出来公共的方法

    在Android中,一般来说一个应用会存在几十个页面,并且一个应用一般也会使用一个特定的主题,其中的页面的风格也是一致的,并且页面中的动画效果.页面的切换效果等也应该保持同样的风格,那么就需要一个基类 ...

  8. git超速掌握之一(基本使用)

    前言: 无论你是运维.开发还是IT爱好者,都会听说github了吧?动不动哪位大神就说在github上有什么什么项目,我的github地址是xxxxx,甚至有自己个github在找新工作时都能给自己加 ...

  9. StarkSoft题库管理系统

    一.功能介绍    1.自定义试题库管理系统目录.难易程度,题型,知识库等.    2.试题录入.    3.强大的试题编辑功能,并与通常应用编辑工具有共通.    4.灵活的试卷构造功能,用户可自定 ...

  10. ansible命令使用

    ansible命令使用 查看每个服务器的主机名 1 $ ansible multi -a "hostname" 使用一个线程执行命令,相当于顺序在每个服务器上运行(默认5个线程执行 ...