1. Introduction to Mybatis Interceptor
1.1 Mybatis execution process
- First read the configuration file, then load the mapping file, and the SqlSessionFactory factory object creates the core object SqlSession. The SqlSession object will execute SQL through the Executor executor object. Then the Executor executor object will call the StatementHandler object to actually access the database and execute SQL statements.
- Before executing the sql statement, the MapperStatement will first encapsulate the mapping information, and then the StatementHandler calls ParameterHandler to set the compilation parameters [#{}, ${}], and the compilation is carried out in the StatementHandler. Then StatementHandler calls the JBDC native API for processing and obtains the execution result. This execution result is handed over to the ResultSetHandler for result set encapsulation, and then returns the result to the StatementHandler.
- Note: Here, MapperStatement is an encapsulation of mapping information, which is used to store the id, parameters and other information of the SQL statement to be mapped. TypeHandler performs database type and JavaBean type mapping processing.
1.2 Types that can be intercepted in Mybatis
- Executor: Method to intercept executor.
- ParameterHandler: Handling of intercepting parameters.
- ResultHandler: Processing of intercepting result sets.
- StatementHandler: Intercepting the processing of Sql syntax construction.
1.3 Usage rules
@Documented @Retention() @Target() public @interface Intercepts { /** * Define the intercept point * Only when the interceptor is met will the interceptor be entered. */ Signature[] value(); }
Signature to specify which method we need to intercept which class object
- type: one of the four types mentioned above;
- method: Which type of method in the corresponding interface (because there may be an overloaded method);
- args: the parameter corresponding to which method is entered;
@Documented @Retention() @Target({}) public @interface Signature { /** * Define an intercepted class Executor, ParameterHandler, StatementHandler, and ResultSetHandler */ Class<?> type(); /** * On the basis of defining the intercepting class, define the intercepting method */ String method(); /** * On the basis of defining the intercept method, define the parameters corresponding to the intercept method. * Methods in JAVA may be overloaded, so pay attention to the type and order of parameters */ Class<?>[] args(); }
1.4 Methods of interceptor rewriting
public interface Interceptor { //It plays an intercepting role, define some functions here Object intercept(Invocation var1) throws Throwable; //The function of this method is to let mybatis determine whether to intercept it, and then make a decision whether to generate a proxy Object plugin(Object var1); //The interceptor requires some variable objects, and this object supports configurable. void setProperties(Properties var1); }
2. Practical part: Intercepting and realizing
Custom annotations
@Target() @Retention() @Documented public @interface DepartAuth { /** * Add the field name of the query condition * @return */ String field(); EnumDepartAuthType authType() default EnumDepartAuthType.DEPART_ID; }
Add annotations on the interface
@DepartAuth(field = "xxx", authType = )
AuthAspect
@Slf4j @Aspect @Component public class DepartAuthAspect { @Autowired private DepartAuthHandler departAuthHandler; @Pointcut("@annotation()") public void departAuthPoint() { } @Before("departAuthPoint()") public void before(JoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) (); Annotation[] annotations = ().getAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof DepartAuth) { String field = ((DepartAuth) annotation).field(); ((DepartAuth) annotation); } } } @After("departAuthPoint()") public void after(JoinPoint pjp) { (); } }
DepartAuthHandler
Store information in TreadLocalhost so that you can read the fields that need to be modified when modifying SQL in the future.
@Slf4j @Component public class DepartAuthHandler { public static ThreadLocal<DepartAuth> DEPART_AUTH_CACHE = new ThreadLocal<>(); public static ThreadLocal<Integer> DEPART_AUTH_COUNT = new ThreadLocal<>(); public void beforeHandler(DepartAuth departAuth) { String field = (); if((field)) { DEPART_AUTH_CACHE.set(departAuth); DEPART_AUTH_COUNT.remove(); } PriorityQueue queue = new PriorityQueue<>(); (); } public void afterHandler() { DEPART_AUTH_CACHE.remove(); DEPART_AUTH_COUNT.remove(); } }
Interceptor part
- Get StatementHandler: Get the currently intercepted StatementHandler object through (). Since MyBatis uses proxy mode, what you get here is a proxy object. Next, its actual proxy object is obtained by reflection, that is, the StatementHandler instance that finally executes SQL.
- Get MappedStatement: Get the mappedStatement object from the StatementHandler through reflection. This object contains all information about the SQL statement to be executed, including SQL type, parameter type, etc.
- Determine SQL type: Get SQL command type through (). If the type is SELECT, it means that it is currently a query operation and permission checking and processing are required.
- Get and parse the original SQL: Get the BoundSql object through (), which contains the actual executed SQL statement and related parameter information. Then use() to parse the SQL statement to obtain an abstract syntax tree (AST).
- Modify SQL: Extract the PlainSelect object (i.e. the body of the SELECT statement) from the AST. Then call the custom buildWhereClause method, build a permission check condition based on the permission information in the departAuth, and inject it into the original SELECT statement. This is usually achieved by appending additional conditions after the WHERE clause.
- Update BoundSql object: Reset the modified SQL statement back into the BoundSql object so that MyBatis can use the modified SQL when executing.
- Continue to follow-up process: After completing the SQL modification, call () to continue to execute the subsequent processing process of MyBatis, including actual SQL execution, result set processing, etc.
@Data @Slf4j @Component @Intercepts({ @Signature(type = , method = "prepare", args = {, }) }) public class DepartAuthMapperInterceptor implements Interceptor { private Properties properties; @Override public Object intercept(Invocation invocation) throws Throwable { DepartAuth departAuth = DepartAuthHandler.DEPART_AUTH_CACHE.get(); Integer count = DepartAuthHandler.DEPART_AUTH_COUNT.get(); if(departAuth != null && count == null) { // Indicates that the current thread has executed the filtering conditions to avoid recursive calls DepartAuthHandler.DEPART_AUTH_COUNT.set(1); RoutingStatementHandler handler = (RoutingStatementHandler) (); //Get StatementHandler constructor StatementHandler delegate = (StatementHandler) (handler, "delegate"); // Get the mappedStatement property of the delegate parent class BaseStatementHandler through reflection MappedStatement mappedStatement = (MappedStatement) (delegate, "mappedStatement"); SqlCommandType commandType = (); // Process select objects if ((commandType)) { // Get the original sql BoundSql boundSql = (); Statement statement = (()); PlainSelect selectBody = (PlainSelect) ((Select) statement).getSelectBody(); ("original sql:{}", ()); // New conditions for splicing buildWhereClause(selectBody, getSql(departAuth)); (boundSql, "sql", ()); } return (); } return (); } @Override public Object plugin(Object target) { return (target, this); } @Override public void setProperties(Properties properties) { = properties; } /** * Add query criteria * @param select * @param dataFilter * @throws JSQLParserException */ private void buildWhereClause(PlainSelect select, String dataFilter) throws JSQLParserException { if((dataFilter)) { return; } if (() == null) { ((dataFilter)); } else { AndExpression and = new AndExpression( (dataFilter), ()); (and); } } private String getSql(DepartAuth departAuth) { //Combining your own business, splicing the corresponding SQL statements } }
This is the end of this article about the implementation of the permission function of Mybatis custom interceptor. For more related Mybatis permission content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!