先展示下mybatis的动态代理是怎样的
结合上一篇文章实践的
1String resource = "mybatis.xml";
2 InputStream resourceAsStream = Resources.getResourceAsStream(resource);
3
4 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
5SqlSession sqlSession = factory.openSession();
6// 不使用动态代理的情况下
7try{
8 List<User> userList = sqlSession.selectList("com.wyl.mybatis.dao.UserDao.getUserList");
9 System.out.println("**********"+JSON.toJSON(userList));
10 }finally {
11 sqlSession.close();
12 }
13
14// 使用动态代理的情况下
15 try{
16 UserDao userDao = sqlSession.getMapper(UserDao.class);
17 List<User> userList = userDao.getUserList();
18 System.out.println("**********"+JSON.toJSON(userList));
19 }finally {
20 sqlSession.close();
21 }
这里就是mybatis最原始的代理实现方式,也是用反射机制完成的,
反射
1// Test.java
2public class Test {
3
4 public void sayHello(String name) {
5 System.out.println("*******"+name);
6 }
7}
8
9//MybatisApplication.java
10public class MybatisApplication {
11 public static void main(String[] args) throws Exception{
12 Object instance = Class.forName(Test.class.getName()).newInstance();
13 Method sayHell = instance.getClass().getMethod("sayHello", String.class);
14 sayHell.invoke(instance,"hello");
15}
16
17// 再举一个例子
18// class.txt
19class=com.wyl.mybatis.controller.Test
20method=printLog
21
22// Texst.java
23public class Test {
24 public void printLog(){
25 System.out.println("****打印日志***8");
26 }
27}
28
29//MybatisApplication.java
30public class MybatisApplication {
31
32 public static void main(String[] args) throws Exception{
33 Properties properties = new Properties();
34 properties.load(new FileReader("src/main/resources/class.txt"));
35 String className = properties.getProperty("class");
36 String methodName = properties.getProperty("method");
37 Class<?> forName = Class.forName(className);
38 Method method = forName.getMethod(methodName);
39 method.invoke(forName.newInstance());
40}
从上面可以知道,反射就是在程序执行之前根本就不知道要加载什么类和什么方法,只有通过程序运行中,动态的调用。
JDK动态代理
1// UserDao.java
2public interface UserDao {
3 void getMoney();
4}
5
6// UserDaoImpl.java
7@Slf4j
8public class UserDaoImpl implements UserDao {
9
10 @Override
11 public void getMoney() {
12 log.info("******获得大量的美元****");
13 }
14}
15
16> 需求需要记录人在什么时候获得金钱
17
18// JDKProxy.java
19@Slf4j
20public class JDKProxy implements InvocationHandler {
21
22 private Object object;
23 public JDKProxy(Object object) {
24 this.object = object;
25 }
26 // 创建代理对象
27 public Object createProxy() {
28 Object o = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
29 return o;
30 }
31
32 @Override
33 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
34 log.info("*********当前时间->{}", DateUtil.format(LocalDateTime.now(),"YYYY-MM-dd:HH:mm:ss") );
35 method.invoke(object, args);
36 return null;
37 }
38}
39
40// MybatisApplication.java
41public class MybatisApplication {
42
43 public static void main(String[] args) throws Exception{
44 UserDaoImpl userDao = new UserDaoImpl();
45 UserDao proxy= (UserDao) new JDKProxy(userDao).createProxy();
46 proxy.getMoney();
47
48
49运行结果:
5011:45:44.330 [main] INFO com.wyl.mybatis.proxy.JDKProxy - *********当前时间->2020-03-10:11:45:44
5111:45:44.363 [main] INFO com.wyl.mybatis.dao.impl.UserDaoImpl - ******获得大量的美元****
CGLIB动态代理
1// CGLIBProxy.java
2@Slf4j
3public class CGLIBProxy implements MethodInterceptor {
4
5 private Object object;
6
7 public CGLIBProxy(Object object) {
8 this.object = object;
9 }
10
11 // 创建代理对象
12 public Object createProxy() {
13 Object o = Enhancer.create(object.getClass(), this);
14 return o;
15
16 }
17
18 @Override
19 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
20
21 log.info("*********当前时间->{}", DateUtil.format(LocalDateTime.now(),"YYYY-MM-dd:HH:mm:ss") );
22 methodProxy.invoke(object,objects);
23 return null;
24 }
25}
26
27
28// 先使用上面有接口的测试用例
29public class MybatisApplication {
30 public static void main(String[] args) throws Exception{
31 UserDaoImpl userDao = new UserDaoImpl();
32 UserDao proxy = (UserDao) new CGLIBProxy(userDao).createProxy();
33 proxy.getMoney();
34 }
35
36运行结果:
3714:49:32.938 [main] INFO com.wyl.mybatis.proxy.CGLIBProxy - *********当前时间->2020-03-10:14:49:32
3814:49:32.973 [main] INFO com.wyl.mybatis.dao.impl.UserDaoImpl - ******获得大量的美元****
39
40// 新建UserController.class
41@Slf4j
42public class UserController {
43
44 public void getMoney() {
45 log.info("**********获得大量的金钱*********");
46 }
47}
48
49// MybatisApplication.java
50public class MybatisApplication {
51
52 public static void main(String[] args) throws Exception{
53 UserController userController = new UserController();
54 UserController proxy = (UserController) new CGLIBProxy(userController).createProxy();
55 proxy.getMoney();
56}
57
58运行结果:
5914:53:04.621 [main] INFO com.wyl.mybatis.proxy.CGLIBProxy - *********当前时间->2020-03-10:14:53:04
6014:53:04.659 [main] INFO com.wyl.mybatis.controller.UserController - **********获得大量的金钱*********
61
62从这里可以看出无论是否有接口CGLIB代理都可以,那么JDK代理呢?接着测试
63
64public class MybatisApplication {
65
66 public static void main(String[] args) throws Exception{
67 UserController userController = new UserController();
68 UserController proxy= (UserController) new JDKProxy(userController).createProxy();
69 proxy.getMoney();
70}
71运行结果:
72Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.wyl.mybatis.controller.UserController
73 at com.wyl.mybatis.MybatisApplication.main(MybatisApplication.java:29)
74
75可以看到,出现了类转换时,发生了不兼容的异常,所以说对于JDK动态代理来说,实现类一定要有接口。
总结
JDK动态代理其实是反射机制实现的aop动态代理,实现类一定要有接口;CGLIB动态代理是JDK动态代理的补充。在不用接口的情况下,也可以使用动态代理。