jdk动态代理讲解
今天看了一个文章顿时明白了jdk动态代理的一些东西,没事赶快记录一下。
之前看了很多关于动态代理文章,没有组织到一起,现在明白点,说一下,有说的不对的地方欢迎大家指出来 流水账开启。(这个教程适合懂一些,又不太明白的)
interface Mappers{
@Select("select * from User where id =#{id} and name=${name}")
List<Integer> selectAll(int id,String name);
}
public class Cesjo {
public static void main(String[] args) {
Mappers mappers = (Mappers) Proxy.newProxyInstance(Tesss.class.getClassLoader(), new Class<?>[]{Mappers.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Select annotation = method.getAnnotation(Select.class);
String sourSql="";
if(annotation.value() !=null && annotation.value().length>0){
sourSql=Arrays.toString(annotation.value());
System.out.println(Arrays.toString(annotation.value()));
}
Map<String, Object> map = buildParamert(method, args);
String afterSql=replaceSql(sourSql,map);
System.out.println(afterSql);
return null;
}
});
mappers.selectAll(100,"你好啊");
}
public static String replaceSql(String sourSql,Map<String ,Object> params){
String afterSql[]={sourSql};
params.forEach((s, o) -> {
afterSql[0]= afterSql[0].replaceAll(".\\{"+s+"\\}",o.toString());
});
return afterSql[0];
}
public static Map<String,Object> buildParamert(Method method, Object[] args){
Map<String ,Object> par=new HashMap<String ,Object>();
Parameter[] parameters = method.getParameters();
int index[]={0};
if(parameters!=null && parameters.length>0){
Arrays.asList(parameters).forEach(parameter -> {
par.put(parameter.getName(),args[index[0]++]);
});
}
return par;
}
}
看一下这个代码,这个是通过proxy动态代理生成的一个mepper对象实例。
先说newProxyInstance参数问题
1.该参数是一个类加载器,作用嘛就是通过类加载器,我们可以把一个class文件加载到内存中生成一个class的对象,这里并不是实例对象啊,是class对象。可以百度一下ClassLoader和class.forName的区别。
2.该参数就是传的一个class对象数组。这个数组里面放的是我们接口的类对象,通过这个class来实现。
3.这个参数是固定参数,是一个实现InvackHandler.接口里面可以写我们的需要去代理的方法。
再给大家看一段代码。这个网上也很多。
public interface Subject { // 定义代理接口
String sayHello();
}
public class SubjectImpl implements Subject { // 定义代理接口实现类
@Override
public String sayHello() {
System.out.println(" Hello World");
return "success";
}
}
public class ProxyInvocationHandler implements InvocationHandler { // 定义动态代理调用处理器
private Object target;
public ProxyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" 🧱 🧱 🧱 进入代理调用处理器 ");
return method.invoke(target, args);
}
}
public class ProxyTest {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
Subject proxy = (Subject) Proxy
.newProxyInstance(
subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(),
new ProxyInvocationHandler(subject));
proxy.sayHello();
/**
* 打印输出如下
* 调用处理器:🧱 🧱 🧱 进入代理调用处理器
* 被代理实现类:Hello World
*/
}
}
估计大家看过很多这样的代码吧,大家有没有好奇这种动态代理方式,为什么要写实现一个实现类,而我们在mybatis中却没有实现类。那是怎么搞的。带着这个问题给大家说一下。
动态代理有两种实现方式,一种是实现是上面写的那种,先实现一个接口的实现类,然后,在invockHander的实现类的invoke方法中通过注入target的方式用反射来调用。
第二种方式就是最开头的那种写法,并没有写被代理接口的实现类,这种方式。仅仅是调用了一下invoke这个方法。
所以说,动态代理的实现可以不用注入代理。
而在mybatis就是采用第二种方式,网上讲的大部分都是第一种方式,所以造成我太久的疑惑,
在proxy类里面有一个内部类叫ProxyClassFactory,这个类是生成我们被代理接口的实现类的,类名开始$proxy
其中有一个属性,我猜的啊,估计八九不离十 ,这个proxyClassFIle这个里面放的就是我们生成代理类的字符串。可以自己求证一下。
说几个mybaits中常见的问题:
- JDK 动态代理能否对类代理?
因为 JDK 动态代理生成的代理类,会继承 Proxy 类,由于 Java 无法多继承,所以无法对类进行代理
- 抽象类是否可以 JDK 动态代理?
不可以,因为jdk动态代理会检测被代理class是否为接口,都在java.lang包下面的。proxy这个类里面的。
- Mybatis Mapper 接口没有实现类,怎么实现的动态代理?
上面已经说了,mybatis的动态代理没有注入被代理对象,仅仅是调用了invockHandler方法中的invoke方法。所以不需要。你懂了不。