MyBatis基于接口编程的原理分析

源码

UserMapper

1
2
3
4
5
6
7
8
9
10
11
package org.denger.mapper;  

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.denger.po.User;

public interface UserMapper {

@Select("select * from tab_uc_account where id=#{userId}")
User getUser(@Param("userId") Long userId);
}

application-context.xml

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
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">


<!-- Provided by annotation-based configuration -->
<context:annotation-config/>

<!--JDBC Transaction Manage -->
<bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg>
<ref bean="dataSource" />
</constructor-arg>
</bean>

<!-- The JDBC c3p0 dataSource bean-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/noah" />
<property name="user" value="root" />
<property name="password" value="123456" />
</bean>

<!--MyBatis integration with Spring as define sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<property name="basePackage" value="org.denger.mapper"></property>
</bean>
</beans>

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.denger.mapper;  

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(locations = { "/application-context.xml"})
public class UserMapperTest extends AbstractJUnit4SpringContextTests{

@Autowired
public UserMapper userMapper;

@Test
public void testGetUser(){
Assert.assertNotNull(userMapper.getUser(300L));
}
}

原理分析

对于以上极其简单代码看上去并无特殊之处,主要亮点在于 UserMapper 居然不用实现类,而且在调用 getUser 的时候,也是使用直接调用了UserMapper实现类,那么Mybatis是如何去实现 UserMapper的接口的呢?
可能你马上能想到的实现机制就是通过动态代理方式,看看MyBatis整个的代理过程吧。
首先在Spring的配置文件中看到下面的Bean。

1
2
3
4
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<property name="basePackage" value="org.denger.mapper"></property>
</bean>

1
2
3
4
5
/**
* BeanDefinitionRegistryPostProcessor that searches recursively starting from a base package for
* interfaces and registers them as {@code MapperFactoryBean}. Note that only interfaces with at
* least one method will be registered; concrete classes will be ignored.
* <p>

以上的MapperScannerConfigurer的注释中描述道。
从base包中搜索所有下面所有interface,并将其注册到Spring Bean容器中,其注册的class bean是MapperFactoryBean
看看它的注册过程,从MapperScannerConfigurer#Scanner类中抽取,在初始化以上application-content.xml文件时就会进行调用。
主要用于是搜索base packages下的所有mapper.class,并将其注册至spring的benfinitionHolder中。

Mybatis一级缓存和二级缓存

一级缓存

一级缓存,又叫本地缓存,是PerpetualCache类型的永久缓存,保存在执行器中(BaseExecutor),而执行器又在SqlSessionDefaultSqlSession)中,所以一级缓存的生命周期与SqlSession是相同的。
MyBatis的一级缓存指的是在一个Session域内,Session为关闭的时候执行的查询会根据SQL为key被缓存,单独使用MyBatis而不继承Spring,使用原生的MyBatis的SqlSessionFactory来构造sqlSession查询,是可以使用以及缓存的。
当参数不变的时候只进行一次查询,参数变更以后,则需要重新进行查询,而清空缓存以后,参数相同的查询过的SQL也需要重新查询,当执行SQL时两次查询中间发生增删改操作,则SqlSession的缓存清空。
如果集成Spring是没有使用一级缓存。原因是一个sqlSession,但是实际上因为我们的dao继承SqlSessionDaoSupport,而SqlSessionDaoSupport内部sqlSession的实现是使用用动态代理实现的,这个动态代理sqlSessionProxy使用一个模板方法封装select()等操作,每一次select()查询都会自动先执行openSession(),执行完后调用close()方法,相当于生成一个新的session实例,所以我们无需手动的去关闭这个session(),当然也无法使用MyBatis的一级缓存,也就是说MyBatis的一级缓存在Spring中是没有作用的。

官方文档

MyBatis SqlSession provides you with specific methods to handle transactions programmatically. But when using MyBatis-Spring your beans will be injected with a Spring managed SqlSession or a Spring managed mapper. That means that Spring will always handle your transactions.
You cannot call SqlSession.commit(), SqlSession.rollback() or SqlSession.close() over a Spring managed SqlSession. If you try to do so, a UnsupportedOperationException exception will be thrown. Note these methods are not exposed in injected mapper classes.

二级缓存

二级缓存,又叫自定义缓存,实现Cache接口的类都可以作为二级缓存,所以可配置如encache等的第三方缓存。二级缓存以namespace名称空间为其唯一标识,被保存在Configuration核心配置对象中。

1
2
3
4
5
public class Configuration {
// ...
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
// ...
}

每次构建SqlSessionFactory对象时都会创建新的Configuration对象,因此,二级缓存的生命周期与SqlSessionFactory是相同的。在创建每个MapperedStatement对象时,都会根据其所属的namespace名称空间,给其分配Cache缓存对象。
二级缓存同样执行增删查改操作,会清空缓存。
二级缓存就是global caching,它超出session范围之外,可以被所有sqlSession共享,它的实现机制和MySQL的缓存一样,开启它只需要在MyBatis的配置文件开启settings里。

Redis持久化RDB和AOF

Redis持久化

Redis提供多种不同级别的持久化方式:一种是RDB,另一种是AOF。
RDB会根据配置的规则定时将内存中的数据持久化到硬盘上,AOF则是在每次执行写命令之后将命令记录下来。两种持久化方式可以单独使用,但是通常会将两者结合使用。
RDB持久化和AOF持久化之间的异同是非常重要的,以下几个小节将详细地介绍这这两种持久化功能,并对它们的相同和不同之处进行说明。

RDB持久化

RDB持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
RDB方式的持久化是通过快照的方式完成的。当符合某种规则时,会将内存中的数据全量生成一份副本存储到硬盘上,这个过程称作”快照”,Redis会在以下几种情况下对数据进行快照。

  1. 根据配置规则进行自动快照。
  2. 用户执行SAVE,BGSAVE命令。
  3. 执行FLUSHALL命令。
  4. 执行复制(replication)。

    执行快照场景

    根据配置自动快照,Redis允许用户自定义快照条件,当满足条件时自动执行快照,快照规则的配置方式如下。
    1
    2
    3
    save 900 1
    save 300 10
    save 60 10000

每个快照条件独占一行,他们之间是或(||)关系,只要满足任何一个就进行快照。上面配置save后的第一个参数T是时间,单位是秒,第二个参数M是更改的键的个数,含义是:当时间T内被更改的键的个数大于M时,自动进行快照。比如save 900 1的含义是15分钟内(900s)被更改的键的个数大于1时,自动进行快照操作。

ASM

  • ASM
  • ASM获取方法参数名字
  • ASM增强代理类,并覆写方法
    • ASM工厂类`
    • Class的适配器类(ClassAdapter )`
    • [代理类
      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
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      	- [生成的class`](#%E7%94%9F%E6%88%90%E7%9A%84class)

      <!-- /TOC -->

      # ASM
      ASM是一个Java字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入Java虚拟机之前动态改变类行为。
      ASM实现可以是接口,也是可以类。
      Java class被存储在严格格式定义的`.class`文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及Java字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
      ## JVM执行class的指令
      在Java中每一个方法在执行的时候JVM都会为其分配一个"帧",帧是用来存储方法中计算所需要的所有数据。其中第0个元素就是`this`,如果方法有参数传入会排在它的后面。
      ### ALOAD_0
      这个指令是`LOAD`系列指令中的一个,它的意思表示装载当前第0个元素到堆栈中。代码上相当于"this"。而这个数据元素的类型是一个引用类型。这些指令包含`ALOAD`,`ILOAD`,`LLOAD`,`FLOAD`,`DLOAD`。区分它们的作用就是针对不用数据类型而准备的`LOAD`指令,此外还有专门负责处理数组的指令`SALOAD`。
      ### Invokespecial
      这个指令是调用系列指令中的一个。其目的是调用对象类的方法。后面需要给上父类的方法完整签名。"#8"的意思是 `.class`文件常量表中第8个元素。值为`java/lang/Object."<init>":()V`。结合`ALOAD_0`。这两个指令可以翻译为`super()`。其含义是调用自己的父类构造方法。
      ### GETSTATIC
      这个指令是`GET`系列指令中的一个其作用是获取静态字段内容到堆栈中。这一系列指令包括了:`GETFIELD`、`GETSTATIC`。它们分别用于获取动态字段和静态字段。
      ### IDC
      这个指令的功能是从常量表中装载一个数据到堆栈中。
      ### Invokevirtual
      是一种调用指令,这个指令区别与`invokespecial`的是它是根据引用调用对象类的方法。这里有一篇文章专门讲解这两个指令:"http://wensiqun.iteye.com/blog/1125503"。
      ### RETURN
      这也是一系列指令中的一个,其目的是方法调用完毕返回,可用的其他指令有:`IRETURN`,`DRETURN`,`ARETURN`等,用于表示不同类型参数的返回。
      <!-- more -->
      # ASM获取方法参数名字
      ## ASM
      ```` java
      package asm;

      import java.io.IOException;
      import java.lang.reflect.Method;
      import java.lang.reflect.Modifier;

      import org.objectweb.asm.ClassReader;
      import org.objectweb.asm.ClassVisitor;
      import org.objectweb.asm.ClassWriter;
      import org.objectweb.asm.Label;
      import org.objectweb.asm.MethodVisitor;
      import org.objectweb.asm.Opcodes;
      import org.objectweb.asm.Type;

      import com.me.WorkServiceImpl;

      /**
      * <p>
      * 基于asm的工具类
      * </p>
      */
      public final class ASM {

      private ASM() {
      }

      /**
      *
      * <p>
      * 比较参数类型是否一致
      * </p>
      *
      * @param types
      * asm的类型({@link Type})
      * @param clazzes
      * java 类型({@link Class})
      * @return
      */
      private static boolean sameType(Type[] types, Class<?>[] clazzes) {
      // 个数不同
      if (types.length != clazzes.length) {
      return false;
      }
      for (int i = 0; i < types.length; i++) {
      if (!Type.getType(clazzes[i]).equals(types[i])) {
      return false;
      }
      }
      return true;
      }

      /**
      *
      * <p>
      * 获取方法的参数名
      * </p>
      *
      * @param m
      * @return
      */
      public static String[] getMethodParamNames(final Method method) {
      //参数名字
      final String[] paramNames = new String[method.getParameterTypes().length];
      //方法名字
      final String methodName = method.getDeclaringClass().getName();
      //class写入
      final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
      ClassReader classReader = null;
      try {
      //写入方法
      classReader = new ClassReader(methodName);
      } catch (IOException e) {
      e.printStackTrace();
      }
      classReader.accept(new ClassVisitor(Opcodes.ASM4, classWriter) {
      @Override
      public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
      //参数类型
      final Type[] args = Type.getArgumentTypes(desc);
      // 判断方法名字,方法参数
      if (!name.equals(method.getName()) || !sameType(args, method.getParameterTypes())) {
      return super.visitMethod(access, name, desc, signature, exceptions);
      }
      //方法访问
      MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
      return new MethodVisitor(Opcodes.ASM4, methodVisitor) {
      @Override
      public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
      int i = index - 1;
      // 如果是静态方法,则第一就是参数
      // 如果不是静态方法,则第一个是"this",然后才是方法的参数
      if (Modifier.isStatic(method.getModifiers())) {
      i = index;
      }
      if (i >= 0 && i < paramNames.length) {
      paramNames[i] = name;
      }
      super.visitLocalVariable(name, desc, signature, start, end, index);
      }
      };
      }
      }, 0);
      return paramNames;
      }

      //ASM支持泛型
      public static void main(String[] args) throws SecurityException, NoSuchMethodException {
      //获取方法参数,方法名字,方法参数
      String[] s = getMethodParamNames(WorkServiceImpl.class.getMethod("work", String.class,Object.class));
      for (int i = 0; i < s.length; i++) {
      System.out.println(s[i]);
      }
      }

      }
      `

MethodParamNamesScanner

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package asm;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;

import com.me.WorkServiceImpl;


/**
* 获取方法参数名字
*/
public class MethodParamNamesScanner {

/**
* 获取方法参数名列表
*
* @param clazz
* @param m
* @return
* @throws IOException
*/
public static List<String> getMethodParamNames(Class<?> clazz, Method method) throws IOException {
try (InputStream in = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class")) {
return getMethodParamNames(in, method);
}
}

public static List<String> getMethodParamNames(InputStream in, Method method) throws IOException {
try (InputStream ins = in) {
return getParamNames(ins, new EnclosingMetadata(method.getName(), Type.getMethodDescriptor(method),
method.getParameterTypes().length));
}
}

/**
* 获取构造器参数名列表
*
* @param clazz
* @param constructor
* @return
*/
public static List<String> getConstructorParamNames(Class<?> clazz, Constructor<?> constructor) {
try (InputStream in = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class")) {
return getConstructorParamNames(in, constructor);
} catch (IOException e) {
e.printStackTrace();
}
return new ArrayList<String>();
}

public static List<String> getConstructorParamNames(InputStream ins, Constructor<?> constructor) {
try (InputStream in = ins) {
return getParamNames(
in,
new EnclosingMetadata(constructor.getName(), Type.getConstructorDescriptor(constructor), constructor
.getParameterTypes().length));
} catch (IOException e1) {
e1.printStackTrace();
}
return new ArrayList<String>();
}

/**
* 获取参数名列表辅助方法
*
* @param in
* @param m
* @return
* @throws IOException
*/
private static List<String> getParamNames(InputStream in, EnclosingMetadata enclosingMetadata) throws IOException {
ClassReader classReader = new ClassReader(in);
ClassNode classNode = new ClassNode();
classReader.accept(classNode, ClassReader.EXPAND_FRAMES);// 建议EXPAND_FRAMES
// ASM树接口形式访问
List<MethodNode> methods = classNode.methods;
List<String> list = new ArrayList<String>();
for (int i = 0; i < methods.size(); ++i) {
List<LocalVariable> varNames = new ArrayList<LocalVariable>();
MethodNode methodNode = methods.get(i);
// 验证方法签名
if (methodNode.desc.equals(enclosingMetadata.desc) && methodNode.name.equals(enclosingMetadata.name)) {
List<LocalVariableNode> local_variables = methodNode.localVariables;
for (int k = 0; k < local_variables.size(); k++) {
String varName = local_variables.get(k).name;
// index-记录了正确的方法本地变量索引。(方法本地变量顺序可能会被打乱。而index记录了原始的顺序)
int index = local_variables.get(k).index;
// 非静态方法,第一个参数是this
if (!"this".equals(varName)) {
varNames.add(new LocalVariable(index, varName));
}
}
LocalVariable[] tmpArr = varNames.toArray(new LocalVariable[varNames.size()]);
// 根据index来重排序,以确保正确的顺序
Arrays.sort(tmpArr);
for (int j = 0; j < enclosingMetadata.size; j++) {
list.add(tmpArr[j].name);
}
break;
}
}
return list;
}

/**
* 方法本地变量索引和参数名封装
*
* @author xby Administrator
*/
static class LocalVariable implements Comparable<LocalVariable> {
public int index;
public String name;

public LocalVariable(int index, String name) {
this.index = index;
this.name = name;
}

public int compareTo(LocalVariable o) {
return this.index - o.index;
}
}

/**
* 封装方法描述和参数个数
*
* @author xby Administrator
*/
static class EnclosingMetadata {
// method name
public String name;
// method description
public String desc;
// params size
public int size;

public EnclosingMetadata(String name, String desc, int size) {
this.name = name;
this.desc = desc;
this.size = size;
}
}

public static void main(String[] args) throws IOException {
for (Method m : WorkServiceImpl.class.getDeclaredMethods()) {
List<String> list = getMethodParamNames(WorkServiceImpl.class, m);
System.out.println(m.getName() + ":");
for (String str : list) {
System.out.println(str);
}
System.out.println("------------------------");
}
}
}

ASM增强代理类,并覆写方法

  1. 生成被代理类的子类,并重写所有方法(除java.lang.Object类定义的方法),增加beforeafter拦截。
  2. 重新定义被代理类的所有属性(不包括属性的赋值)。
  3. 把生成的class文件保存到硬盘中。
  4. 从内存中加载新生成的class。

    ASM工厂类

    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
    package asm;

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;

    import org.objectweb.asm.ClassReader;
    import org.objectweb.asm.ClassVisitor;
    import org.objectweb.asm.ClassWriter;

    /**
    *
    * <p>
    * asm 代理工厂
    * </p>
    */
    public class AsmFactory {
    public static final String SUFIX = "$EnhancedByCc";
    public static BytecodeLoader classLoader = new BytecodeLoader();

    /**
    *
    * <p>
    * 根据字节码加载class
    * </p>
    */
    public static class BytecodeLoader extends ClassLoader {
    public Class<?> defineClass(String className, byte[] byteCodes) {
    return super.defineClass(className, byteCodes, 0, byteCodes.length);
    }
    }

    /**
    *
    * <p>
    * 返回代理类(增强类)
    * </p>
    *
    * @param <T>
    * @param clazz
    * @return
    * @throws Exception
    */
    @SuppressWarnings("unchecked")
    protected static <T> Class<T> getEnhancedClass(Class<T> clazz) {
    String enhancedClassName = clazz.getName() + SUFIX;
    try {
    return (Class<T>) classLoader.loadClass(enhancedClassName);
    } catch (ClassNotFoundException classNotFoundException) {//class没有找到,重新加载class
    ClassReader reader = null;
    try {
    reader = new ClassReader(clazz.getName());
    } catch (IOException ioexception) {
    throw new RuntimeException(ioexception);
    }
    ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    //初始化classvisitor
    ClassVisitor visitor = new ClassAdapter(enhancedClassName, clazz, writer);
    reader.accept(visitor, 0);
    byte[] byteCodes = writer.toByteArray();
    //写入class文件
    writeClazz(enhancedClassName, byteCodes);
    Class<T> result = (Class<T>) classLoader.defineClass(enhancedClassName, byteCodes);
    return result;
    }
    }

    /**
    *
    * <p>
    * 把java字节码写入class文件
    * </p>
    *
    * @param <T>
    * @param name
    * @param data
    * @throws FileNotFoundException
    * @throws IOException
    */
    public static <T> void writeClazz(String name, byte[] data) {
    try {
    File file = new File("C:/TEMP/" + name + ".class");
    FileOutputStream fout = new FileOutputStream(file);
    fout.write(data);
    fout.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    Class<RoleService> rsCls = getEnhancedClass(RoleService.class);
    rsCls.newInstance().executeOuter(1);
    }

    }

Class的适配器类(ClassAdapter )

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
package asm;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
*
* <p>根据class A生成一个class B extends A</p>
* <li>重写A及A父类的所有方法,eg. public void xx() {super.xx();}
* <li>copy A类定义的所有属性
*
1、生成被代理类的子类,并重写所有方法(除java.lang.Object类定义的方法),增加before和after拦截。
2、重新定义被代理类的所有属性(不包括属性的赋值)。
3、把生成的class文件保存到硬盘中。
4、从内存中加载新生成的class。
*
*/
public class ClassAdapter extends ClassVisitor implements Opcodes{
public static final String INIT = "<init>";
private ClassWriter classWriter;
//原始类名字
private String originalClassName;
//增强类名字
private String enhancedClassName;
//原始类
private Class<?> originalClass;


/**
*
* <p>Title: </p>
* <p>Description: </p>
* @param enhancedClassName 增强类
* @param targetClass 目标类
* @param writer class写入
*/
public ClassAdapter(String enhancedClassName, Class<?> targetClass, ClassWriter writer) {
super(Opcodes.ASM4, writer);
this.classWriter = writer;
this.originalClassName = targetClass.getName();
this.enhancedClassName = enhancedClassName;
this.originalClass = targetClass;
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
cv.visit(version, Opcodes.ACC_PUBLIC, toAsmCls(enhancedClassName), signature, name, interfaces);
}

/**
* 访问属性
*/
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
// 拷贝所有属性 可使用java反射给属性赋值(生成class后newInstance在赋值)
return super.visitField(access, name, desc, signature, value);
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// 删除所有方法
return null;
}

/**
* 把类名中的.替换为/
*
* @param className
* @return
*/
private static String toAsmCls(String className) {
return className.replace('.', '/');
}

/**
*
* <p>
* 前置方法
* </p>
*
* @see TxHandler
* @param mWriter
*/
private static void doBefore(MethodVisitor mWriter, String methodInfo) {
mWriter.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mWriter.visitLdcInsn("before method : " + methodInfo);
mWriter.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
// 或者直接调用静态方法
// mWriter.visitLdcInsn(methodInfo);
// mWriter.visitMethodInsn(INVOKESTATIC,toAsmCls(TxHandler.class.getName()),"before","(Ljava/lang/String;)V");
}

/**
*
* <p>
* 后置方法
* </p>
*
* @see TxHandler
* @param mWriter
*/
private static void doAfter(MethodVisitor mWriter, String methodInfo) {
mWriter.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mWriter.visitLdcInsn("after method : " + methodInfo);
mWriter.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
}

/**
* 判断方法是否可以重新覆写
* <p>
* object类本身的方法不做重写
* </p>
* <p>
* "main" 方法不做重写
* </p>
*
* @param m
* @return
*/
public static boolean needOverride(Method method) {
// object类本身的方法不做重写
if (method.getDeclaringClass().getName().equals(Object.class.getName())) {
return false;
}
// "main" 方法不做重写
if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers()) && method.getReturnType().getName().equals("void")
&& method.getName().equals("main")) {
return false;
}
return true;
}

@Override
public void visitEnd() {
// 如果originalClass定义了私有成员变量,那么直接在visitMethod中复制originalClass的<init>会报错。
// ALOAD 0
// INVOKESPECIAL cc/RoleService.<init>()V
// RETURN
// // 调用originalClassName的<init>方法,否则class不能实例化
MethodVisitor mvInit = classWriter.visitMethod(ACC_PUBLIC, INIT, "()V", null, null);
mvInit.visitVarInsn(ALOAD, 0);
mvInit.visitMethodInsn(INVOKESPECIAL, toAsmCls(originalClassName), INIT, "()V");
mvInit.visitInsn(RETURN);
mvInit.visitMaxs(0, 0);
mvInit.visitEnd();

// 获取所有方法,并重写(main方法 和 Object的方法除外)
Method[] methods = originalClass.getMethods();
for (Method m : methods) {
//不覆写main方法
if (!needOverride(m)) {
continue;
}
//类型
Type mt = Type.getType(m);
StringBuilder methodInfo = new StringBuilder(originalClassName);
methodInfo.append(".").append(m.getName());
methodInfo.append("|");

Class<?>[] paramTypes = m.getParameterTypes();
//类型名字
for (Class<?> t : paramTypes) {
methodInfo.append(t.getName()).append(",");
}
if (paramTypes.length > 0) {
//删除最后一个“,”
methodInfo.deleteCharAt(methodInfo.length() - 1);
}

// 方法是被哪个类定义的
String declaringCls = toAsmCls(m.getDeclaringClass().getName());
// 方法 description
MethodVisitor mWriter = classWriter.visitMethod(ACC_PUBLIC, m.getName(), mt.toString(), null, null);
// insert code here (before)
doBefore(mWriter, methodInfo.toString());
int i = 0;
// 如果不是静态方法 load this对象
if (!Modifier.isStatic(m.getModifiers())) {
mWriter.visitVarInsn(ALOAD, i++);
}
StringBuilder sb = new StringBuilder(m.getName());
// load 出方法的所有参数
for (Class<?> tCls : m.getParameterTypes()) {
Type t = Type.getType(tCls);
sb.append(loadCode(t)).append(",");
mWriter.visitVarInsn(loadCode(t), i++);
if (t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) {
i++;
}
}
// super.xxx();
mWriter.visitMethodInsn(INVOKESPECIAL, declaringCls, m.getName(), mt.toString());
// 处理返回值类型
Type rt = Type.getReturnType(m);
// 没有返回值
if (rt.toString().equals("V")) {
doAfter(mWriter, methodInfo.toString());
mWriter.visitInsn(RETURN);
}
// 把return xxx() 转变成 : Object o = xxx(); return o;
else {
int storeCode = storeCode(rt);
int loadCode = loadCode(rt);
int returnCode = rtCode(rt);
mWriter.visitVarInsn(storeCode, i);
doAfter(mWriter, methodInfo.toString());
mWriter.visitVarInsn(loadCode, i);
mWriter.visitInsn(returnCode);
}
// 已设置了自动计算,但还是要调用一下,不然会报错
mWriter.visitMaxs(i, ++i);
mWriter.visitEnd();
}
cv.visitEnd();
}

/**
*
* <p>
* get StoreCode(Opcodes#xStore)
* </p>
*
*
* @param type
* @return
*/
public static int storeCode(Type type) {
//排序
int sort = type.getSort();
switch (sort) {
case Type.ARRAY:
sort = ASTORE;
break;
case Type.BOOLEAN:
sort = ISTORE;
break;
case Type.BYTE:
sort = ISTORE;
break;
case Type.CHAR:
sort = ISTORE;
break;
case Type.DOUBLE:
sort = DSTORE;
break;
case Type.FLOAT:
sort = FSTORE;
break;
case Type.INT:
sort = ISTORE;
break;
case Type.LONG:
sort = LSTORE;
break;
case Type.OBJECT:
sort = ASTORE;
break;
case Type.SHORT:
sort = ISTORE;
break;
default:
break;
}
return sort;
}

/**
*
* <p>
* get StoreCode(Opcodes#xLOAD)
* </p>
*
* @param type
* @return
*/
public static int loadCode(Type type) {
int sort = type.getSort();
switch (sort) {
case Type.ARRAY:
sort = ALOAD;
break;
case Type.BOOLEAN:
sort = ILOAD;
break;
case Type.BYTE:
sort = ILOAD;
break;
case Type.CHAR:
sort = ILOAD;
break;
case Type.DOUBLE:
sort = DLOAD;
break;
case Type.FLOAT:
sort = FLOAD;
break;
case Type.INT:
sort = ILOAD;
break;
case Type.LONG:
sort = LLOAD;
break;
case Type.OBJECT:
sort = ALOAD;
break;
case Type.SHORT:
sort = ILOAD;
break;
default:
break;
}
return sort;
}

/**
*
* <p>
* get StoreCode(Opcodes#xRETURN)
* </p>
*
* @param type
* @return
*/
public static int rtCode(Type type) {
int sort = type.getSort();
switch (sort) {
case Type.ARRAY:
sort = ARETURN;
break;
case Type.BOOLEAN:
sort = IRETURN;
break;
case Type.BYTE:
sort = IRETURN;
break;
case Type.CHAR:
sort = IRETURN;
break;
case Type.DOUBLE:
sort = DRETURN;
break;
case Type.FLOAT:
sort = FRETURN;
break;
case Type.INT:
sort = IRETURN;
break;
case Type.LONG:
sort = LRETURN;
break;
case Type.OBJECT:
sort = ARETURN;
break;
case Type.SHORT:
sort = IRETURN;
break;
default:
break;
}
return sort;
}
}

代理类

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
package asm;


/**
* <p></p>
*/
public class RoleService extends Service{
private String field1 = "";

public String executeOuter(Integer name) {
System.out.println("executeOuter call super.query()");
return query();
}

@Override
public String query() {
System.out.println("execute (RoleService): query");
return "query result (RoleService)";
}

public String getField1() {
return field1;
}

public void setField1(String field1) {
this.field1 = field1;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Service.java 11:59:38 AM Apr 27, 2012
*
* Copyright(c) 2000-2012 HC360.COM, All Rights Reserved.
*/
package asm;


/**
* <p></p>
*
*/
public class Service extends Dao{

@Override
public void insert(Object o) {
super.insert(o);
}

}
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
/**
* Dao.java 11:58:24 AM Apr 27, 2012
*
* Copyright(c) 2000-2012 HC360.COM, All Rights Reserved.
*/
package asm;
/**
* <p></p>
*
*/
public class Dao {
public void insert(Object o) {
System.out.println("insert : " + o);
}

public String query() {
System.out.println("execute (Dao): query");
return "query result (Dao)";
}
public String query2() {
System.out.println("execute (Dao): query2");
return "query2 result (Dao)";
}

public int test() {
System.out.println("execute (Dao) : test");
return 0;
}

protected void testProtected() {
System.out.println("execute (Dao) : testProtected");
testPrivate();
}

private void testPrivate() {
System.out.println("execute (Dao) : testPrivate");
}

public Dao[] testArray(String[] s,Object o) {
System.out.println("execute (Dao) : testArray");
return null;
}
}

生成的class

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
package asm;

import java.io.PrintStream;

public class RoleService$EnhancedByCc extends RoleService
{
private String field1;

public String executeOuter(Integer paramInteger)
{
System.out.println("before method : asm.RoleService.executeOuter|java.lang.Integer");
String str = super.executeOuter(paramInteger);
System.out.println("after method : asm.RoleService.executeOuter|java.lang.Integer");
return str;
}

public String query()
{
System.out.println("before method : asm.RoleService.query|");
String str = super.query();
System.out.println("after method : asm.RoleService.query|");
return str;
}

public void setField1(String paramString)
{
System.out.println("before method : asm.RoleService.setField1|java.lang.String");
super.setField1(paramString);
System.out.println("after method : asm.RoleService.setField1|java.lang.String");
}

public String getField1()
{
System.out.println("before method : asm.RoleService.getField1|");
String str = super.getField1();
System.out.println("after method : asm.RoleService.getField1|");
return str;
}

public void insert(Object paramObject)
{
System.out.println("before method : asm.RoleService.insert|java.lang.Object");
super.insert(paramObject);
System.out.println("after method : asm.RoleService.insert|java.lang.Object");
}

public String query2()
{
System.out.println("before method : asm.RoleService.query2|");
String str = super.query2();
System.out.println("after method : asm.RoleService.query2|");
return str;
}

public int test()
{
System.out.println("before method : asm.RoleService.test|");
int i = super.test();
System.out.println("after method : asm.RoleService.test|");
return i;
}

public Dao[] testArray(String[] paramArrayOfString, Object paramObject)
{
System.out.println("before method : asm.RoleService.testArray|[Ljava.lang.String;,java.lang.Object");
Dao[] arrayOfDao = super.testArray(paramArrayOfString, paramObject);
System.out.println("after method : asm.RoleService.testArray|[Ljava.lang.String;,java.lang.Object");
return arrayOfDao;
}
}

(转载)Spring Bean生命周期,实例化顺序

简介

Spring中Bean容器的生命周期。

首先明确一点,并非Spring容器中所有的Bean都有生命周期行为,只有接受容器管理生命周期的Bean才具有生命周期行为;而单例(Singleton)Bean接受容器管理,非单例(non-singleton)Bean在实例化后,完全交给了客户端代码管理,容器不再跟踪其生命周期,每次客户请求,容器都会创建一个新的实例,所以Spring容易无法知晓Bean何时销毁。
Bean容器的生命周期。其实上图有个节点没有画出,就是在实例化所有Bean之前会执行BeanFactoryPostProcessors

Spring四种注入方式(IOC)

Set方式

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SpringAction1 {
// 注入对象springDao
private SpringDao springDao;

// 一定要写被注入对象的set方法
public void setSpringDao(SpringDao springDao) {
this.springDao = springDao;
}

public void ok() {
springDao.ok();
}
}
1
2
3
4
5
6
7
package test.spring1;

public class SpringDao {
public void ok() {
System.out.println("Spring dao");
}
}
1
2
3
4
5
<bean name="springAction" class="test.spring1.SpringAction1">  
<!-- 依赖注入,配置当前类中相应的属性 -->
<property name="springDao" ref="springDao"></property>
</bean>
<bean name="springDao" class="test.spring1.SpringDao"></bean>
1
2
SpringAction1 springAction1 = (SpringAction1) ContextUtil.getBean("springAction", SpringAction1.class);
springAction1.ok();

清理Spring内省对象

问题

在服务器运行过程中,Spring不停的运行计划任务和OpenSessionInViewFilter,使得Tomcat反复加载对象而产生框架并用时可能产生的内存泄漏,则使用IntrospectorCleanupListener作为相应的解决办法。

解决方式

Spring中的提供一个名为org.springframework.web.util.IntrospectorCleanupListener的监听器。它主要负责处理由JavaBeans Introspector(内省对象)的使用而引起的内存泄露。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×