0%

反射与动态代理的关系

先展示下mybatis的动态代理是怎样的

结合上一篇文章实践的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
String resource = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = factory.openSession();
// 不使用动态代理的情况下
try{
List<User> userList = sqlSession.selectList("com.wyl.mybatis.dao.UserDao.getUserList");
System.out.println("**********"+JSON.toJSON(userList));
}finally {
sqlSession.close();
}

// 使用动态代理的情况下
try{
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
System.out.println("**********"+JSON.toJSON(userList));
}finally {
sqlSession.close();
}

这里就是mybatis最原始的代理实现方式,也是用反射机制完成的,

反射

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
// Test.java
public class Test {

public void sayHello(String name) {
System.out.println("*******"+name);
}
}

//MybatisApplication.java
public class MybatisApplication {
public static void main(String[] args) throws Exception{
Object instance = Class.forName(Test.class.getName()).newInstance();
Method sayHell = instance.getClass().getMethod("sayHello", String.class);
sayHell.invoke(instance,"hello");
}

// 再举一个例子
// class.txt
class=com.wyl.mybatis.controller.Test
method=printLog

// Texst.java
public class Test {
public void printLog(){
System.out.println("****打印日志***8");
}
}

//MybatisApplication.java
public class MybatisApplication {

public static void main(String[] args) throws Exception{
Properties properties = new Properties();
properties.load(new FileReader("src/main/resources/class.txt"));
String className = properties.getProperty("class");
String methodName = properties.getProperty("method");
Class<?> forName = Class.forName(className);
Method method = forName.getMethod(methodName);
method.invoke(forName.newInstance());
}

从上面可以知道,反射就是在程序执行之前根本就不知道要加载什么类和什么方法,只有通过程序运行中,动态的调用。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// UserDao.java
public interface UserDao {
void getMoney();
}

// UserDaoImpl.java
@Slf4j
public class UserDaoImpl implements UserDao {

@Override
public void getMoney() {
log.info("******获得大量的美元****");
}
}

> 需求需要记录人在什么时候获得金钱

// JDKProxy.java
@Slf4j
public class JDKProxy implements InvocationHandler {

private Object object;
public JDKProxy(Object object) {
this.object = object;
}
// 创建代理对象
public Object createProxy() {
Object o = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
return o;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("*********当前时间->{}", DateUtil.format(LocalDateTime.now(),"YYYY-MM-dd:HH:mm:ss") );
method.invoke(object, args);
return null;
}
}

// MybatisApplication.java
public class MybatisApplication {

public static void main(String[] args) throws Exception{
UserDaoImpl userDao = new UserDaoImpl();
UserDao proxy= (UserDao) new JDKProxy(userDao).createProxy();
proxy.getMoney();


运行结果:
11:45:44.330 [main] INFO com.wyl.mybatis.proxy.JDKProxy - *********当前时间->2020-03-10:11:45:44
11:45:44.363 [main] INFO com.wyl.mybatis.dao.impl.UserDaoImpl - ******获得大量的美元****

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
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
// CGLIBProxy.java
@Slf4j
public class CGLIBProxy implements MethodInterceptor {

private Object object;

public CGLIBProxy(Object object) {
this.object = object;
}

// 创建代理对象
public Object createProxy() {
Object o = Enhancer.create(object.getClass(), this);
return o;

}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

log.info("*********当前时间->{}", DateUtil.format(LocalDateTime.now(),"YYYY-MM-dd:HH:mm:ss") );
methodProxy.invoke(object,objects);
return null;
}
}


// 先使用上面有接口的测试用例
public class MybatisApplication {
public static void main(String[] args) throws Exception{
UserDaoImpl userDao = new UserDaoImpl();
UserDao proxy = (UserDao) new CGLIBProxy(userDao).createProxy();
proxy.getMoney();
}

运行结果:
14:49:32.938 [main] INFO com.wyl.mybatis.proxy.CGLIBProxy - *********当前时间->2020-03-10:14:49:32
14:49:32.973 [main] INFO com.wyl.mybatis.dao.impl.UserDaoImpl - ******获得大量的美元****

// 新建UserController.class
@Slf4j
public class UserController {

public void getMoney() {
log.info("**********获得大量的金钱*********");
}
}

// MybatisApplication.java
public class MybatisApplication {

public static void main(String[] args) throws Exception{
UserController userController = new UserController();
UserController proxy = (UserController) new CGLIBProxy(userController).createProxy();
proxy.getMoney();
}

运行结果:
14:53:04.621 [main] INFO com.wyl.mybatis.proxy.CGLIBProxy - *********当前时间->2020-03-10:14:53:04
14:53:04.659 [main] INFO com.wyl.mybatis.controller.UserController - **********获得大量的金钱*********

从这里可以看出无论是否有接口CGLIB代理都可以,那么JDK代理呢?接着测试

public class MybatisApplication {

public static void main(String[] args) throws Exception{
UserController userController = new UserController();
UserController proxy= (UserController) new JDKProxy(userController).createProxy();
proxy.getMoney();
}
运行结果:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.wyl.mybatis.controller.UserController
at com.wyl.mybatis.MybatisApplication.main(MybatisApplication.java:29)

可以看到,出现了类转换时,发生了不兼容的异常,所以说对于JDK动态代理来说,实现类一定要有接口。

总结

JDK动态代理其实是反射机制实现的aop动态代理,实现类一定要有接口;CGLIB动态代理是JDK动态代理的补充。在不用接口的情况下,也可以使用动态代理。

-------------本文结束感谢你的阅读---------